WebSocketClient.cs 7.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199
  1. //------------------------------------------------------------------------------
  2. // 此代码版权(除特别声明或在XREF结尾的命名空间的代码)归作者本人若汝棋茗所有
  3. // 源代码使用协议遵循本仓库的开源协议及附加协议,若本仓库没有设置,则按MIT开源协议授权
  4. // CSDN博客:https://blog.csdn.net/qq_40374647
  5. // 哔哩哔哩视频:https://space.bilibili.com/94253567
  6. // Gitee源代码仓库:https://gitee.com/RRQM_Home
  7. // Github源代码仓库:https://github.com/RRQM
  8. // API首页:https://www.yuque.com/rrqm/touchsocket/index
  9. // 交流QQ群:234762506
  10. // 感谢您的下载和使用
  11. //------------------------------------------------------------------------------
  12. //------------------------------------------------------------------------------
  13. using System;
  14. using System.Threading;
  15. using System.Threading.Tasks;
  16. using TouchSocket.Core;
  17. using TouchSocket.Sockets;
  18. namespace TouchSocket.Http.WebSockets
  19. {
  20. /// <summary>
  21. /// WebSocketClient用户终端简单实现。
  22. /// </summary>
  23. public class WebSocketClient : WebSocketClientBase
  24. {
  25. /// <summary>
  26. /// 收到WebSocket数据
  27. /// </summary>
  28. public WSDataFrameEventHandler<WebSocketClient> Received { get; set; }
  29. /// <summary>
  30. /// <inheritdoc/>
  31. /// </summary>
  32. /// <param name="dataFrame"></param>
  33. protected override void OnHandleWSDataFrame(WSDataFrame dataFrame)
  34. {
  35. Received?.Invoke(this, dataFrame);
  36. base.OnHandleWSDataFrame(dataFrame);
  37. }
  38. }
  39. /// <summary>
  40. /// WebSocket用户终端。
  41. /// </summary>
  42. public class WebSocketClientBase : HttpClientBase, IWebSocketClient
  43. {
  44. /// <summary>
  45. /// 请求连接到WebSocket。
  46. /// </summary>
  47. /// <returns></returns>
  48. public override ITcpClient Connect(int timeout = 5000)
  49. {
  50. return Connect(default, timeout);
  51. }
  52. /// <summary>
  53. /// <inheritdoc/>
  54. /// </summary>
  55. /// <param name="timeout"></param>
  56. /// <param name="token"></param>
  57. /// <returns></returns>
  58. public virtual ITcpClient Connect(CancellationToken token, int timeout = 5000)
  59. {
  60. lock (this)
  61. {
  62. if (!Online)
  63. {
  64. base.Connect(timeout);
  65. }
  66. string base64Key;
  67. IPHost iPHost = Config.GetValue<IPHost>(TouchSocketConfigExtension.RemoteIPHostProperty);
  68. string url = iPHost.IsUri ? iPHost.Uri.PathAndQuery : string.Empty;
  69. HttpRequest request = WSTools.GetWSRequest(RemoteIPHost.Host, url, this.GetWebSocketVersion(), out base64Key);
  70. OnHandshaking(new HttpContextEventArgs(new HttpContext(request)));
  71. var response = Request(request, timeout: timeout, token: token);
  72. if (!response.StatusCode.Trim().Equals("101"))
  73. {
  74. throw new WebSocketConnectException($"协议升级失败,信息:{response.StatusMessage},更多信息请捕获WebSocketConnectException异常,获得HttpContext得知。", new HttpContext(request, response));
  75. }
  76. string accept = response.GetHeader("sec-websocket-accept").Trim();
  77. if (accept.IsNullOrEmpty() || !accept.Equals(WSTools.CalculateBase64Key(base64Key).Trim(), StringComparison.OrdinalIgnoreCase))
  78. {
  79. MainSocket.SafeDispose();
  80. throw new WebSocketConnectException($"WS服务器返回的应答码不正确,更多信息请捕获WebSocketConnectException异常,获得HttpContext得知。", new HttpContext(request, response));
  81. }
  82. SetAdapter(new WebSocketDataHandlingAdapter());
  83. SetValue(WebSocketServerPlugin.HandshakedProperty, true);
  84. response.Flag = true;
  85. OnHandshaked(new HttpContextEventArgs(new HttpContext(request, response)));
  86. return this;
  87. }
  88. }
  89. /// <summary>
  90. /// <inheritdoc/>
  91. /// </summary>
  92. /// <param name="token"></param>
  93. /// <param name="timeout"></param>
  94. /// <returns></returns>
  95. public Task<ITcpClient> ConnectAsync(CancellationToken token, int timeout = 5000)
  96. {
  97. return EasyTask.Run(() =>
  98. {
  99. return Connect(token, timeout);
  100. });
  101. }
  102. #region 事件
  103. /// <summary>
  104. /// 表示在即将握手连接时。
  105. /// </summary>
  106. public HttpContextEventHandler<WebSocketClientBase> Handshaking { get; set; }
  107. /// <summary>
  108. /// 表示完成握手后。
  109. /// </summary>
  110. public HttpContextEventHandler<WebSocketClientBase> Handshaked { get; set; }
  111. /// <summary>
  112. /// 表示在即将握手连接时。
  113. /// </summary>
  114. /// <param name="e"></param>
  115. protected virtual void OnHandshaking(HttpContextEventArgs e)
  116. {
  117. if (UsePlugin && PluginsManager.Raise<IWebSocketPlugin>("OnHandshaking", this, e))
  118. {
  119. return;
  120. }
  121. Handshaking?.Invoke(this, e);
  122. }
  123. /// <summary>
  124. /// 表示完成握手后。
  125. /// </summary>
  126. /// <param name="e"></param>
  127. protected virtual void OnHandshaked(HttpContextEventArgs e)
  128. {
  129. if (UsePlugin && PluginsManager.Raise<IWebSocketPlugin>("OnHandshaked", this, e))
  130. {
  131. return;
  132. }
  133. Handshaked?.Invoke(this, e);
  134. }
  135. #endregion 事件
  136. /// <summary>
  137. /// 当收到WS数据时。
  138. /// </summary>
  139. /// <param name="dataFrame"></param>
  140. protected virtual void OnHandleWSDataFrame(WSDataFrame dataFrame)
  141. {
  142. if (UsePlugin)
  143. {
  144. PluginsManager.Raise<IWebSocketPlugin>("OnHandleWSDataFrame", this, new WSDataFrameEventArgs(dataFrame));
  145. }
  146. }
  147. /// <summary>
  148. /// <inheritdoc/>
  149. /// </summary>
  150. /// <param name="byteBlock"></param>
  151. /// <param name="requestInfo"></param>
  152. protected override void HandleReceivedData(ByteBlock byteBlock, IRequestInfo requestInfo)
  153. {
  154. if (this.GetHandshaked())
  155. {
  156. WSDataFrame dataFrame = (WSDataFrame)requestInfo;
  157. OnHandleWSDataFrame(dataFrame);
  158. }
  159. else
  160. {
  161. if (requestInfo is HttpResponse response)
  162. {
  163. response.Flag = false;
  164. base.HandleReceivedData(byteBlock, requestInfo);
  165. SpinWait.SpinUntil(() =>
  166. {
  167. return (bool)response.Flag;
  168. }, 1000);
  169. }
  170. }
  171. }
  172. /// <summary>
  173. /// <inheritdoc/>
  174. /// </summary>
  175. /// <param name="e"></param>
  176. protected override void OnDisconnected(DisconnectEventArgs e)
  177. {
  178. SetValue(WebSocketServerPlugin.HandshakedProperty, false);
  179. base.OnDisconnected(e);
  180. }
  181. }
  182. }