WebSocket简单使用(三) - 服务器端

本文涉及的产品
云解析 DNS,旗舰版 1个月
全局流量管理 GTM,标准版 1个月
公共DNS(含HTTPDNS解析),每月1000万次HTTP解析
简介: 原文 http://www.oseye.net/user/kevin/blog/80 关于WebSocket我打算分如下几篇博文来探探路: WebSocket简单使用(一) - 概念 WebSocket简单使用(二) - 客户端 WebSocket简单使用(三) - 服务器端 WebSocket简单使用(四) - 完整实例 WebSocket简单使用(五) - 模拟微信 连接键盘 功能 背景: 前几天老大突然发现微信有个链接键盘的功能,我仔细想了下或许是使用WebSocket来实现的,又加上最近HTML5的呼声愈来愈高,觉得有必要研究下HTML5了。

原文 http://www.oseye.net/user/kevin/blog/80

关于WebSocket我打算分如下几篇博文来探探路:

  1. WebSocket简单使用(一) - 概念
  2. WebSocket简单使用(二) - 客户端
  3. WebSocket简单使用(三) - 服务器端
  4. WebSocket简单使用(四) - 完整实例
  5. WebSocket简单使用(五) - 模拟微信 连接键盘 功能

背景:

前几天老大突然发现微信有个链接键盘的功能,我仔细想了下或许是使用WebSocket来实现的,又加上最近HTML5的呼声愈来愈高,觉得有必要研究下HTML5了。

握手协议的客户端数据已经由浏览器代劳了,服务器端需要我们自己来实现,目前市场上开源的实现也比较多如:

  • Kaazing WebSocket Gateway(一个 Java 实现的 WebSocket Server);
  • mod_pywebsocket(一个 Python 实现的 WebSocket Server);
  • Netty(一个 Java 实现的网络框架其中包括了对 WebSocket 的支持);
  • node.js(一个 Server 端的 JavaScript 框架提供了对 WebSocket 的支持);
  • WebSocket4Net(一个.net的服务器端实现);

其实在目前的.net4.5框架中已经实现了WebSocket,不用官方实现,我们自己来写个简单的。服务器端需要根据协议来握手、接收和发送。


握手

首先我们再来回顾下握手协议:

  1. 客户端发到服务器的内容:
  2. GET /chat HTTP/1.1
  3. Host: server.example.com
  4. Upgrade: websocket
  5. Connection:Upgrade
  6. Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==
  7. Origin: http://example.com
  8. Sec-WebSocket-Protocol: chat, superchat
  9. Sec-WebSocket-Version:13
  10.  
  11. 从服务器到客户端的内容:
  12. HTTP/1.1101SwitchingProtocols
  13. Upgrade: websocket
  14. Connection:Upgrade
  15. Sec-WebSocket-Accept: s3pPLMBiTxaQ9kYGzzhZRbK+xOo=
  16. Sec-WebSocket-Protocol: chat

关键是服务器端Sec-WebSocket-Accept,它是根据Sec-WebSocket-Key计算出来的:

  1. 取出Sec-WebSocket-Key,与一个magic string “258EAFA5-E914-47DA-95CA-C5AB0DC85B11” 连接成一个新的key串;
  2. 将新的key串SHA1编码,生成一个由多组两位16进制数构成的加密串;
  3. 把加密串进行base64编码生成最终的key,这个key就是Sec-WebSocket-Key;

实例代码如下:

  1. /// <summary>
  2. /// 生成Sec-WebSocket-Accept
  3. /// </summary>
  4. /// <param name="handShakeText">客户端握手信息</param>
  5. /// <returns>Sec-WebSocket-Accept</returns>
  6. privatestaticstringGetSecKeyAccetp(byte[] handShakeBytes,int bytesLength)
  7. {
  8. string handShakeText =Encoding.UTF8.GetString(handShakeBytes,0, bytesLength);
  9. string key =string.Empty;
  10. Regex r =newRegex(@"Sec\-WebSocket\-Key:(.*?)\r\n");
  11. Match m = r.Match(handShakeText);
  12. if(m.Groups.Count!=0)
  13. {
  14. key =Regex.Replace(m.Value,@"Sec\-WebSocket\-Key:(.*?)\r\n","$1").Trim();
  15. }
  16. byte[] encryptionString = SHA1.Create().ComputeHash(Encoding.ASCII.GetBytes(key +"258EAFA5-E914-47DA-95CA-C5AB0DC85B11"));
  17. returnConvert.ToBase64String(encryptionString);
  18. }

