UdsClient.cs 32 KB

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