UdpServer.cs 36 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951
  1. using System;
  2. using System.Diagnostics;
  3. using System.Net;
  4. using System.Net.Sockets;
  5. using System.Text;
  6. namespace NetCoreServer
  7. {
  8. /// <summary>
  9. /// UDP server is used to send or multicast datagrams to UDP endpoints
  10. /// </summary>
  11. /// <remarks>Thread-safe</remarks>
  12. public class UdpServer : IDisposable
  13. {
  14. /// <summary>
  15. /// Initialize UDP server with a given IP address and port number
  16. /// </summary>
  17. /// <param name="address">IP address</param>
  18. /// <param name="port">Port number</param>
  19. public UdpServer(IPAddress address, int port) : this(new IPEndPoint(address, port)) {}
  20. /// <summary>
  21. /// Initialize UDP server with a given IP address and port number
  22. /// </summary>
  23. /// <param name="address">IP address</param>
  24. /// <param name="port">Port number</param>
  25. public UdpServer(string address, int port) : this(new IPEndPoint(IPAddress.Parse(address), port)) {}
  26. /// <summary>
  27. /// Initialize UDP server with a given DNS endpoint
  28. /// </summary>
  29. /// <param name="endpoint">DNS endpoint</param>
  30. public UdpServer(DnsEndPoint endpoint) : this(endpoint as EndPoint, endpoint.Host, endpoint.Port) {}
  31. /// <summary>
  32. /// Initialize UDP server with a given IP endpoint
  33. /// </summary>
  34. /// <param name="endpoint">IP endpoint</param>
  35. public UdpServer(IPEndPoint endpoint) : this(endpoint as EndPoint, endpoint.Address.ToString(), endpoint.Port) {}
  36. /// <summary>
  37. /// Initialize UDP server with a given endpoint, address and port
  38. /// </summary>
  39. /// <param name="endpoint">Endpoint</param>
  40. /// <param name="address">Server address</param>
  41. /// <param name="port">Server port</param>
  42. private UdpServer(EndPoint endpoint, string address, int port)
  43. {
  44. Id = Guid.NewGuid();
  45. Address = address;
  46. Port = port;
  47. Endpoint = endpoint;
  48. }
  49. /// <summary>
  50. /// Server Id
  51. /// </summary>
  52. public Guid Id { get; }
  53. /// <summary>
  54. /// UDP server address
  55. /// </summary>
  56. public string Address { get; }
  57. /// <summary>
  58. /// UDP server port
  59. /// </summary>
  60. public int Port { get; }
  61. /// <summary>
  62. /// Endpoint
  63. /// </summary>
  64. public EndPoint Endpoint { get; private set; }
  65. /// <summary>
  66. /// Multicast endpoint
  67. /// </summary>
  68. public EndPoint MulticastEndpoint { get; private set; }
  69. /// <summary>
  70. /// Socket
  71. /// </summary>
  72. public Socket Socket { get; private set; }
  73. /// <summary>
  74. /// Number of bytes pending sent by the server
  75. /// </summary>
  76. public long BytesPending { get; private set; }
  77. /// <summary>
  78. /// Number of bytes sending by the server
  79. /// </summary>
  80. public long BytesSending { get; private set; }
  81. /// <summary>
  82. /// Number of bytes sent by the server
  83. /// </summary>
  84. public long BytesSent { get; private set; }
  85. /// <summary>
  86. /// Number of bytes received by the server
  87. /// </summary>
  88. public long BytesReceived { get; private set; }
  89. /// <summary>
  90. /// Number of datagrams sent by the server
  91. /// </summary>
  92. public long DatagramsSent { get; private set; }
  93. /// <summary>
  94. /// Number of datagrams received by the server
  95. /// </summary>
  96. public long DatagramsReceived { get; private set; }
  97. /// <summary>
  98. /// Option: dual mode socket
  99. /// </summary>
  100. /// <remarks>
  101. /// Specifies whether the Socket is a dual-mode socket used for both IPv4 and IPv6.
  102. /// Will work only if socket is bound on IPv6 address.
  103. /// </remarks>
  104. public bool OptionDualMode { get; set; }
  105. /// <summary>
  106. /// Option: reuse address
  107. /// </summary>
  108. /// <remarks>
  109. /// This option will enable/disable SO_REUSEADDR if the OS support this feature
  110. /// </remarks>
  111. public bool OptionReuseAddress { get; set; }
  112. /// <summary>
  113. /// Option: enables a socket to be bound for exclusive access
  114. /// </summary>
  115. /// <remarks>
  116. /// This option will enable/disable SO_EXCLUSIVEADDRUSE if the OS support this feature
  117. /// </remarks>
  118. public bool OptionExclusiveAddressUse { get; set; }
  119. /// <summary>
  120. /// Option: receive buffer limit
  121. /// </summary>
  122. public int OptionReceiveBufferLimit { get; set; } = 0;
  123. /// <summary>
  124. /// Option: receive buffer size
  125. /// </summary>
  126. public int OptionReceiveBufferSize { get; set; } = 8192;
  127. /// <summary>
  128. /// Option: send buffer limit
  129. /// </summary>
  130. public int OptionSendBufferLimit { get; set; } = 0;
  131. /// <summary>
  132. /// Option: send buffer size
  133. /// </summary>
  134. public int OptionSendBufferSize { get; set; } = 8192 * 10;
  135. #region Connect/Disconnect client
  136. /// <summary>
  137. /// Is the server started?
  138. /// </summary>
  139. public bool IsStarted { get; private set; }
  140. /// <summary>
  141. /// Create a new socket object
  142. /// </summary>
  143. /// <remarks>
  144. /// Method may be override if you need to prepare some specific socket object in your implementation.
  145. /// </remarks>
  146. /// <returns>Socket object</returns>
  147. protected virtual Socket CreateSocket()
  148. {
  149. return new Socket(Endpoint.AddressFamily, SocketType.Dgram, ProtocolType.Udp);
  150. }
  151. /// <summary>
  152. /// Start the server (synchronous)
  153. /// </summary>
  154. /// <returns>'true' if the server was successfully started, 'false' if the server failed to start</returns>
  155. public virtual bool Start()
  156. {
  157. Debug.Assert(!IsStarted, "UDP server is already started!");
  158. if (IsStarted)
  159. return false;
  160. // Setup buffers
  161. _receiveBuffer = new Buffer();
  162. _sendBuffer = new Buffer();
  163. // Setup event args
  164. _receiveEventArg = new SocketAsyncEventArgs();
  165. _receiveEventArg.Completed += OnAsyncCompleted;
  166. _sendEventArg = new SocketAsyncEventArgs();
  167. _sendEventArg.Completed += OnAsyncCompleted;
  168. // Create a new server socket
  169. Socket = CreateSocket();
  170. // Update the server socket disposed flag
  171. IsSocketDisposed = false;
  172. // Apply the option: reuse address
  173. Socket.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ReuseAddress, OptionReuseAddress);
  174. // Apply the option: exclusive address use
  175. Socket.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ExclusiveAddressUse, OptionExclusiveAddressUse);
  176. // Apply the option: dual mode (this option must be applied before recieving)
  177. if (Socket.AddressFamily == AddressFamily.InterNetworkV6)
  178. Socket.DualMode = OptionDualMode;
  179. // Bind the server socket to the endpoint
  180. Socket.Bind(Endpoint);
  181. // Refresh the endpoint property based on the actual endpoint created
  182. Endpoint = Socket.LocalEndPoint;
  183. // Call the server starting handler
  184. OnStarting();
  185. // Prepare receive endpoint
  186. _receiveEndpoint = new IPEndPoint((Endpoint.AddressFamily == AddressFamily.InterNetworkV6) ? IPAddress.IPv6Any : IPAddress.Any, 0);
  187. // Prepare receive & send buffers
  188. _receiveBuffer.Reserve(OptionReceiveBufferSize);
  189. // Reset statistic
  190. BytesPending = 0;
  191. BytesSending = 0;
  192. BytesSent = 0;
  193. BytesReceived = 0;
  194. DatagramsSent = 0;
  195. DatagramsReceived = 0;
  196. // Update the started flag
  197. IsStarted = true;
  198. // Call the server started handler
  199. OnStarted();
  200. return true;
  201. }
  202. /// <summary>
  203. /// Start the server with a given multicast IP address and port number (synchronous)
  204. /// </summary>
  205. /// <param name="multicastAddress">Multicast IP address</param>
  206. /// <param name="multicastPort">Multicast port number</param>
  207. /// <returns>'true' if the server was successfully started, 'false' if the server failed to start</returns>
  208. public virtual bool Start(IPAddress multicastAddress, int multicastPort) => Start(new IPEndPoint(multicastAddress, multicastPort));
  209. /// <summary>
  210. /// Start the server with a given multicast IP address and port number (synchronous)
  211. /// </summary>
  212. /// <param name="multicastAddress">Multicast IP address</param>
  213. /// <param name="multicastPort">Multicast port number</param>
  214. /// <returns>'true' if the server was successfully started, 'false' if the server failed to start</returns>
  215. public virtual bool Start(string multicastAddress, int multicastPort) => Start(new IPEndPoint(IPAddress.Parse(multicastAddress), multicastPort));
  216. /// <summary>
  217. /// Start the server with a given multicast endpoint (synchronous)
  218. /// </summary>
  219. /// <param name="multicastEndpoint">Multicast endpoint</param>
  220. /// <returns>'true' if the server was successfully started, 'false' if the server failed to start</returns>
  221. public virtual bool Start(EndPoint multicastEndpoint)
  222. {
  223. MulticastEndpoint = multicastEndpoint;
  224. return Start();
  225. }
  226. /// <summary>
  227. /// Stop the server (synchronous)
  228. /// </summary>
  229. /// <returns>'true' if the server was successfully stopped, 'false' if the server is already stopped</returns>
  230. public virtual bool Stop()
  231. {
  232. Debug.Assert(IsStarted, "UDP server is not started!");
  233. if (!IsStarted)
  234. return false;
  235. // Reset event args
  236. _receiveEventArg.Completed -= OnAsyncCompleted;
  237. _sendEventArg.Completed -= OnAsyncCompleted;
  238. // Call the server stopping handler
  239. OnStopping();
  240. try
  241. {
  242. // Close the server socket
  243. Socket.Close();
  244. // Dispose the server socket
  245. Socket.Dispose();
  246. // Dispose event arguments
  247. _receiveEventArg.Dispose();
  248. _sendEventArg.Dispose();
  249. // Update the server socket disposed flag
  250. IsSocketDisposed = true;
  251. }
  252. catch (ObjectDisposedException) {}
  253. // Update the started flag
  254. IsStarted = false;
  255. // Update sending/receiving flags
  256. _receiving = false;
  257. _sending = false;
  258. // Clear send/receive buffers
  259. ClearBuffers();
  260. // Call the server stopped handler
  261. OnStopped();
  262. return true;
  263. }
  264. /// <summary>
  265. /// Restart the server (synchronous)
  266. /// </summary>
  267. /// <returns>'true' if the server was successfully restarted, 'false' if the server failed to restart</returns>
  268. public virtual bool Restart()
  269. {
  270. if (!Stop())
  271. return false;
  272. return Start();
  273. }
  274. #endregion
  275. #region Send/Receive data
  276. // Receive and send endpoints
  277. EndPoint _receiveEndpoint;
  278. EndPoint _sendEndpoint;
  279. // Receive buffer
  280. private bool _receiving;
  281. private Buffer _receiveBuffer;
  282. private SocketAsyncEventArgs _receiveEventArg;
  283. // Send buffer
  284. private bool _sending;
  285. private Buffer _sendBuffer;
  286. private SocketAsyncEventArgs _sendEventArg;
  287. /// <summary>
  288. /// Multicast datagram to the prepared mulicast endpoint (synchronous)
  289. /// </summary>
  290. /// <param name="buffer">Datagram buffer to multicast</param>
  291. /// <returns>Size of multicasted datagram</returns>
  292. public virtual long Multicast(byte[] buffer) => Multicast(buffer.AsSpan());
  293. /// <summary>
  294. /// Multicast datagram to the prepared mulicast endpoint (synchronous)
  295. /// </summary>
  296. /// <param name="buffer">Datagram buffer to multicast</param>
  297. /// <param name="offset">Datagram buffer offset</param>
  298. /// <param name="size">Datagram buffer size</param>
  299. /// <returns>Size of multicasted datagram</returns>
  300. public virtual long Multicast(byte[] buffer, long offset, long size) => Multicast(buffer.AsSpan((int)offset, (int)size));
  301. /// <summary>
  302. /// Multicast datagram to the prepared mulicast endpoint (synchronous)
  303. /// </summary>
  304. /// <param name="buffer">Datagram buffer to multicast as a span of bytes</param>
  305. /// <returns>Size of multicasted datagram</returns>
  306. public virtual long Multicast(ReadOnlySpan<byte> buffer) => Send(MulticastEndpoint, buffer);
  307. /// <summary>
  308. /// Multicast text to the prepared mulicast endpoint (synchronous)
  309. /// </summary>
  310. /// <param name="text">Text string to multicast</param>
  311. /// <returns>Size of multicasted datagram</returns>
  312. public virtual long Multicast(string text) => Multicast(Encoding.UTF8.GetBytes(text));
  313. /// <summary>
  314. /// Multicast text to the prepared mulicast endpoint (synchronous)
  315. /// </summary>
  316. /// <param name="text">Text to multicast as a span of characters</param>
  317. /// <returns>Size of multicasted datagram</returns>
  318. public virtual long Multicast(ReadOnlySpan<char> text) => Multicast(Encoding.UTF8.GetBytes(text.ToArray()));
  319. /// <summary>
  320. /// Multicast datagram to the prepared mulicast endpoint (asynchronous)
  321. /// </summary>
  322. /// <param name="buffer">Datagram buffer to multicast</param>
  323. /// <returns>'true' if the datagram was successfully multicasted, 'false' if the datagram was not multicasted</returns>
  324. public virtual bool MulticastAsync(byte[] buffer) => MulticastAsync(buffer.AsSpan());
  325. /// <summary>
  326. /// Multicast datagram to the prepared mulicast endpoint (asynchronous)
  327. /// </summary>
  328. /// <param name="buffer">Datagram buffer to multicast</param>
  329. /// <param name="offset">Datagram buffer offset</param>
  330. /// <param name="size">Datagram buffer size</param>
  331. /// <returns>'true' if the datagram was successfully multicasted, 'false' if the datagram was not multicasted</returns>
  332. public virtual bool MulticastAsync(byte[] buffer, long offset, long size) => MulticastAsync(buffer.AsSpan((int)offset, (int)size));
  333. /// <summary>
  334. /// Multicast datagram to the prepared mulicast endpoint (asynchronous)
  335. /// </summary>
  336. /// <param name="buffer">Datagram buffer to multicast as a span of bytes</param>
  337. /// <returns>'true' if the datagram was successfully multicasted, 'false' if the datagram was not multicasted</returns>
  338. public virtual bool MulticastAsync(ReadOnlySpan<byte> buffer) => SendAsync(MulticastEndpoint, buffer);
  339. /// <summary>
  340. /// Multicast text to the prepared mulicast endpoint (asynchronous)
  341. /// </summary>
  342. /// <param name="text">Text string to multicast</param>
  343. /// <returns>'true' if the text was successfully multicasted, 'false' if the text was not multicasted</returns>
  344. public virtual bool MulticastAsync(string text) => MulticastAsync(Encoding.UTF8.GetBytes(text));
  345. /// <summary>
  346. /// Multicast text to the prepared mulicast endpoint (asynchronous)
  347. /// </summary>
  348. /// <param name="text">Text to multicast as a span of characters</param>
  349. /// <returns>'true' if the text was successfully multicasted, 'false' if the text was not multicasted</returns>
  350. public virtual bool MulticastAsync(ReadOnlySpan<char> text) => MulticastAsync(Encoding.UTF8.GetBytes(text.ToArray()));
  351. /// <summary>
  352. /// Send datagram to the connected server (synchronous)
  353. /// </summary>
  354. /// <param name="buffer">Datagram buffer to send</param>
  355. /// <returns>Size of sent datagram</returns>
  356. public virtual long Send(byte[] buffer) => Send(buffer.AsSpan());
  357. /// <summary>
  358. /// Send datagram to the connected server (synchronous)
  359. /// </summary>
  360. /// <param name="buffer">Datagram buffer to send</param>
  361. /// <param name="offset">Datagram buffer offset</param>
  362. /// <param name="size">Datagram buffer size</param>
  363. /// <returns>Size of sent datagram</returns>
  364. public virtual long Send(byte[] buffer, long offset, long size) => Send(buffer.AsSpan((int)offset, (int)size));
  365. /// <summary>
  366. /// Send datagram to the connected server (synchronous)
  367. /// </summary>
  368. /// <param name="buffer">Datagram buffer to send as a span of bytes</param>
  369. /// <returns>Size of sent datagram</returns>
  370. public virtual long Send(ReadOnlySpan<byte> buffer) => Send(Endpoint, buffer);
  371. /// <summary>
  372. /// Send text to the connected server (synchronous)
  373. /// </summary>
  374. /// <param name="text">Text string to send</param>
  375. /// <returns>Size of sent datagram</returns>
  376. public virtual long Send(string text) => Send(Encoding.UTF8.GetBytes(text));
  377. /// <summary>
  378. /// Send text to the connected server (synchronous)
  379. /// </summary>
  380. /// <param name="text">Text to send as a span of characters</param>
  381. /// <returns>Size of sent datagram</returns>
  382. public virtual long Send(ReadOnlySpan<char> text) => Send(Encoding.UTF8.GetBytes(text.ToArray()));
  383. /// <summary>
  384. /// Send datagram to the given endpoint (synchronous)
  385. /// </summary>
  386. /// <param name="endpoint">Endpoint to send</param>
  387. /// <param name="buffer">Datagram buffer to send</param>
  388. /// <returns>Size of sent datagram</returns>
  389. public virtual long Send(EndPoint endpoint, byte[] buffer) => Send(endpoint, buffer.AsSpan());
  390. /// <summary>
  391. /// Send datagram to the given endpoint (synchronous)
  392. /// </summary>
  393. /// <param name="endpoint">Endpoint to send</param>
  394. /// <param name="buffer">Datagram buffer to send</param>
  395. /// <param name="offset">Datagram buffer offset</param>
  396. /// <param name="size">Datagram buffer size</param>
  397. /// <returns>Size of sent datagram</returns>
  398. public virtual long Send(EndPoint endpoint, byte[] buffer, long offset, long size) => Send(endpoint, buffer.AsSpan((int)offset, (int)size));
  399. /// <summary>
  400. /// Send datagram to the given endpoint (synchronous)
  401. /// </summary>
  402. /// <param name="endpoint">Endpoint to send</param>
  403. /// <param name="buffer">Datagram buffer to send as a span of bytes</param>
  404. /// <returns>Size of sent datagram</returns>
  405. public virtual long Send(EndPoint endpoint, ReadOnlySpan<byte> buffer)
  406. {
  407. if (!IsStarted)
  408. return 0;
  409. if (buffer.IsEmpty)
  410. return 0;
  411. try
  412. {
  413. // Sent datagram to the client
  414. long sent = Socket.SendTo(buffer, SocketFlags.None, endpoint);
  415. if (sent > 0)
  416. {
  417. // Update statistic
  418. DatagramsSent++;
  419. BytesSent += sent;
  420. // Call the datagram sent handler
  421. OnSent(endpoint, sent);
  422. }
  423. return sent;
  424. }
  425. catch (ObjectDisposedException) { return 0; }
  426. catch (SocketException ex)
  427. {
  428. SendError(ex.SocketErrorCode);
  429. return 0;
  430. }
  431. }
  432. /// <summary>
  433. /// Send text to the given endpoint (synchronous)
  434. /// </summary>
  435. /// <param name="endpoint">Endpoint to send</param>
  436. /// <param name="text">Text string to send</param>
  437. /// <returns>Size of sent datagram</returns>
  438. public virtual long Send(EndPoint endpoint, string text) => Send(endpoint, Encoding.UTF8.GetBytes(text));
  439. /// <summary>
  440. /// Send text to the given endpoint (synchronous)
  441. /// </summary>
  442. /// <param name="endpoint">Endpoint to send</param>
  443. /// <param name="text">Text to send as a span of characters</param>
  444. /// <returns>Size of sent datagram</returns>
  445. public virtual long Send(EndPoint endpoint, ReadOnlySpan<char> text) => Send(endpoint, Encoding.UTF8.GetBytes(text.ToArray()));
  446. /// <summary>
  447. /// Send datagram to the given endpoint (asynchronous)
  448. /// </summary>
  449. /// <param name="endpoint">Endpoint to send</param>
  450. /// <param name="buffer">Datagram buffer to send</param>
  451. /// <returns>'true' if the datagram was successfully sent, 'false' if the datagram was not sent</returns>
  452. public virtual bool SendAsync(EndPoint endpoint, byte[] buffer) => SendAsync(endpoint, buffer.AsSpan());
  453. /// <summary>
  454. /// Send datagram to the given endpoint (asynchronous)
  455. /// </summary>
  456. /// <param name="endpoint">Endpoint to send</param>
  457. /// <param name="buffer">Datagram buffer to send</param>
  458. /// <param name="offset">Datagram buffer offset</param>
  459. /// <param name="size">Datagram buffer size</param>
  460. /// <returns>'true' if the datagram was successfully sent, 'false' if the datagram was not sent</returns>
  461. public virtual bool SendAsync(EndPoint endpoint, byte[] buffer, long offset, long size) => SendAsync(endpoint, buffer.AsSpan((int)offset, (int)size));
  462. /// <summary>
  463. /// Send datagram to the given endpoint (asynchronous)
  464. /// </summary>
  465. /// <param name="endpoint">Endpoint to send</param>
  466. /// <param name="buffer">Datagram buffer to send as a span of bytes</param>
  467. /// <returns>'true' if the datagram was successfully sent, 'false' if the datagram was not sent</returns>
  468. public virtual bool SendAsync(EndPoint endpoint, ReadOnlySpan<byte> buffer)
  469. {
  470. if (_sending)
  471. return false;
  472. if (!IsStarted)
  473. return false;
  474. if (buffer.IsEmpty)
  475. return true;
  476. // Check the send buffer limit
  477. if (((_sendBuffer.Size + buffer.Length) > OptionSendBufferLimit) && (OptionSendBufferLimit > 0))
  478. {
  479. SendError(SocketError.NoBufferSpaceAvailable);
  480. return false;
  481. }
  482. // Fill the main send buffer
  483. _sendBuffer.Append(buffer);
  484. // Update statistic
  485. BytesSending = _sendBuffer.Size;
  486. // Update send endpoint
  487. _sendEndpoint = endpoint;
  488. // Try to send the main buffer
  489. TrySend();
  490. return true;
  491. }
  492. /// <summary>
  493. /// Send text to the given endpoint (asynchronous)
  494. /// </summary>
  495. /// <param name="endpoint">Endpoint to send</param>
  496. /// <param name="text">Text string to send</param>
  497. /// <returns>'true' if the text was successfully sent, 'false' if the text was not sent</returns>
  498. public virtual bool SendAsync(EndPoint endpoint, string text) => SendAsync(endpoint, Encoding.UTF8.GetBytes(text));
  499. /// <summary>
  500. /// Send text to the given endpoint (asynchronous)
  501. /// </summary>
  502. /// <param name="endpoint">Endpoint to send</param>
  503. /// <param name="text">Text to send as a span of characters</param>
  504. /// <returns>'true' if the text was successfully sent, 'false' if the text was not sent</returns>
  505. public virtual bool SendAsync(EndPoint endpoint, ReadOnlySpan<char> text) => SendAsync(endpoint, Encoding.UTF8.GetBytes(text.ToArray()));
  506. /// <summary>
  507. /// Receive a new datagram from the given endpoint (synchronous)
  508. /// </summary>
  509. /// <param name="endpoint">Endpoint to receive from</param>
  510. /// <param name="buffer">Datagram buffer to receive</param>
  511. /// <returns>Size of received datagram</returns>
  512. public virtual long Receive(ref EndPoint endpoint, byte[] buffer) { return Receive(ref endpoint, buffer, 0, buffer.Length); }
  513. /// <summary>
  514. /// Receive a new datagram from the given endpoint (synchronous)
  515. /// </summary>
  516. /// <param name="endpoint">Endpoint to receive from</param>
  517. /// <param name="buffer">Datagram buffer to receive</param>
  518. /// <param name="offset">Datagram buffer offset</param>
  519. /// <param name="size">Datagram buffer size</param>
  520. /// <returns>Size of received datagram</returns>
  521. public virtual long Receive(ref EndPoint endpoint, byte[] buffer, long offset, long size)
  522. {
  523. if (!IsStarted)
  524. return 0;
  525. if (size == 0)
  526. return 0;
  527. try
  528. {
  529. // Receive datagram from the client
  530. long received = Socket.ReceiveFrom(buffer, (int)offset, (int)size, SocketFlags.None, ref endpoint);
  531. // Update statistic
  532. DatagramsReceived++;
  533. BytesReceived += received;
  534. // Call the datagram received handler
  535. OnReceived(endpoint, buffer, offset, size);
  536. return received;
  537. }
  538. catch (ObjectDisposedException) { return 0; }
  539. catch (SocketException ex)
  540. {
  541. SendError(ex.SocketErrorCode);
  542. return 0;
  543. }
  544. }
  545. /// <summary>
  546. /// Receive text from the given endpoint (synchronous)
  547. /// </summary>
  548. /// <param name="endpoint">Endpoint to receive from</param>
  549. /// <param name="size">Text size to receive</param>
  550. /// <returns>Received text</returns>
  551. public virtual string Receive(ref EndPoint endpoint, long size)
  552. {
  553. var buffer = new byte[size];
  554. var length = Receive(ref endpoint, buffer);
  555. return Encoding.UTF8.GetString(buffer, 0, (int)length);
  556. }
  557. /// <summary>
  558. /// Receive datagram from the client (asynchronous)
  559. /// </summary>
  560. public virtual void ReceiveAsync()
  561. {
  562. // Try to receive datagram
  563. TryReceive();
  564. }
  565. /// <summary>
  566. /// Try to receive new data
  567. /// </summary>
  568. private void TryReceive()
  569. {
  570. if (_receiving)
  571. return;
  572. if (!IsStarted)
  573. return;
  574. try
  575. {
  576. // Async receive with the receive handler
  577. _receiving = true;
  578. _receiveEventArg.RemoteEndPoint = _receiveEndpoint;
  579. _receiveEventArg.SetBuffer(_receiveBuffer.Data, 0, (int)_receiveBuffer.Capacity);
  580. if (!Socket.ReceiveFromAsync(_receiveEventArg))
  581. ProcessReceiveFrom(_receiveEventArg);
  582. }
  583. catch (ObjectDisposedException) {}
  584. }
  585. /// <summary>
  586. /// Try to send pending data
  587. /// </summary>
  588. private void TrySend()
  589. {
  590. if (_sending)
  591. return;
  592. if (!IsStarted)
  593. return;
  594. try
  595. {
  596. // Async write with the write handler
  597. _sending = true;
  598. _sendEventArg.RemoteEndPoint = _sendEndpoint;
  599. _sendEventArg.SetBuffer(_sendBuffer.Data, 0, (int)(_sendBuffer.Size));
  600. if (!Socket.SendToAsync(_sendEventArg))
  601. ProcessSendTo(_sendEventArg);
  602. }
  603. catch (ObjectDisposedException) {}
  604. }
  605. /// <summary>
  606. /// Clear send/receive buffers
  607. /// </summary>
  608. private void ClearBuffers()
  609. {
  610. // Clear send buffers
  611. _sendBuffer.Clear();
  612. // Update statistic
  613. BytesPending = 0;
  614. BytesSending = 0;
  615. }
  616. #endregion
  617. #region IO processing
  618. /// <summary>
  619. /// This method is called whenever a receive or send operation is completed on a socket
  620. /// </summary>
  621. private void OnAsyncCompleted(object sender, SocketAsyncEventArgs e)
  622. {
  623. if (IsSocketDisposed)
  624. return;
  625. // Determine which type of operation just completed and call the associated handler
  626. switch (e.LastOperation)
  627. {
  628. case SocketAsyncOperation.ReceiveFrom:
  629. ProcessReceiveFrom(e);
  630. break;
  631. case SocketAsyncOperation.SendTo:
  632. ProcessSendTo(e);
  633. break;
  634. default:
  635. throw new ArgumentException("The last operation completed on the socket was not a receive or send");
  636. }
  637. }
  638. /// <summary>
  639. /// This method is invoked when an asynchronous receive from operation completes
  640. /// </summary>
  641. private void ProcessReceiveFrom(SocketAsyncEventArgs e)
  642. {
  643. _receiving = false;
  644. if (!IsStarted)
  645. return;
  646. // Check for error
  647. if (e.SocketError != SocketError.Success)
  648. {
  649. SendError(e.SocketError);
  650. // Call the datagram received zero handler
  651. OnReceived(e.RemoteEndPoint, _receiveBuffer.Data, 0, 0);
  652. return;
  653. }
  654. // Received some data from the client
  655. long size = e.BytesTransferred;
  656. // Update statistic
  657. DatagramsReceived++;
  658. BytesReceived += size;
  659. // Call the datagram received handler
  660. OnReceived(e.RemoteEndPoint, _receiveBuffer.Data, 0, size);
  661. // If the receive buffer is full increase its size
  662. if (_receiveBuffer.Capacity == size)
  663. {
  664. // Check the receive buffer limit
  665. if (((2 * size) > OptionReceiveBufferLimit) && (OptionReceiveBufferLimit > 0))
  666. {
  667. SendError(SocketError.NoBufferSpaceAvailable);
  668. // Call the datagram received zero handler
  669. OnReceived(e.RemoteEndPoint, _receiveBuffer.Data, 0, 0);
  670. return;
  671. }
  672. _receiveBuffer.Reserve(2 * size);
  673. }
  674. }
  675. /// <summary>
  676. /// This method is invoked when an asynchronous send to operation completes
  677. /// </summary>
  678. private void ProcessSendTo(SocketAsyncEventArgs e)
  679. {
  680. _sending = false;
  681. if (!IsStarted)
  682. return;
  683. // Check for error
  684. if (e.SocketError != SocketError.Success)
  685. {
  686. SendError(e.SocketError);
  687. // Call the buffer sent zero handler
  688. OnSent(_sendEndpoint, 0);
  689. return;
  690. }
  691. long sent = e.BytesTransferred;
  692. // Send some data to the client
  693. if (sent > 0)
  694. {
  695. // Update statistic
  696. BytesSending = 0;
  697. BytesSent += sent;
  698. // Clear the send buffer
  699. _sendBuffer.Clear();
  700. // Call the buffer sent handler
  701. OnSent(_sendEndpoint, sent);
  702. }
  703. }
  704. #endregion
  705. #region Datagram handlers
  706. /// <summary>
  707. /// Handle server starting notification
  708. /// </summary>
  709. protected virtual void OnStarting() {}
  710. /// <summary>
  711. /// Handle server started notification
  712. /// </summary>
  713. protected virtual void OnStarted() {}
  714. /// <summary>
  715. /// Handle server stopping notification
  716. /// </summary>
  717. protected virtual void OnStopping() {}
  718. /// <summary>
  719. /// Handle server stopped notification
  720. /// </summary>
  721. protected virtual void OnStopped() {}
  722. /// <summary>
  723. /// Handle datagram received notification
  724. /// </summary>
  725. /// <param name="endpoint">Received endpoint</param>
  726. /// <param name="buffer">Received datagram buffer</param>
  727. /// <param name="offset">Received datagram buffer offset</param>
  728. /// <param name="size">Received datagram buffer size</param>
  729. /// <remarks>
  730. /// Notification is called when another datagram was received from some endpoint
  731. /// </remarks>
  732. protected virtual void OnReceived(EndPoint endpoint, byte[] buffer, long offset, long size) {}
  733. /// <summary>
  734. /// Handle datagram sent notification
  735. /// </summary>
  736. /// <param name="endpoint">Endpoint of sent datagram</param>
  737. /// <param name="sent">Size of sent datagram buffer</param>
  738. /// <remarks>
  739. /// Notification is called when a datagram was sent to the client.
  740. /// This handler could be used to send another datagram to the client for instance when the pending size is zero.
  741. /// </remarks>
  742. protected virtual void OnSent(EndPoint endpoint, long sent) {}
  743. /// <summary>
  744. /// Handle error notification
  745. /// </summary>
  746. /// <param name="error">Socket error code</param>
  747. protected virtual void OnError(SocketError error) {}
  748. #endregion
  749. #region Error handling
  750. /// <summary>
  751. /// Send error notification
  752. /// </summary>
  753. /// <param name="error">Socket error code</param>
  754. private void SendError(SocketError error)
  755. {
  756. // Skip disconnect errors
  757. if ((error == SocketError.ConnectionAborted) ||
  758. (error == SocketError.ConnectionRefused) ||
  759. (error == SocketError.ConnectionReset) ||
  760. (error == SocketError.OperationAborted) ||
  761. (error == SocketError.Shutdown))
  762. return;
  763. OnError(error);
  764. }
  765. #endregion
  766. #region IDisposable implementation
  767. /// <summary>
  768. /// Disposed flag
  769. /// </summary>
  770. public bool IsDisposed { get; private set; }
  771. /// <summary>
  772. /// Server socket disposed flag
  773. /// </summary>
  774. public bool IsSocketDisposed { get; private set; } = true;
  775. // Implement IDisposable.
  776. public void Dispose()
  777. {
  778. Dispose(true);
  779. GC.SuppressFinalize(this);
  780. }
  781. protected virtual void Dispose(bool disposingManagedResources)
  782. {
  783. // The idea here is that Dispose(Boolean) knows whether it is
  784. // being called to do explicit cleanup (the Boolean is true)
  785. // versus being called due to a garbage collection (the Boolean
  786. // is false). This distinction is useful because, when being
  787. // disposed explicitly, the Dispose(Boolean) method can safely
  788. // execute code using reference type fields that refer to other
  789. // objects knowing for sure that these other objects have not been
  790. // finalized or disposed of yet. When the Boolean is false,
  791. // the Dispose(Boolean) method should not execute code that
  792. // refer to reference type fields because those objects may
  793. // have already been finalized."
  794. if (!IsDisposed)
  795. {
  796. if (disposingManagedResources)
  797. {
  798. // Dispose managed resources here...
  799. Stop();
  800. }
  801. // Dispose unmanaged resources here...
  802. // Set large fields to null here...
  803. // Mark as disposed.
  804. IsDisposed = true;
  805. }
  806. }
  807. #endregion
  808. }
  809. }