如果握手成功,将会触发客户端的onopen事件。


解析接收的客户端信息

接收到客户端数据解析规则如下:

  • 1byte
    1bit: frame-fin,x0表示该message后续还有frame;x1表示是message的最后一个frame
    3bit: 分别是frame-rsv1、frame-rsv2和frame-rsv3,通常都是x0
    4bit: frame-opcode,x0表示是延续frame;x1表示文本frame;x2表示二进制frame;x3-7保留给非控制frame;x8表示关 闭连接;x9表示ping;xA表示pong;xB-F保留给控制frame
  • 2byte
    1bit: Mask,1表示该frame包含掩码;0,表示无掩码
    7bit、7bit+2byte、7bit+8byte: 7bit取整数值,若在0-125之间,则是负载数据长度;若是126表示,后两个byte取无符号16位整数值,是负载长度;127表示后8个 byte,取64位无符号整数值,是负载长度
  • 3-6byte: 这里假定负载长度在0-125之间,并且Mask为1,则这4个byte是掩码
  • 7-end byte: 长度是上面取出的负载长度,包括扩展数据和应用数据两部分,通常没有扩展数据;若Mask为1,则此数据需要解码,解码规则为1-4byte掩码循环和数据byte做异或操作。

解析代码如下,但没有处理多帧和不包含掩码的包:

  1. /// <summary>
  2. /// 解析客户端数据包
  3. /// </summary>
  4. /// <param name="recBytes">服务器接收的数据包</param>
  5. /// <param name="recByteLength">有效数据长度</param>
  6. /// <returns></returns>
  7. privatestaticstringAnalyticData(byte[] recBytes,int recByteLength)
  8. {
  9. if(recByteLength <2){returnstring.Empty;}
  10.  
  11. bool fin =(recBytes[0]&0x80)==0x80;// 1bit,1表示最后一帧
  12. if(!fin){
  13. returnstring.Empty;// 超过一帧暂不处理
  14. }
  15.  
  16. bool mask_flag =(recBytes[1]&0x80)==0x80;// 是否包含掩码
  17. if(!mask_flag){
  18. returnstring.Empty;// 不包含掩码的暂不处理
  19. }
  20.  
  21. int payload_len = recBytes[1]&0x7F;// 数据长度
  22.  
  23. byte[] masks =newbyte[4];
  24. byte[] payload_data;
  25.  
  26. if(payload_len ==126){
  27. Array.Copy(recBytes,4, masks,0,4);
  28. payload_len =(UInt16)(recBytes[2]<<8| recBytes[3]);
  29. payload_data =newbyte[payload_len];
  30. Array.Copy(recBytes,8, payload_data,0, payload_len);
  31.  
  32. }elseif(payload_len ==127){
  33. Array.Copy(recBytes,10, masks,0,4);
  34. byte[] uInt64Bytes =newbyte[8];
  35. for(int i =0; i <8; i++){
  36. uInt64Bytes[i]= recBytes[9- i];
  37. }
  38. UInt64 len =BitConverter.ToUInt64(uInt64Bytes,0);
  39.  
  40. payload_data =newbyte[len];
  41. for(UInt64 i =0; i < len; i++){
  42. payload_data[i]= recBytes[i +14];
  43. }
  44. }else{
  45. Array.Copy(recBytes,2, masks,0,4);
  46. payload_data =newbyte[payload_len];
  47. Array.Copy(recBytes,6, payload_data,0, payload_len);
  48.  
  49. }
  50.  
  51. for(var i =0; i < payload_len; i++){
  52. payload_data[i]=(byte)(payload_data[i]^ masks[i %4]);
  53. }
  54. returnEncoding.UTF8.GetString(payload_data);
  55. }


