Advertisement
Guest User

Untitled

a guest
Apr 20th, 2017
87
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 11.37 KB | None | 0 0
  1. using System;
  2. using System.Collections.Concurrent;
  3. using System.Collections.Generic;
  4. using System.Data;
  5. using System.Diagnostics;
  6. using System.IO;
  7. using System.Linq;
  8. using System.Net.Security;
  9. using System.Net.Sockets;
  10. using System.Text;
  11. using System.Threading;
  12. using LunaBotConnection.EventArgs;
  13. using LunaBotGlobals;
  14.  
  15. namespace LunaBotConnection
  16. {
  17. public class Connection
  18. {
  19. private static readonly string[] MessageSeparators = {"\r\n"};
  20.  
  21. private readonly List<string> _channelList = new List<string>();
  22. private readonly string _user;
  23. private readonly string _password;
  24. private readonly string _nick;
  25. private readonly string _url;
  26. private readonly int _port;
  27. private readonly TwitchCaps[] _caps;
  28. private readonly int _rateLimit;
  29. private readonly string _name;
  30.  
  31. private readonly ConcurrentQueue<string> _prioritySendQueue = new ConcurrentQueue<string>();
  32. private readonly ConcurrentQueue<string> _sendQueue = new ConcurrentQueue<string>();
  33.  
  34. private TcpClient _client;
  35. private Thread _readerThread;
  36. private Thread _senderThread;
  37. private SslStream _sslstream;
  38. private NetworkStream _stream;
  39. private bool _work;
  40.  
  41. private ConnectionState _connectionState;
  42.  
  43. internal Connection(string user, string password, string nick, string url, int port,
  44. TwitchCaps[] caps, int rateLimit, string name)
  45. {
  46. _connectionState = ConnectionState.Connecting;
  47. _name = name;
  48. _user = user;
  49. _password = password;
  50. _nick = nick;
  51. _url = url;
  52. _port = port;
  53. _caps = caps;
  54. _rateLimit = rateLimit;
  55.  
  56. ConnectionLogger.Debug(GetType(), $"{_name}: Connection created.");
  57. }
  58.  
  59. public event EventHandler<MessageEventArgs> RaiseMessageEvent;
  60. public event EventHandler<System.EventArgs> ConnectionEstablishedEvent;
  61. public event EventHandler<ConnectionUnexpectedCloseEventArgs> UnexpectedCloseEvent;
  62.  
  63. public IEnumerable<string> GetChannels()
  64. {
  65. return new List<string>(_channelList);
  66. }
  67.  
  68. public bool Connect()
  69. {
  70. ConnectionLogger.Debug(GetType(), $"{_name}: Try to connect.");
  71.  
  72. if (_client == null)
  73. {
  74. try
  75. {
  76. _client = new TcpClient();
  77. var result = _client.BeginConnect(_url, _port, null, null);
  78.  
  79. var success = result.AsyncWaitHandle.WaitOne(TimeSpan.FromSeconds(1));
  80.  
  81. if (!success)
  82. {
  83. throw new Exception("Connection Timeout");
  84. }
  85.  
  86. _client.EndConnect(result);
  87. }
  88. catch (Exception ex)
  89. {
  90. ConnectionLogger.Error(GetType(), ex);
  91. UnexpectedClose("TCP-Client Error");
  92. return false;
  93. }
  94.  
  95. _sslstream = null;
  96. _stream = null;
  97. }
  98.  
  99. if (!_client.Connected)
  100. return false;
  101.  
  102. ConnectionLogger.Debug(GetType(), $"{_name}: TCP-Client connected.");
  103.  
  104. if (_stream == null)
  105. _stream = _client.GetStream();
  106.  
  107. if (_sslstream == null)
  108. {
  109. try
  110. {
  111. var sslStream = new SslStream(
  112. _client.GetStream(),
  113. false,
  114. ConnectionManager.ValidateServerCertificate,
  115. null
  116. );
  117. sslStream.AuthenticateAsClient(_url);
  118. _sslstream = sslStream;
  119. }
  120. catch (Exception ex)
  121. {
  122. ConnectionLogger.Error(GetType(), ex);
  123. UnexpectedClose("SSL Error!");
  124. return false;
  125. }
  126. }
  127.  
  128. ConnectionLogger.Debug(GetType(), $"{_name}: SSL check okay.");
  129.  
  130. _work = true;
  131. _readerThread = new Thread(Reader);
  132. _readerThread.Start();
  133.  
  134. _senderThread = new Thread(Sender);
  135. _senderThread.Start();
  136.  
  137. EnqueueMessage(@"USER " + _user, true);
  138. EnqueueMessage(@"PASS " + _password, true);
  139. EnqueueMessage(@"NICK " + _nick, true);
  140.  
  141. if (_caps != null)
  142. {
  143. foreach (var cap in _caps)
  144. {
  145. EnqueueMessage(@"CAP REQ :twitch.tv/" + cap.ToString().ToLower(), true);
  146. }
  147. }
  148.  
  149. if (_channelList.Count != 0)
  150. {
  151. _channelList.ForEach(Join);
  152. }
  153.  
  154. ConnectionLogger.Debug(GetType(), $"{_name}: Connection complete.");
  155. _connectionState = ConnectionState.Fetching;
  156. return true;
  157. }
  158.  
  159. public void Join(string channel)
  160. {
  161. EnqueueMessage(@"JOIN #" + channel, true);
  162.  
  163. if (!_channelList.Contains(channel))
  164. _channelList.Add(channel);
  165. }
  166.  
  167. private void UnexpectedClose(string reason)
  168. {
  169. try
  170. {
  171. _work = false;
  172.  
  173. _client?.Close();
  174. _stream?.Close();
  175. _sslstream?.Close();
  176.  
  177. _client = null;
  178. _stream = null;
  179. _sslstream = null;
  180. }
  181. catch (Exception e)
  182. {
  183. ConnectionLogger.Error(GetType(), e);
  184. }
  185.  
  186. ConnectionLogger.Debug(GetType(), $"{_name}: Closed unexpected. Reason: {reason}");
  187. OnUnexpectedCloseEvent(new ConnectionUnexpectedCloseEventArgs(reason, _name));
  188. }
  189.  
  190. public bool Close()
  191. {
  192. try
  193. {
  194. _work = false;
  195.  
  196. _client?.Close();
  197. _stream?.Close();
  198. _sslstream?.Close();
  199.  
  200. _client = null;
  201. _stream = null;
  202. _sslstream = null;
  203.  
  204. ConnectionLogger.Debug(GetType(), $"{_name}: Connection closed.");
  205.  
  206. return true;
  207. }
  208. catch (Exception e)
  209. {
  210. ConnectionLogger.Error(
  211. GetType(),
  212. $"{_name}: Error while closing. See below for further information:"
  213. );
  214. ConnectionLogger.Error(GetType(), e);
  215.  
  216. return false;
  217. }
  218. }
  219.  
  220. private void EnqueueMessage(string message, bool hasPriority = false)
  221. {
  222. (hasPriority ? _prioritySendQueue : _sendQueue).Enqueue(message);
  223. }
  224.  
  225. private void Sender()
  226. {
  227. while (_client != null && _client.Connected)
  228. {
  229. if (!_work)
  230. return;
  231.  
  232. string message;
  233. if (!_prioritySendQueue.IsEmpty && _prioritySendQueue.TryDequeue(out message))
  234. {
  235. Write(message);
  236. ConnectionLogger.Debug(GetType(), $"{_name}: Sent priority message: " + message);
  237. }
  238. else if (!_sendQueue.IsEmpty && _sendQueue.TryDequeue(out message))
  239. {
  240. Write(message);
  241. ConnectionLogger.Debug(GetType(), $"{_name}: Sent message: " + message);
  242.  
  243. Thread.Sleep(_rateLimit);
  244. }
  245.  
  246. Thread.Sleep(1);
  247. }
  248. }
  249.  
  250. private void Reader()
  251. {
  252. var buffer = new byte[1024];
  253. var msg = new StringBuilder();
  254.  
  255. var badMessagesReceived = 0;
  256.  
  257. Stream stream = _sslstream;
  258.  
  259. while (_client != null && _client.Connected)
  260. {
  261. Thread.Sleep(1);
  262.  
  263. if (!_work)
  264. return;
  265.  
  266. if (!_stream.DataAvailable)
  267. continue;
  268.  
  269. Array.Clear(buffer, 0, buffer.Length);
  270.  
  271. stream.Read(buffer, 0, buffer.Length);
  272. var data = Encoding.UTF8.GetString(buffer).TrimEnd('\0');
  273.  
  274. msg.Append(data);
  275.  
  276. var msgstr = msg.ToString();
  277. if (!msgstr.EndsWith("\r\n"))
  278. {
  279. var all0 = buffer.All(b => b == 0);
  280.  
  281. if (all0)
  282. {
  283. if (++badMessagesReceived <= 2)
  284. continue;
  285.  
  286. UnexpectedClose("Received to many bad messages!");
  287. break;
  288. }
  289.  
  290. var idx = msgstr.LastIndexOf("\r\n", StringComparison.Ordinal);
  291. if (idx != -1)
  292. {
  293. idx += 2;
  294. msg.Remove(0, idx);
  295. msgstr = msgstr.Substring(0, idx);
  296. }
  297. else
  298. continue;
  299. }
  300. else
  301. msg.Clear();
  302.  
  303. var messages = msgstr.Split(MessageSeparators, StringSplitOptions.RemoveEmptyEntries);
  304.  
  305. foreach (var message in messages)
  306. {
  307. if (_connectionState == ConnectionState.Fetching)
  308. {
  309. if (!message.StartsWith(":")) continue;
  310. var end = message.IndexOf(" ", StringComparison.Ordinal);
  311. var sub = message.Substring(end).Trim();
  312. end = sub.IndexOf(" ", StringComparison.Ordinal);
  313. var code = sub.Substring(0, end);
  314. if (code != "004") continue;
  315. _connectionState = ConnectionState.Open;
  316. OnConnectionEstablishedEvent(new System.EventArgs());
  317. }
  318. else
  319. {
  320. if (message.StartsWith("PING"))
  321. {
  322. EnqueueMessage(message.Replace("PING", "PONG"), true);
  323. ConnectionLogger.Debug(GetType(), $"{_name}: PING received -> PONG sent!");
  324. }
  325. else
  326. {
  327. ConnectionLogger.Debug(GetType(), $"{_name}: Received message: " + message);
  328. }
  329.  
  330. OnRaiseMessageEvent(new MessageEventArgs(message));
  331. }
  332. }
  333. }
  334. }
  335.  
  336. private void Write(string message)
  337. {
  338. message = message.Replace("\r\n", " ");
  339.  
  340. var buffer = Encoding.UTF8.GetBytes(message + "\r\n");
  341.  
  342. try
  343. {
  344. _sslstream.Write(buffer, 0, buffer.Length);
  345. }
  346. catch (ObjectDisposedException)
  347. {
  348. UnexpectedClose("Lost Connection.");
  349. }
  350. catch (IOException)
  351. {
  352. UnexpectedClose("Lost Connection.");
  353. }
  354. }
  355.  
  356. protected virtual void OnRaiseMessageEvent(MessageEventArgs e)
  357. => RaiseMessageEvent?.Invoke(this, e);
  358.  
  359. protected virtual void OnConnectionEstablishedEvent(System.EventArgs e)
  360. => ConnectionEstablishedEvent?.Invoke(this, e);
  361.  
  362. protected virtual void OnUnexpectedCloseEvent(ConnectionUnexpectedCloseEventArgs e)
  363. => UnexpectedCloseEvent?.Invoke(this, e);
  364. }
  365. }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement