better.doc
[tfs.git] / tools / tfsnd / SmartIrc4net / IrcConnection / IrcConnection.cs
blob94b8f0373aba2aeca0283199465442fc248a8e53
1 /*
2 * $Id: IrcConnection.cs 211 2007-02-01 22:08:32Z meebey $
3 * $URL: svn://svn.qnetp.net/smartirc/SmartIrc4net/tags/0.4.0/src/IrcConnection/IrcConnection.cs $
4 * $Rev: 211 $
5 * $Author: meebey $
6 * $Date: 2007-02-01 23:08:32 +0100 (Thu, 01 Feb 2007) $
8 * SmartIrc4net - the IRC library for .NET/C# <http://smartirc4net.sf.net>
10 * Copyright (c) 2003-2005 Mirco Bauer <meebey@meebey.net> <http://www.meebey.net>
12 * Full LGPL License: <http://www.gnu.org/licenses/lgpl.txt>
14 * This library is free software; you can redistribute it and/or
15 * modify it under the terms of the GNU Lesser General Public
16 * License as published by the Free Software Foundation; either
17 * version 2.1 of the License, or (at your option) any later version.
19 * This library is distributed in the hope that it will be useful,
20 * but WITHOUT ANY WARRANTY; without even the implied warranty of
21 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
22 * Lesser General Public License for more details.
24 * You should have received a copy of the GNU Lesser General Public
25 * License along with this library; if not, write to the Free Software
26 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
29 using System;
30 using System.IO;
31 using System.Text;
32 using System.Collections;
33 using System.Threading;
34 using System.Reflection;
35 using System.Net.Sockets;
37 namespace Meebey.SmartIrc4net
39 /// <summary>
40 ///
41 /// </summary>
42 /// <threadsafety static="true" instance="true" />
43 public class IrcConnection
45 private string _VersionNumber;
46 private string _VersionString;
47 private string[] _AddressList = {"localhost"};
48 private int _CurrentAddress;
49 private int _Port = 6667;
50 private StreamReader _Reader;
51 private StreamWriter _Writer;
52 private ReadThread _ReadThread;
53 private WriteThread _WriteThread;
54 private IdleWorkerThread _IdleWorkerThread;
55 private IrcTcpClient _TcpClient;
56 private Hashtable _SendBuffer = Hashtable.Synchronized(new Hashtable());
57 private int _SendDelay = 200;
58 private bool _IsRegistered;
59 private bool _IsConnected;
60 private bool _IsConnectionError;
61 private int _ConnectTries;
62 private bool _AutoRetry;
63 private int _AutoRetryDelay = 30;
64 private bool _AutoReconnect;
65 private Encoding _Encoding = Encoding.Default;
66 private int _SocketReceiveTimeout = 600;
67 private int _SocketSendTimeout = 600;
68 private int _IdleWorkerInterval = 60;
69 private int _PingInterval = 60;
70 private int _PingTimeout = 300;
71 private DateTime _LastPingSent;
72 private DateTime _LastPongReceived;
73 private TimeSpan _Lag;
75 /// <event cref="OnReadLine">
76 /// Raised when a \r\n terminated line is read from the socket
77 /// </event>
78 public event ReadLineEventHandler OnReadLine;
79 /// <event cref="OnWriteLine">
80 /// Raised when a \r\n terminated line is written to the socket
81 /// </event>
82 public event WriteLineEventHandler OnWriteLine;
83 /// <event cref="OnConnect">
84 /// Raised before the connect attempt
85 /// </event>
86 public event EventHandler OnConnecting;
87 /// <event cref="OnConnect">
88 /// Raised on successful connect
89 /// </event>
90 public event EventHandler OnConnected;
91 /// <event cref="OnConnect">
92 /// Raised before the connection is closed
93 /// </event>
94 public event EventHandler OnDisconnecting;
95 /// <event cref="OnConnect">
96 /// Raised when the connection is closed
97 /// </event>
98 public event EventHandler OnDisconnected;
99 /// <event cref="OnConnectionError">
100 /// Raised when the connection got into an error state
101 /// </event>
102 public event EventHandler OnConnectionError;
103 /// <event cref="AutoConnectErrorEventHandler">
104 /// Raised when the connection got into an error state during auto connect loop
105 /// </event>
106 public event AutoConnectErrorEventHandler OnAutoConnectError;
108 /// <summary>
109 /// When a connection error is detected this property will return true
110 /// </summary>
111 protected bool IsConnectionError {
112 get {
113 lock (this) {
114 return _IsConnectionError;
117 set {
118 lock (this) {
119 _IsConnectionError = value;
124 /// <summary>
125 /// Gets the current address of the connection
126 /// </summary>
127 public string Address {
128 get {
129 return _AddressList[_CurrentAddress];
133 /// <summary>
134 /// Gets the address list of the connection
135 /// </summary>
136 public string[] AddressList {
137 get {
138 return _AddressList;
142 /// <summary>
143 /// Gets the used port of the connection
144 /// </summary>
145 public int Port {
146 get {
147 return _Port;
151 /// <summary>
152 /// By default nothing is done when the library looses the connection
153 /// to the server.
154 /// Default: false
155 /// </summary>
156 /// <value>
157 /// true, if the library should reconnect on lost connections
158 /// false, if the library should not take care of it
159 /// </value>
160 public bool AutoReconnect {
161 get {
162 return _AutoReconnect;
164 set {
165 #if LOG4NET
166 if (value) {
167 Logger.Connection.Info("AutoReconnect enabled");
168 } else {
169 Logger.Connection.Info("AutoReconnect disabled");
171 #endif
172 _AutoReconnect = value;
176 /// <summary>
177 /// If the library should retry to connect when the connection fails.
178 /// Default: false
179 /// </summary>
180 /// <value>
181 /// true, if the library should retry to connect
182 /// false, if the library should not retry
183 /// </value>
184 public bool AutoRetry {
185 get {
186 return _AutoRetry;
188 set {
189 #if LOG4NET
190 if (value) {
191 Logger.Connection.Info("AutoRetry enabled");
192 } else {
193 Logger.Connection.Info("AutoRetry disabled");
195 #endif
196 _AutoRetry = value;
200 /// <summary>
201 /// Delay between retry attempts in Connect() in seconds.
202 /// Default: 30
203 /// </summary>
204 public int AutoRetryDelay {
205 get {
206 return _AutoRetryDelay;
208 set {
209 _AutoRetryDelay = value;
213 /// <summary>
214 /// To prevent flooding the IRC server, it's required to delay each
215 /// message, given in milliseconds.
216 /// Default: 200
217 /// </summary>
218 public int SendDelay {
219 get {
220 return _SendDelay;
222 set {
223 _SendDelay = value;
227 /// <summary>
228 /// On successful registration on the IRC network, this is set to true.
229 /// </summary>
230 public bool IsRegistered {
231 get {
232 return _IsRegistered;
236 /// <summary>
237 /// On successful connect to the IRC server, this is set to true.
238 /// </summary>
239 public bool IsConnected {
240 get {
241 return _IsConnected;
245 /// <summary>
246 /// Gets the SmartIrc4net version number
247 /// </summary>
248 public string VersionNumber {
249 get {
250 return _VersionNumber;
254 /// <summary>
255 /// Gets the full SmartIrc4net version string
256 /// </summary>
257 public string VersionString {
258 get {
259 return _VersionString;
263 /// <summary>
264 /// Encoding which is used for reading and writing to the socket
265 /// Default: encoding of the system
266 /// </summary>
267 public Encoding Encoding {
268 get {
269 return _Encoding;
271 set {
272 _Encoding = value;
276 /// <summary>
277 /// Timeout in seconds for receiving data from the socket
278 /// Default: 600
279 /// </summary>
280 public int SocketReceiveTimeout {
281 get {
282 return _SocketReceiveTimeout;
284 set {
285 _SocketReceiveTimeout = value;
289 /// <summary>
290 /// Timeout in seconds for sending data to the socket
291 /// Default: 600
292 /// </summary>
293 public int SocketSendTimeout {
294 get {
295 return _SocketSendTimeout;
297 set {
298 _SocketSendTimeout = value;
302 /// <summary>
303 /// Interval in seconds to run the idle worker
304 /// Default: 60
305 /// </summary>
306 public int IdleWorkerInterval {
307 get {
308 return _IdleWorkerInterval;
310 set {
311 _IdleWorkerInterval = value;
315 /// <summary>
316 /// Interval in seconds to send a PING
317 /// Default: 60
318 /// </summary>
319 public int PingInterval {
320 get {
321 return _PingInterval;
323 set {
324 _PingInterval = value;
328 /// <summary>
329 /// Timeout in seconds for server response to a PING
330 /// Default: 600
331 /// </summary>
332 public int PingTimeout {
333 get {
334 return _PingTimeout;
336 set {
337 _PingTimeout = value;
341 /// <summary>
342 /// Latency between client and the server
343 /// </summary>
344 public TimeSpan Lag {
345 get {
346 return _Lag;
350 /// <summary>
351 /// Initializes the message queues, read and write thread
352 /// </summary>
353 public IrcConnection()
355 #if LOG4NET
356 Logger.Init();
357 Logger.Main.Debug("IrcConnection created");
358 #endif
359 _SendBuffer[Priority.High] = Queue.Synchronized(new Queue());
360 _SendBuffer[Priority.AboveMedium] = Queue.Synchronized(new Queue());
361 _SendBuffer[Priority.Medium] = Queue.Synchronized(new Queue());
362 _SendBuffer[Priority.BelowMedium] = Queue.Synchronized(new Queue());
363 _SendBuffer[Priority.Low] = Queue.Synchronized(new Queue());
365 // setup own callbacks
366 OnReadLine += new ReadLineEventHandler(_SimpleParser);
367 OnConnectionError += new EventHandler(_OnConnectionError);
369 _ReadThread = new ReadThread(this);
370 _WriteThread = new WriteThread(this);
371 _IdleWorkerThread = new IdleWorkerThread(this);
373 Assembly assm = Assembly.GetAssembly(this.GetType());
374 AssemblyName assm_name = assm.GetName(false);
376 AssemblyProductAttribute pr = (AssemblyProductAttribute)assm.GetCustomAttributes(typeof(AssemblyProductAttribute), false)[0];
378 _VersionNumber = assm_name.Version.ToString();
379 _VersionString = pr.Product+" "+_VersionNumber;
382 #if LOG4NET
383 ~IrcConnection()
385 Logger.Main.Debug("IrcConnection destroyed");
387 #endif
389 /// <overloads>this method has 2 overloads</overloads>
390 /// <summary>
391 /// Connects to the specified server and port, when the connection fails
392 /// the next server in the list will be used.
393 /// </summary>
394 /// <param name="addresslist">List of servers to connect to</param>
395 /// <param name="port">Portnumber to connect to</param>
396 /// <exception cref="CouldNotConnectException">The connection failed</exception>
397 /// <exception cref="AlreadyConnectedException">If there is already an active connection</exception>
398 public void Connect(string[] addresslist, int port)
400 if (_IsConnected) {
401 throw new AlreadyConnectedException("Already connected to: "+Address+":"+Port);
404 #if LOG4NET
405 Logger.Connection.Info("connecting...");
406 #endif
407 _ConnectTries++;
408 _AddressList = (string[])addresslist.Clone();
409 _Port = port;
411 if (OnConnecting != null) {
412 OnConnecting(this, EventArgs.Empty);
414 try {
415 System.Net.IPAddress ip = System.Net.Dns.Resolve(Address).AddressList[0];
416 _TcpClient = new IrcTcpClient();
417 _TcpClient.NoDelay = true;
418 _TcpClient.Socket.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.KeepAlive, 1);
419 // set timeout, after this the connection will be aborted
420 _TcpClient.ReceiveTimeout = _SocketReceiveTimeout*1000;
421 _TcpClient.SendTimeout = _SocketSendTimeout*1000;
422 _TcpClient.Connect(ip, port);
424 _Reader = new StreamReader(_TcpClient.GetStream(), _Encoding);
425 _Writer = new StreamWriter(_TcpClient.GetStream(), _Encoding);
427 if (_Encoding.GetPreamble().Length > 0) {
428 // HACK: we have an encoding that has some kind of preamble
429 // like UTF-8 has a BOM, this will confuse the IRCd!
430 // Thus we send a \r\n so the IRCd can safely ignore that
431 // garbage.
432 _Writer.WriteLine();
435 // Connection was succeful, reseting the connect counter
436 _ConnectTries = 0;
438 // updating the connection error state, so connecting is possible again
439 IsConnectionError = false;
440 _IsConnected = true;
442 // lets power up our threads
443 _ReadThread.Start();
444 _WriteThread.Start();
445 _IdleWorkerThread.Start();
447 #if LOG4NET
448 Logger.Connection.Info("connected");
449 #endif
450 if (OnConnected != null) {
451 OnConnected(this, EventArgs.Empty);
453 } catch (Exception e) {
454 if (_Reader != null) {
455 try {
456 _Reader.Close();
457 } catch (ObjectDisposedException) {
460 if (_Writer != null) {
461 try {
462 _Writer.Close();
463 } catch (ObjectDisposedException) {
466 if (_TcpClient != null) {
467 _TcpClient.Close();
469 _IsConnected = false;
470 IsConnectionError = true;
472 #if LOG4NET
473 Logger.Connection.Info("connection failed: "+e.Message);
474 #endif
475 if (_AutoRetry &&
476 _ConnectTries <= 3) {
477 if (OnAutoConnectError != null) {
478 OnAutoConnectError(this, new AutoConnectErrorEventArgs(Address, Port, e));
480 #if LOG4NET
481 Logger.Connection.Debug("delaying new connect attempt for "+_AutoRetryDelay+" sec");
482 #endif
483 Thread.Sleep(_AutoRetryDelay * 1000);
484 _NextAddress();
485 Connect(_AddressList, _Port);
486 } else {
487 throw new CouldNotConnectException("Could not connect to: "+Address+":"+Port+" "+e.Message, e);
492 /// <summary>
493 /// Connects to the specified server and port.
494 /// </summary>
495 /// <param name="address">Server address to connect to</param>
496 /// <param name="port">Port number to connect to</param>
497 public void Connect(string address, int port)
499 Connect(new string[] {address}, port);
502 /// <summary>
503 /// Reconnects to the server
504 /// </summary>
505 /// <exception cref="NotConnectedException">
506 /// If there was no active connection
507 /// </exception>
508 /// <exception cref="CouldNotConnectException">
509 /// The connection failed
510 /// </exception>
511 /// <exception cref="AlreadyConnectedException">
512 /// If there is already an active connection
513 /// </exception>
514 public void Reconnect()
516 #if LOG4NET
517 Logger.Connection.Info("reconnecting...");
518 #endif
519 Disconnect();
520 Connect(_AddressList, _Port);
523 /// <summary>
524 /// Disconnects from the server
525 /// </summary>
526 /// <exception cref="NotConnectedException">
527 /// If there was no active connection
528 /// </exception>
529 public void Disconnect()
531 if (!IsConnected) {
532 throw new NotConnectedException("The connection could not be disconnected because there is no active connection");
535 #if LOG4NET
536 Logger.Connection.Info("disconnecting...");
537 #endif
538 if (OnDisconnecting != null) {
539 OnDisconnecting(this, EventArgs.Empty);
542 _ReadThread.Stop();
543 _WriteThread.Stop();
544 _TcpClient.Close();
545 _IsConnected = false;
546 _IsRegistered = false;
548 if (OnDisconnected != null) {
549 OnDisconnected(this, EventArgs.Empty);
552 #if LOG4NET
553 Logger.Connection.Info("disconnected");
554 #endif
557 /// <summary>
558 ///
559 /// </summary>
560 /// <param name="blocking"></param>
561 public void Listen(bool blocking)
563 if (blocking) {
564 while (IsConnected) {
565 ReadLine(true);
567 } else {
568 while (ReadLine(false).Length > 0) {
569 // loop as long as we receive messages
574 /// <summary>
575 ///
576 /// </summary>
577 public void Listen()
579 Listen(true);
582 /// <summary>
583 ///
584 /// </summary>
585 /// <param name="blocking"></param>
586 public void ListenOnce(bool blocking)
588 ReadLine(blocking);
591 /// <summary>
592 ///
593 /// </summary>
594 public void ListenOnce()
596 ListenOnce(true);
599 /// <summary>
600 ///
601 /// </summary>
602 /// <param name="blocking"></param>
603 /// <returns></returns>
604 public string ReadLine(bool blocking)
606 string data = "";
607 if (blocking) {
608 // block till the queue has data, but bail out on connection error
609 while (IsConnected &&
610 !IsConnectionError &&
611 _ReadThread.Queue.Count == 0) {
612 Thread.Sleep(10);
616 if (IsConnected &&
617 _ReadThread.Queue.Count > 0) {
618 data = (string)(_ReadThread.Queue.Dequeue());
621 if (data != null && data.Length > 0) {
622 #if LOG4NET
623 Logger.Queue.Debug("read: \""+data+"\"");
624 #endif
625 if (OnReadLine != null) {
626 OnReadLine(this, new ReadLineEventArgs(data));
630 if (IsConnectionError &&
631 OnConnectionError != null) {
632 OnConnectionError(this, EventArgs.Empty);
635 return data;
638 /// <summary>
639 ///
640 /// </summary>
641 /// <param name="data"></param>
642 /// <param name="priority"></param>
643 public void WriteLine(string data, Priority priority)
645 if (priority == Priority.Critical) {
646 if (!IsConnected) {
647 throw new NotConnectedException();
650 _WriteLine(data);
651 } else {
652 ((Queue)_SendBuffer[priority]).Enqueue(data);
656 /// <summary>
657 ///
658 /// </summary>
659 /// <param name="data"></param>
660 public void WriteLine(string data)
662 WriteLine(data, Priority.Medium);
665 private bool _WriteLine(string data)
667 if (IsConnected) {
668 try {
669 _Writer.Write(data+"\r\n");
670 _Writer.Flush();
671 } catch (IOException) {
672 #if LOG4NET
673 Logger.Socket.Warn("sending data failed, connection lost");
674 #endif
675 IsConnectionError = true;
676 return false;
677 } catch (ObjectDisposedException) {
678 #if LOG4NET
679 Logger.Socket.Warn("sending data failed (stream error), connection lost");
680 #endif
681 IsConnectionError = true;
682 return false;
685 #if LOG4NET
686 Logger.Socket.Debug("sent: \""+data+"\"");
687 #endif
688 if (OnWriteLine != null) {
689 OnWriteLine(this, new WriteLineEventArgs(data));
691 return true;
694 return false;
697 private void _NextAddress()
699 _CurrentAddress++;
700 if (_CurrentAddress >= _AddressList.Length) {
701 _CurrentAddress = 0;
703 #if LOG4NET
704 Logger.Connection.Info("set server to: "+Address);
705 #endif
708 private void _SimpleParser(object sender, ReadLineEventArgs args)
710 string rawline = args.Line;
711 string[] rawlineex = rawline.Split(new char[] {' '});
712 string messagecode = "";
714 if (rawline[0] == ':') {
715 messagecode = rawlineex[1];
717 ReplyCode replycode = ReplyCode.Null;
718 try {
719 replycode = (ReplyCode)int.Parse(messagecode);
720 } catch (FormatException) {
723 if (replycode != ReplyCode.Null) {
724 switch (replycode) {
725 case ReplyCode.Welcome:
726 _IsRegistered = true;
727 #if LOG4NET
728 Logger.Connection.Info("logged in");
729 #endif
730 break;
732 } else {
733 switch (rawlineex[1]) {
734 case "PONG":
735 DateTime now = DateTime.Now;
736 _LastPongReceived = now;
737 _Lag = now - _LastPingSent;
739 #if LOG4NET
740 Logger.Connection.Debug("PONG received, took: "+_Lag.TotalMilliseconds+" ms");
741 #endif
742 break;
745 } else {
746 messagecode = rawlineex[0];
747 switch (messagecode) {
748 case "ERROR":
749 IsConnectionError = true;
750 break;
755 private void _OnConnectionError(object sender, EventArgs e)
757 try {
758 if (AutoReconnect) {
759 // lets try to recover the connection
760 Reconnect();
761 } else {
762 // make sure we clean up
763 Disconnect();
765 } catch (ConnectionException) {
769 /// <summary>
770 ///
771 /// </summary>
772 private class ReadThread
774 private IrcConnection _Connection;
775 private Thread _Thread;
776 private Queue _Queue = Queue.Synchronized(new Queue());
778 public Queue Queue {
779 get {
780 return _Queue;
784 /// <summary>
785 ///
786 /// </summary>
787 /// <param name="connection"></param>
788 public ReadThread(IrcConnection connection)
790 _Connection = connection;
793 /// <summary>
794 ///
795 /// </summary>
796 public void Start()
798 _Thread = new Thread(new ThreadStart(_Worker));
799 _Thread.Name = "ReadThread ("+_Connection.Address+":"+_Connection.Port+")";
800 _Thread.IsBackground = true;
801 _Thread.Start();
804 /// <summary>
805 ///
806 /// </summary>
807 public void Stop()
809 _Thread.Abort();
810 try {
811 _Connection._Reader.Close();
812 } catch (ObjectDisposedException) {
816 private void _Worker()
818 #if LOG4NET
819 Logger.Socket.Debug("ReadThread started");
820 #endif
821 try {
822 string data = "";
823 try {
824 while (_Connection.IsConnected &&
825 ((data = _Connection._Reader.ReadLine()) != null)) {
826 _Queue.Enqueue(data);
827 #if LOG4NET
828 Logger.Socket.Debug("received: \""+data+"\"");
829 #endif
831 } catch (IOException e) {
832 #if LOG4NET
833 Logger.Socket.Warn("IOException: "+e.Message);
834 #endif
835 } finally {
836 #if LOG4NET
837 Logger.Socket.Warn("connection lost");
838 #endif
839 _Connection.IsConnectionError = true;
841 } catch (ThreadAbortException) {
842 Thread.ResetAbort();
843 #if LOG4NET
844 Logger.Socket.Debug("ReadThread aborted");
845 #endif
850 /// <summary>
851 ///
852 /// </summary>
853 private class WriteThread
855 private IrcConnection _Connection;
856 private Thread _Thread;
857 private int _HighCount;
858 private int _AboveMediumCount;
859 private int _MediumCount;
860 private int _BelowMediumCount;
861 private int _LowCount;
862 private int _AboveMediumSentCount;
863 private int _MediumSentCount;
864 private int _BelowMediumSentCount;
865 private int _AboveMediumThresholdCount = 4;
866 private int _MediumThresholdCount = 2;
867 private int _BelowMediumThresholdCount = 1;
868 private int _BurstCount;
870 /// <summary>
871 ///
872 /// </summary>
873 /// <param name="connection"></param>
874 public WriteThread(IrcConnection connection)
876 _Connection = connection;
879 /// <summary>
880 ///
881 /// </summary>
882 public void Start()
884 _Thread = new Thread(new ThreadStart(_Worker));
885 _Thread.Name = "WriteThread ("+_Connection.Address+":"+_Connection.Port+")";
886 _Thread.IsBackground = true;
887 _Thread.Start();
890 /// <summary>
891 ///
892 /// </summary>
893 public void Stop()
895 _Thread.Abort();
896 try {
897 _Connection._Writer.Close();
898 } catch (ObjectDisposedException) {
902 private void _Worker()
904 #if LOG4NET
905 Logger.Socket.Debug("WriteThread started");
906 #endif
907 try {
908 try {
909 while (_Connection.IsConnected) {
910 _CheckBuffer();
911 Thread.Sleep(_Connection._SendDelay);
913 } catch (IOException e) {
914 #if LOG4NET
915 Logger.Socket.Warn("IOException: "+e.Message);
916 #endif
917 } finally {
918 #if LOG4NET
919 Logger.Socket.Warn("connection lost");
920 #endif
921 _Connection.IsConnectionError = true;
923 } catch (ThreadAbortException) {
924 Thread.ResetAbort();
925 #if LOG4NET
926 Logger.Socket.Debug("WriteThread aborted");
927 #endif
931 #region WARNING: complex scheduler, don't even think about changing it!
932 // WARNING: complex scheduler, don't even think about changing it!
933 private void _CheckBuffer()
935 // only send data if we are succefully registered on the IRC network
936 if (!_Connection._IsRegistered) {
937 return;
940 _HighCount = ((Queue)_Connection._SendBuffer[Priority.High]).Count;
941 _AboveMediumCount = ((Queue)_Connection._SendBuffer[Priority.AboveMedium]).Count;
942 _MediumCount = ((Queue)_Connection._SendBuffer[Priority.Medium]).Count;
943 _BelowMediumCount = ((Queue)_Connection._SendBuffer[Priority.BelowMedium]).Count;
944 _LowCount = ((Queue)_Connection._SendBuffer[Priority.Low]).Count;
946 if (_CheckHighBuffer() &&
947 _CheckAboveMediumBuffer() &&
948 _CheckMediumBuffer() &&
949 _CheckBelowMediumBuffer() &&
950 _CheckLowBuffer()) {
951 // everything is sent, resetting all counters
952 _AboveMediumSentCount = 0;
953 _MediumSentCount = 0;
954 _BelowMediumSentCount = 0;
955 _BurstCount = 0;
958 if (_BurstCount < 3) {
959 _BurstCount++;
960 //_CheckBuffer();
964 private bool _CheckHighBuffer()
966 if (_HighCount > 0) {
967 string data = (string)((Queue)_Connection._SendBuffer[Priority.High]).Dequeue();
968 if (_Connection._WriteLine(data) == false) {
969 #if LOG4NET
970 Logger.Queue.Warn("Sending data was not sucessful, data is requeued!");
971 #endif
972 ((Queue)_Connection._SendBuffer[Priority.High]).Enqueue(data);
975 if (_HighCount > 1) {
976 // there is more data to send
977 return false;
981 return true;
984 private bool _CheckAboveMediumBuffer()
986 if ((_AboveMediumCount > 0) &&
987 (_AboveMediumSentCount < _AboveMediumThresholdCount)) {
988 string data = (string)((Queue)_Connection._SendBuffer[Priority.AboveMedium]).Dequeue();
989 if (_Connection._WriteLine(data) == false) {
990 #if LOG4NET
991 Logger.Queue.Warn("Sending data was not sucessful, data is requeued!");
992 #endif
993 ((Queue)_Connection._SendBuffer[Priority.AboveMedium]).Enqueue(data);
995 _AboveMediumSentCount++;
997 if (_AboveMediumSentCount < _AboveMediumThresholdCount) {
998 return false;
1002 return true;
1005 private bool _CheckMediumBuffer()
1007 if ((_MediumCount > 0) &&
1008 (_MediumSentCount < _MediumThresholdCount)) {
1009 string data = (string)((Queue)_Connection._SendBuffer[Priority.Medium]).Dequeue();
1010 if (_Connection._WriteLine(data) == false) {
1011 #if LOG4NET
1012 Logger.Queue.Warn("Sending data was not sucessful, data is requeued!");
1013 #endif
1014 ((Queue)_Connection._SendBuffer[Priority.Medium]).Enqueue(data);
1016 _MediumSentCount++;
1018 if (_MediumSentCount < _MediumThresholdCount) {
1019 return false;
1023 return true;
1026 private bool _CheckBelowMediumBuffer()
1028 if ((_BelowMediumCount > 0) &&
1029 (_BelowMediumSentCount < _BelowMediumThresholdCount)) {
1030 string data = (string)((Queue)_Connection._SendBuffer[Priority.BelowMedium]).Dequeue();
1031 if (_Connection._WriteLine(data) == false) {
1032 #if LOG4NET
1033 Logger.Queue.Warn("Sending data was not sucessful, data is requeued!");
1034 #endif
1035 ((Queue)_Connection._SendBuffer[Priority.BelowMedium]).Enqueue(data);
1037 _BelowMediumSentCount++;
1039 if (_BelowMediumSentCount < _BelowMediumThresholdCount) {
1040 return false;
1044 return true;
1047 private bool _CheckLowBuffer()
1049 if (_LowCount > 0) {
1050 if ((_HighCount > 0) ||
1051 (_AboveMediumCount > 0) ||
1052 (_MediumCount > 0) ||
1053 (_BelowMediumCount > 0)) {
1054 return true;
1057 string data = (string)((Queue)_Connection._SendBuffer[Priority.Low]).Dequeue();
1058 if (_Connection._WriteLine(data) == false) {
1059 #if LOG4NET
1060 Logger.Queue.Warn("Sending data was not sucessful, data is requeued!");
1061 #endif
1062 ((Queue)_Connection._SendBuffer[Priority.Low]).Enqueue(data);
1065 if (_LowCount > 1) {
1066 return false;
1070 return true;
1072 // END OF WARNING, below this you can read/change again ;)
1073 #endregion
1076 /// <summary>
1077 ///
1078 /// </summary>
1079 private class IdleWorkerThread
1081 private IrcConnection _Connection;
1082 private Thread _Thread;
1084 /// <summary>
1085 ///
1086 /// </summary>
1087 /// <param name="connection"></param>
1088 public IdleWorkerThread(IrcConnection connection)
1090 _Connection = connection;
1093 /// <summary>
1094 ///
1095 /// </summary>
1096 public void Start()
1098 DateTime now = DateTime.Now;
1099 _Connection._LastPingSent = now;
1100 _Connection._LastPongReceived = now;
1102 _Thread = new Thread(new ThreadStart(_Worker));
1103 _Thread.Name = "IdleWorkerThread ("+_Connection.Address+":"+_Connection.Port+")";
1104 _Thread.IsBackground = true;
1105 _Thread.Start();
1108 /// <summary>
1109 ///
1110 /// </summary>
1111 public void Stop()
1113 _Thread.Abort();
1116 private void _Worker()
1118 #if LOG4NET
1119 Logger.Socket.Debug("IdleWorkerThread started");
1120 #endif
1121 try {
1122 while (_Connection.IsConnected ) {
1123 if (_Connection.IsRegistered) {
1124 DateTime now = DateTime.Now;
1125 int last_ping_sent = (int)(now - _Connection._LastPingSent).TotalSeconds;
1126 int last_pong_rcvd = (int)(now - _Connection._LastPongReceived).TotalSeconds;
1127 // determins if the resoponse time is ok
1128 if (last_ping_sent < _Connection._PingTimeout) {
1129 // determines if it need to send another ping yet
1130 if (last_pong_rcvd > _Connection._PingInterval) {
1131 _Connection.WriteLine(Rfc2812.Ping(_Connection.Address), Priority.Critical);
1132 _Connection._LastPingSent = now;
1133 _Connection._LastPongReceived = now;
1134 } // else connection is fine, just continue
1135 } else {
1136 #if LOG4NET
1137 Logger.Socket.Warn("ping timeout, connection lost");
1138 #endif
1139 _Connection.IsConnectionError = true;
1140 break;
1143 Thread.Sleep(_Connection._IdleWorkerInterval);
1145 } catch (ThreadAbortException) {
1146 Thread.ResetAbort();
1147 #if LOG4NET
1148 Logger.Socket.Debug("IdleWorkerThread aborted");
1149 #endif