WebSocketServerPlugin.cs 6.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183
  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 TouchSocket.Core;
  15. using TouchSocket.Sockets;
  16. namespace TouchSocket.Http.WebSockets
  17. {
  18. /// <summary>
  19. /// 基于Http的WebSocket的扩展。
  20. /// <para>此组件只能挂载在<see cref="HttpService"/>中</para>
  21. /// </summary>
  22. [SingletonPlugin]
  23. public class WebSocketServerPlugin : HttpPluginBase
  24. {
  25. /// <summary>
  26. /// 表示是否完成WS握手
  27. /// </summary>
  28. public static readonly DependencyProperty<bool> HandshakedProperty =
  29. DependencyProperty<bool>.Register("Handshaked", typeof(WebSocketServerPlugin), false);
  30. /// <summary>
  31. /// 表示WebSocketVersion
  32. /// </summary>
  33. public static readonly DependencyProperty<string> WebSocketVersionProperty =
  34. DependencyProperty<string>.Register("WebSocketVersion", typeof(WebSocketServerPlugin), "13");
  35. private readonly IPluginsManager m_pluginsManager;
  36. private string m_wSUrl = "/ws";
  37. /// <summary>
  38. /// WebSocketServerPlugin
  39. /// </summary>
  40. /// <param name="pluginsManager"></param>
  41. public WebSocketServerPlugin(IPluginsManager pluginsManager)
  42. {
  43. m_pluginsManager = pluginsManager ?? throw new ArgumentNullException(nameof(pluginsManager));
  44. }
  45. /// <summary>
  46. /// 是否默认处理Close报文。
  47. /// </summary>
  48. public bool AutoClose { get; set; } = true;
  49. /// <summary>
  50. /// 当收到ping报文时,是否自动回应pong。
  51. /// </summary>
  52. public bool AutoPong { get; set; }
  53. /// <summary>
  54. /// 处理WS数据的回调
  55. /// </summary>
  56. public Action<ITcpClientBase, WSDataFrameEventArgs> HandleWSDataFrameCallback { get; set; }
  57. /// <summary>
  58. /// 用于WebSocket连接的路径,默认为“/ws”
  59. /// <para>如果设置为null或空,则意味着所有的连接都将解释为WS</para>
  60. /// </summary>
  61. public string WSUrl
  62. {
  63. get => m_wSUrl;
  64. set => m_wSUrl = string.IsNullOrEmpty(value) ? "/" : value;
  65. }
  66. /// <summary>
  67. /// 不处理Close报文。
  68. /// </summary>
  69. /// <returns></returns>
  70. public WebSocketServerPlugin NoAutoClose()
  71. {
  72. AutoClose = false;
  73. return this;
  74. }
  75. /// <summary>
  76. /// 当收到ping报文时,自动回应pong。
  77. /// </summary>
  78. /// <returns></returns>
  79. public WebSocketServerPlugin UseAutoPong()
  80. {
  81. AutoPong = true;
  82. return this;
  83. }
  84. /// <summary>
  85. /// 设置处理WS数据的回调。
  86. /// </summary>
  87. /// <param name="action"></param>
  88. public WebSocketServerPlugin SetCallback(Action<ITcpClientBase, WSDataFrameEventArgs> action)
  89. {
  90. HandleWSDataFrameCallback = action;
  91. return this;
  92. }
  93. /// <summary>
  94. /// 用于WebSocket连接的路径,默认为“/ws”
  95. /// <para>如果设置为null或空,则意味着所有的连接都将解释为WS</para>
  96. /// </summary>
  97. /// <param name="url"></param>
  98. /// <returns></returns>
  99. public WebSocketServerPlugin SetWSUrl(string url)
  100. {
  101. WSUrl = url;
  102. return this;
  103. }
  104. /// <summary>
  105. /// <inheritdoc/>
  106. /// </summary>
  107. /// <param name="client"></param>
  108. /// <param name="e"></param>
  109. protected override void OnGet(ITcpClientBase client, HttpContextEventArgs e)
  110. {
  111. if (WSUrl == "/" || e.Context.Request.UrlEquals(WSUrl))
  112. {
  113. if (client.Protocol == Protocol.Http)
  114. {
  115. e.Handled = true;
  116. if (client is HttpSocketClient socketClient)
  117. {
  118. socketClient.SwitchProtocolToWebSocket(e.Context);
  119. }
  120. }
  121. }
  122. base.OnGet(client, e);
  123. }
  124. /// <summary>
  125. /// 处理WS数据帧。覆盖父类方法将不会触发<see cref="HandleWSDataFrameCallback"/>回调和插件。
  126. /// </summary>
  127. /// <param name="client"></param>
  128. /// <param name="e"></param>
  129. protected virtual void OnHandleWSDataFrame(ITcpClientBase client, WSDataFrameEventArgs e)
  130. {
  131. if (AutoClose&&e.DataFrame.Opcode == WSDataType.Close)
  132. {
  133. string msg = e.DataFrame.PayloadData?.ToString();
  134. m_pluginsManager.Raise<IWebSocketPlugin>(nameof(IWebSocketPlugin.OnClosing), client, new MsgEventArgs() { Message = msg });
  135. client.Close(msg);
  136. return;
  137. }
  138. if (AutoPong&& e.DataFrame.Opcode == WSDataType.Ping)
  139. {
  140. ((HttpSocketClient)client).PongWS();
  141. return;
  142. }
  143. if (m_pluginsManager.Raise<IWebSocketPlugin>(nameof(IWebSocketPlugin.OnHandleWSDataFrame), client, e))
  144. {
  145. return;
  146. }
  147. HandleWSDataFrameCallback?.Invoke(client, e);
  148. }
  149. /// <summary>
  150. /// <inheritdoc/>
  151. /// </summary>
  152. /// <param name="client"></param>
  153. /// <param name="e"></param>
  154. protected override void OnReceivedData(ITcpClientBase client, ReceivedDataEventArgs e)
  155. {
  156. if (client.Protocol == Protocol.WebSocket)
  157. {
  158. if (e.RequestInfo is WSDataFrame dataFrame)
  159. {
  160. e.Handled = true;
  161. OnHandleWSDataFrame(client, new WSDataFrameEventArgs(dataFrame));
  162. }
  163. }
  164. base.OnReceivedData(client, e);
  165. }
  166. }
  167. }