TcpSession.cs 27 KB

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