SslSession.cs 29 KB

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