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

原文 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. }

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

时间: 2024-09-14 17:46:45

WebSocket简单使用(三) - 服务器端的相关文章

WebSocket简单使用(一) - 概念

原文 http://www.oseye.net/user/kevin/blog/78 关于WebSocket我打算分如下几篇博文来探探路: WebSocket简单使用(一) - 概念 WebSocket简单使用(二) - 客户端 WebSocket简单使用(三) - 服务器端 WebSocket简单使用(四) - 完整实例 WebSocket简单使用(五) - 模拟微信 连接键盘 功能 背景: 前几天老大突然发现微信有个链接键盘的功能,我仔细想了下或许是使用WebSocket来实现的,又加上最近

WebSocket简单使用(二) - 客户端

原文 http://www.oseye.net/user/kevin/blog/79 关于WebSocket我打算分如下几篇博文来探探路: WebSocket简单使用(一) - 概念 WebSocket简单使用(二) - 客户端 WebSocket简单使用(三) - 服务器端 WebSocket简单使用(四) - 完整实例 WebSocket简单使用(五) - 模拟微信 连接键盘 功能 背景: 前几天老大突然发现微信有个链接键盘的功能,我仔细想了下或许是使用WebSocket来实现的,又加上最近

WebSocket简单使用(四) - 完整实例

原文 http://www.oseye.net/user/kevin/blog/81 关于WebSocket我打算分如下几篇博文来探探路: WebSocket简单使用(一) - 概念 WebSocket简单使用(二) - 客户端 WebSocket简单使用(三) - 服务器端 WebSocket简单使用(四) - 完整实例 WebSocket简单使用(五) - 模拟微信 连接键盘 功能 背景: 前几天老大突然发现微信有个链接键盘的功能,我仔细想了下或许是使用WebSocket来实现的,又加上最近

简单粗暴三步photoshop调色让暗片变亮片

  简单粗暴三步photoshop调色让暗片变亮片,技巧实用,思路清晰,素材有提供,点击大图查阅参数更清楚,值得借鉴 分类: PS图片处理

如何通过简单的三步还原动画

  摄影师@苍旻之鹰 说他喜欢有故事的画面和有画面的音乐,而他的摄影作品也确实像动画一样,故事感中夹杂着质感.而那些散发着浓浓动画feel的作品,后期步骤仅需简单的三步这你又能想到吗?今天就来看看如何通过简单的三步还原动画吧 分类: PS图片处理

简单介绍三款常用的建站工具

建站软件有很多,但是网站建设大致分为手工建站和自助建站.简单的说,手工建站就是根据自己的需求去设计和开发网站:自助建站就是基于已经成形的网站系统框架,只要将内容填充到框架中,一个网站就可以基本完成.但不管怎么说他们都需要一定的软件做基础! 下面简单介绍三款常用的建站工具 第一款:Dreamweaver(手工建站工具) Dreamweaver软件和Fireworks,flash合称网页三剑客,他是由美国软件开发商Macromedia公司研发并推出的一款可视化网站开发工具. 后来Macromedia

C++简单QQ程序服务器端的实现代码_C 语言

本文实例为大家分享了C++简单QQ程序服务器端的实现代码,供大家参考,具体内容如下 // ServerDlg.cpp : implementation file // #include "StdAfx.h" #include "Server.h" #include "ServerDlg.h" #ifdef _DEBUG #define new DEBUG_NEW #undef THIS_FILE static char THIS_FILE[] =

websocket++简单使用及实例分析_C 语言

前言 html5支持使用websocket协议与服务器保持一个长连接,方便双方互相传输数据,而且服务器也能主动发送信息给客户端,而在这之前使用HTTP是很难做到的.下面介绍使用C++实现的websocket++的简单使用.websocket++更详细介绍点此. websocket++需要boost的支持,所以工程中需要包含boost的头文件和库.boost在VS中的如何使用参考此文章. C++代码 #include "stdafx.h" #include <iostream>

看电影更简单!三步学会用风行

看在线电影,你还用再打开那慢腾腾的网页,点击无数数不清的链接吗?现在使用风行在线影视软件,这一切变得非常简单,只须几步,就能看到自己想要的热门影视. 第一步:点击电影名称链接 点击今日推荐.电影标题链接 "今日推荐"是风行为保证大部分在线用户都能享受到流畅在线播放效果所推出的特色频道服务,推荐频道影视节目的特点主要为: 1.热门影视剧集或经典大片,分为爱情.动作.戏剧.战争等12个类别上千部影视剧集及综艺节目,每日更新. 2.影片连接数多.种子完整度好. 3.所有影片子码率控制在一定范