WSTools.cs 7.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210
  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.Text;
  15. using TouchSocket.Core;
  16. namespace TouchSocket.Http.WebSockets
  17. {
  18. /// <summary>
  19. /// WSTools
  20. /// </summary>
  21. public static class WSTools
  22. {
  23. /// <summary>
  24. /// 应答。
  25. /// </summary>
  26. public const string acceptMask = "258EAFA5-E914-47DA-95CA-C5AB0DC85B11";
  27. /// <summary>
  28. /// 构建数据
  29. /// </summary>
  30. /// <param name="byteBlock"></param>
  31. /// <param name="dataFrame"></param>
  32. /// <param name="buffer"></param>
  33. /// <param name="offset"></param>
  34. /// <param name="length"></param>
  35. /// <returns></returns>
  36. public static bool Build(ByteBlock byteBlock, WSDataFrame dataFrame, byte[] buffer, int offset, int length)
  37. {
  38. int payloadLength;
  39. byte[] extLen;
  40. if (length < 126)
  41. {
  42. payloadLength = length;
  43. extLen = new byte[0];
  44. }
  45. else if (length < 65536)
  46. {
  47. payloadLength = 126;
  48. extLen = TouchSocketBitConverter.BigEndian.GetBytes((ushort)length);
  49. }
  50. else
  51. {
  52. payloadLength = 127;
  53. extLen = TouchSocketBitConverter.BigEndian.GetBytes((ulong)length);
  54. }
  55. int header = dataFrame.FIN ? 1 : 0;
  56. header = (header << 1) + (dataFrame.RSV1 ? 1 : 0);
  57. header = (header << 1) + (dataFrame.RSV2 ? 1 : 0);
  58. header = (header << 1) + (dataFrame.RSV3 ? 1 : 0);
  59. header = (header << 4) + (ushort)dataFrame.Opcode;
  60. if (dataFrame.Mask)
  61. {
  62. header = (header << 1) + 1;
  63. }
  64. else
  65. {
  66. header = (header << 1) + 0;
  67. }
  68. header = (header << 7) + payloadLength;
  69. byteBlock.Write(TouchSocketBitConverter.BigEndian.GetBytes((ushort)header));
  70. if (payloadLength > 125)
  71. {
  72. byteBlock.Write(extLen, 0, extLen.Length);
  73. }
  74. if (dataFrame.Mask)
  75. {
  76. byteBlock.Write(dataFrame.MaskingKey, 0, 4);
  77. }
  78. if (payloadLength > 0)
  79. {
  80. if (dataFrame.Mask)
  81. {
  82. if (byteBlock.Capacity < byteBlock.Pos + length)
  83. {
  84. byteBlock.SetCapacity(byteBlock.Pos + length, true);
  85. }
  86. WSTools.DoMask(byteBlock.Buffer, byteBlock.Pos, buffer, offset, length, dataFrame.MaskingKey);
  87. byteBlock.SetLength(byteBlock.Pos + length);
  88. }
  89. else
  90. {
  91. byteBlock.Write(buffer, offset, length);
  92. }
  93. }
  94. return true;
  95. }
  96. /// <summary>
  97. /// 计算Base64值
  98. /// </summary>
  99. /// <param name="str"></param>
  100. /// <returns></returns>
  101. public static string CalculateBase64Key(string str)
  102. {
  103. return (str + acceptMask).ToSha1(Encoding.UTF8).ToBase64();
  104. }
  105. /// <summary>
  106. /// 获取Base64随即字符串。
  107. /// </summary>
  108. /// <returns></returns>
  109. public static string CreateBase64Key()
  110. {
  111. var src = new byte[16];
  112. new Random().NextBytes(src);
  113. return Convert.ToBase64String(src);
  114. }
  115. /// <summary>
  116. /// 掩码运算
  117. /// </summary>
  118. /// <param name="storeBuf"></param>
  119. /// <param name="sOffset"></param>
  120. /// <param name="buffer"></param>
  121. /// <param name="offset"></param>
  122. /// <param name="length"></param>
  123. /// <param name="masks"></param>
  124. public static void DoMask(byte[] storeBuf, int sOffset, byte[] buffer, int offset, int length, byte[] masks)
  125. {
  126. for (var i = 0; i < length; i++)
  127. {
  128. storeBuf[sOffset + i] = (byte)(buffer[offset + i] ^ masks[i % 4]);
  129. }
  130. }
  131. /// <summary>
  132. /// 获取WS的请求头
  133. /// </summary>
  134. /// <param name="host"></param>
  135. /// <param name="url"></param>
  136. /// <param name="version"></param>
  137. /// <param name="base64Key"></param>
  138. /// <returns></returns>
  139. public static HttpRequest GetWSRequest(string host, string url, string version, out string base64Key)
  140. {
  141. HttpRequest request = new HttpRequest
  142. {
  143. Method = "GET",
  144. Protocols = "HTTP",
  145. ProtocolVersion = "1.1"
  146. };
  147. request.SetUrl(url);
  148. request.SetHeader(HttpHeaders.Host, host);
  149. request.SetHeader(HttpHeaders.Pragma, "no-cache");
  150. request.SetHeader(HttpHeaders.UserAgent, "TouchSocket.Http.WebSockets");
  151. request.SetHeader(HttpHeaders.Origin, "RRQM");
  152. request.SetHeader(HttpHeaders.AcceptEncoding, "deflate, br");
  153. request.SetHeaderByKey("Connection", "upgrade");
  154. request.SetHeaderByKey("Upgrade", "websocket");
  155. request.SetHeaderByKey("Sec-WebSocket-Version", $"{version}");
  156. base64Key = CreateBase64Key();
  157. request.SetHeaderByKey("Sec-WebSocket-Key", base64Key);
  158. return request;
  159. }
  160. /// <summary>
  161. /// 获取响应
  162. /// </summary>
  163. /// <param name="request"></param>
  164. /// <param name="response"></param>
  165. /// <returns></returns>
  166. public static bool TryGetResponse(HttpRequest request, HttpResponse response)
  167. {
  168. string upgrade = request.GetHeader(HttpHeaders.Upgrade);
  169. if (string.IsNullOrEmpty(upgrade))
  170. {
  171. return false;
  172. }
  173. string connection = request.GetHeader(HttpHeaders.Connection);
  174. if (string.IsNullOrEmpty(connection))
  175. {
  176. return false;
  177. }
  178. string secWebSocketKey = request.GetHeader("sec-websocket-key");
  179. if (string.IsNullOrEmpty(secWebSocketKey))
  180. {
  181. return false;
  182. }
  183. response.StatusCode = "101";
  184. response.StatusMessage = "switching protocols";
  185. response.SetHeader(HttpHeaders.Connection, "upgrade");
  186. response.SetHeader(HttpHeaders.Upgrade, "websocket");
  187. response.SetHeader("sec-websocket-accept", CalculateBase64Key(secWebSocketKey));
  188. return true;
  189. }
  190. }
  191. }