ServProgram.cs 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275
  1. using System;
  2. using System.Collections.Concurrent;
  3. using System.Collections.Generic;
  4. using System.Diagnostics;
  5. using System.IO;
  6. using System.Linq;
  7. using System.Net;
  8. using System.Net.Sockets;
  9. using System.Security.Cryptography;
  10. using System.Threading;
  11. namespace UdPunching.Serv
  12. {
  13. internal static class ServProgram
  14. {
  15. private static readonly ConcurrentDictionary<Guid, OnlineSession> OnlineSessions = new ConcurrentDictionary<Guid, OnlineSession>();
  16. private static RSACryptoServiceProvider _serverKey;
  17. private static IReadOnlyDictionary<Guid, RSACryptoServiceProvider> _peerKeyRegister;
  18. private static volatile int _packetSeq;
  19. private static Socket _svrSocket;
  20. private static void ProcessPacket(SocketAsyncEventArgs sae)
  21. {
  22. //KeepAlive 1s -------- add endpoint to list if no exist, return list
  23. //Logout -------- remove from list
  24. //Knock -------- PeerID
  25. ++_packetSeq;
  26. var peerId = TransferCodec.ReadId(sae.Buffer);
  27. _peerKeyRegister.TryGetValue(peerId, out var peerKey);
  28. if (null == peerKey)
  29. {
  30. Console.WriteLine($"pacet #{_packetSeq} peer id {peerId} no registered");
  31. _svrSocket.SendTo(BuildInPeerId.Invalid.ToByteArray(), sae.RemoteEndPoint);
  32. return;
  33. }
  34. ExchangeMessage requestMessage;
  35. try
  36. {
  37. var msgData = TransferCodec.DecodeData(_serverKey, sae.Buffer);
  38. requestMessage = new ExchangeMessage(msgData);
  39. }
  40. catch (Exception exception)
  41. {
  42. Console.WriteLine($"pacet #{_packetSeq} decode fail:{exception}");
  43. _svrSocket.SendTo(BuildInPeerId.Invalid.ToByteArray(), sae.RemoteEndPoint);
  44. return;
  45. }
  46. var responseMessage = new ExchangeMessage { TimeStamp = DateTime.Now };
  47. if (false == requestMessage.TimeStamp.HasValue || Math.Abs((DateTime.Now - requestMessage.TimeStamp.Value).TotalSeconds) > 10)
  48. {
  49. responseMessage.Id = ExchangeMessageId.ErrTimeStamp;
  50. }
  51. else
  52. {
  53. OnlineSessions.TryGetValue(peerId, out var session);
  54. if (null == session)
  55. {
  56. if (ExchangeMessageId.KeepAliveReq == requestMessage.Id)
  57. {
  58. session = new OnlineSession(peerId, sae.RemoteEndPoint);
  59. OnlineSessions[peerId] = session;
  60. responseMessage.Id = ExchangeMessageId.KeepAliveAckSessionCreated;
  61. responseMessage.PeerEndPoint = (IPEndPoint)sae.RemoteEndPoint;
  62. Console.WriteLine($"pacet #{_packetSeq} Session created: {peerId} from {sae.RemoteEndPoint}");
  63. }
  64. else
  65. {
  66. responseMessage.Id = ExchangeMessageId.ErrSessionNoCreated;
  67. }
  68. }
  69. else
  70. {
  71. session.Actively();
  72. switch (requestMessage.Id)
  73. {
  74. case ExchangeMessageId.KeepAliveReq:
  75. if (false == session.RemoteEndPoint.IpEndPointEqualsTo(sae.RemoteEndPoint))
  76. {
  77. session = new OnlineSession(peerId, sae.RemoteEndPoint);
  78. OnlineSessions[peerId] = session;
  79. responseMessage.Id = ExchangeMessageId.KeepAliveAckSessionCreated;
  80. responseMessage.PeerEndPoint = (IPEndPoint)sae.RemoteEndPoint;
  81. Console.WriteLine($"pacet #{_packetSeq} Session created: {peerId} from {sae.RemoteEndPoint}");
  82. }
  83. else
  84. {
  85. responseMessage.Id = ExchangeMessageId.KeepAliveAckNoChg;
  86. }
  87. break;
  88. case ExchangeMessageId.PeerKnockReq:
  89. if (false == requestMessage.PeerId.HasValue
  90. || false == _peerKeyRegister.TryGetValue(requestMessage.PeerId.Value, out var knockKey)
  91. || false == OnlineSessions.TryGetValue(requestMessage.PeerId.Value, out var knockSession)
  92. )
  93. {
  94. Console.WriteLine($"pacet #{_packetSeq} bad knock request: peer id no available {requestMessage.PeerId} by {peerId}");
  95. responseMessage.Id = ExchangeMessageId.PeerKnockReqErrPeerNoAvailable;
  96. responseMessage.PeerId = requestMessage.PeerId;
  97. break;
  98. }
  99. Console.WriteLine($"pacet #{_packetSeq} knock to {requestMessage.PeerId} by {peerId}");
  100. var reqRelayMessage = new ExchangeMessage(ExchangeMessageId.PeerKnockReqRelay)
  101. {
  102. PeerId = peerId,
  103. PeerEndPoint = (IPEndPoint)sae.RemoteEndPoint,
  104. };
  105. var reqRelayBytes = TransferCodec.Encode(knockKey, BuildInPeerId.Server, reqRelayMessage.ToBytes());
  106. _svrSocket.SendTo(reqRelayBytes, knockSession.RemoteEndPoint);
  107. responseMessage.Id = ExchangeMessageId.PeerKnockReqRelayed;
  108. responseMessage.PeerId = requestMessage.PeerId;
  109. break;
  110. case ExchangeMessageId.PeerKnockAck:
  111. if (false == requestMessage.PeerId.HasValue
  112. || false == _peerKeyRegister.TryGetValue(requestMessage.PeerId.Value, out var knockAckKey)
  113. || false == OnlineSessions.TryGetValue(requestMessage.PeerId.Value, out var knockAckSession)
  114. )
  115. {
  116. Console.WriteLine($"pacet #{_packetSeq} bad knock ack: peer id no available {requestMessage.PeerId}");
  117. responseMessage.Id = ExchangeMessageId.PeerKnockReqErrPeerNoAvailable;
  118. responseMessage.PeerId = requestMessage.PeerId;
  119. break;
  120. }
  121. Console.WriteLine($"pacet #{_packetSeq} knock ack {requestMessage.PeerId} by {peerId}");
  122. var ackRelayMessage = new ExchangeMessage(ExchangeMessageId.PeerKnockAckRelay)
  123. {
  124. PeerId = peerId,
  125. PeerEndPoint = (IPEndPoint)sae.RemoteEndPoint,
  126. };
  127. _svrSocket.SendExchangeMessageTo(BuildInPeerId.Server, ackRelayMessage, knockAckSession.RemoteEndPoint, knockAckKey);
  128. responseMessage.Id = ExchangeMessageId.PeerKnockAckRelayed;
  129. responseMessage.PeerId = requestMessage.PeerId;
  130. break;
  131. case ExchangeMessageId.PeerKnockDenied:
  132. break;
  133. //case ExchangeMessageId.PeerKnockConnectionReq:
  134. // break;
  135. //case ExchangeMessageId.PeerKnockConnectionAck:
  136. // break;
  137. //case ExchangeMessageId.ErrSessionNoCreated:
  138. // break;
  139. default:
  140. throw new ArgumentOutOfRangeException();
  141. }
  142. }
  143. }
  144. _svrSocket.SendExchangeMessageTo(BuildInPeerId.Server, responseMessage, sae.RemoteEndPoint, peerKey);
  145. }
  146. private static void DeadLoopTimeOutKiller()
  147. {
  148. while (true)
  149. {
  150. //TimeOutKiller
  151. const int timeOutSeconds = 10;
  152. foreach (var session in OnlineSessions.Values.ToArray())
  153. {
  154. if ((DateTime.Now - session.LastActively).TotalSeconds > timeOutSeconds)
  155. {
  156. OnlineSessions.TryRemove(session.Id, out _);
  157. Console.WriteLine($"Session removed by TimeOutKiller: {session.Id}");
  158. }
  159. }
  160. Thread.Sleep(timeOutSeconds * 1000);
  161. }
  162. // ReSharper disable once FunctionNeverReturns
  163. }
  164. private class OnlineSession
  165. {
  166. public Guid Id { get; }
  167. public DateTime LastActively { get; private set; }
  168. public EndPoint RemoteEndPoint { get; }
  169. public OnlineSession(Guid id, EndPoint remoteEndPoint)
  170. {
  171. Id = id;
  172. RemoteEndPoint = remoteEndPoint;
  173. Actively();
  174. }
  175. public void Actively() => LastActively = DateTime.Now;
  176. }
  177. private static void Main()
  178. {
  179. Console.WriteLine("Init...");
  180. _serverKey = new RSACryptoServiceProvider();
  181. _serverKey.FromXmlString(File.ReadAllText("ServerPrivateKey.txt"));
  182. _peerKeyRegister = Directory.GetFiles(Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "PeerPublicKey"))
  183. .ToDictionary(
  184. s => new Guid(Path.GetFileNameWithoutExtension(s)),
  185. p =>
  186. {
  187. var rsa = new RSACryptoServiceProvider();
  188. rsa.FromXmlString(File.ReadAllText(p));
  189. return rsa;
  190. }
  191. );
  192. _svrSocket = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp);
  193. _svrSocket.Bind(new IPEndPoint(IPAddress.Any, Properties.Settings.Default.ListenPort));
  194. Console.WriteLine($"Server Bind on {_svrSocket.LocalEndPoint}");
  195. const int receiveBufferSize = 1500;
  196. var sae = new SocketAsyncEventArgs();
  197. sae.SetBuffer(new byte[receiveBufferSize], 0, receiveBufferSize);
  198. sae.RemoteEndPoint = new IPEndPoint(IPAddress.Any, 0);
  199. Console.WriteLine($"Receive buffer:{receiveBufferSize}");
  200. void SaeOnCompleted(object sender, SocketAsyncEventArgs e)
  201. {
  202. if (sae.SocketError == SocketError.Success)
  203. {
  204. try
  205. {
  206. ProcessPacket(sae);
  207. }
  208. catch (Exception exception)
  209. {
  210. Console.WriteLine(exception);
  211. }
  212. }
  213. sae.RemoteEndPoint = new IPEndPoint(IPAddress.Any, 0);
  214. if (false == _svrSocket.ReceiveFromAsync(sae)) SaeOnCompleted(null, null);
  215. }
  216. sae.Completed += SaeOnCompleted;
  217. if (false == _svrSocket.ReceiveFromAsync(sae))
  218. {
  219. SaeOnCompleted(null, null);
  220. }
  221. Console.WriteLine($"Main thread enter dead sleep loop... to exit, kill me, pid:{Process.GetCurrentProcess().Id}");
  222. DeadLoopTimeOutKiller();
  223. }
  224. }
  225. }