发送数据至客户端

 

服务器发送的数据以0x81开头,紧接发送内容的长度(若长度在0-125,则1个byte表示长度;若长度不超过0xFFFF,则后2个byte 作为无符号16位整数表示长度;若超过0xFFFF,则后8个byte作为无符号64位整数表示长度),最后是内容的byte数组。

代码如下:

  1. /// <summary>
  2. /// 打包服务器数据
  3. /// </summary>
  4. /// <param name="message">数据</param>
  5. /// <returns>数据包</returns>
  6. privatestaticbyte[]PackData(string message)
  7. {
  8. byte[] contentBytes =null;
  9. byte[] temp =Encoding.UTF8.GetBytes(message);
  10.  
  11. if(temp.Length<126){
  12. contentBytes =newbyte[temp.Length+2];
  13. contentBytes[0]=0x81;
  14. contentBytes[1]=(byte)temp.Length;
  15. Array.Copy(temp,0, contentBytes,2, temp.Length);
  16. }elseif(temp.Length<0xFFFF){
  17. contentBytes =newbyte[temp.Length+4];
  18. contentBytes[0]=0x81;
  19. contentBytes[1]=126;
  20. contentBytes[2]=(byte)(temp.Length&0xFF);
  21. contentBytes[3]=(byte)(temp.Length>>8&0xFF);
  22. Array.Copy(temp,0, contentBytes,4, temp.Length);
  23. }else{
  24. // 暂不处理超长内容
  25. }
  26.  
  27. return contentBytes;
  28. }

这里只是简单介绍,下节来做个完整的实例。

目录
相关文章
|
7月前
|
JSON 数据格式
【虚幻引擎】DTWebSocketServer 蓝图创建WebSocket服务器插件使用说明
该插件用于创建和管理WebSocket服务器,支持通过蓝图创建服务器、监听端口并接收响应。主要功能包括:创建服务器、绑定各种回调函数(用户连接、断开、发送消息时触发)、关闭服务器、获取所有用户、获取用户数据、向用户发送消息及主动关闭用户连接。示例和下载链接在原文中提供。
95 2
|
7月前
|
移动开发 缓存 网络协议
Websocket协议原理及Ws服务器代码实现
Websocket协议原理及Ws服务器代码实现
|
7月前
|
网络协议 C++ 数据格式
websocket协议介绍与基于reactor模型的websocket服务器实现
websocket协议介绍与基于reactor模型的websocket服务器实现
127 0
|
7月前
|
网络协议 JavaScript 前端开发
WebSocket:实现客户端与服务器实时通信的技术
WebSocket:实现客户端与服务器实时通信的技术
|
4月前
|
前端开发 网络协议 物联网
Django Web:搭建Websocket服务器(入门篇)
Django Web:搭建Websocket服务器(入门篇)
102 1
|
7月前
|
JSON JavaScript 前端开发
服务器通信:使用WebSocket与后端实时交互
【4月更文挑战第24天】WebSocket为解决服务器与客户端实时双向通信问题而生,常用于聊天、游戏和实时通知等场景。本文通过4步教你实现WebSocket通信:1) 客户端使用JavaScript创建WebSocket连接;2) 监听`open`、`message`和`close`事件;3) 使用`send`方法发送数据;4) 使用`close`方法关闭连接。服务器端则需处理连接和数据发送,具体实现依后端技术而定。WebSocket为现代Web应用的实时交互提供了强大支持。
555 3
|
7月前
|
前端开发 网络协议 JavaScript
|
7月前
|
网络协议 Linux C++
Linux C/C++ websocket协议与服务器实现
Linux C/C++ websocket协议与服务器实现
349 0
|
开发框架 JavaScript 前端开发
如何使用SpringBoot和Netty实现一个WebSocket服务器,并配合Vue前端实现聊天功能?
如何使用SpringBoot和Netty实现一个WebSocket服务器,并配合Vue前端实现聊天功能?
322 0
|
Java Shell Windows
Jmeter测试 Websocket服务器
使用Jmeter来对websocket服务进行测试
223 0