Websocket是一种可双向通讯的网络协议,其底层的数据收发是基于socket的,所以使用c语言来实现理论上是没有问题的,主要难点在于协议中要求对个别数据进行加密处理,这些加密方法(库)在java、c#等专门开发web的平台中都自带API,而在用c语言开发时则苦于去实现这些加密方法、打包格式,使得用c来实现Websocket变得繁琐而吐血!所以非要用c语言来实现Websocket的话,要做好刀耕火种的准备。。。

前面已经翻阅过很多博文,不管是协议还是c的代码都可以找到很多,本文也是参考了这些前辈的资料而得,但苦在搜罗到的代码都是片段或不够工整的,所以本文重点在尝试整合c实现Websocket的代码,同时也尽量清晰易懂的讲解其数据格式。

一、建立连接

一切的开始,先上一张网络数据抓包图(这里用的Wireshark软件,还不知道抓包的童鞋可自行百度先玩玩)

GET /null HTTP/1.1
Connection: Upgrade
Host: 172.16.104.78:9999
Sec-WebSocket-Key: J2BJc+GQuSw34hi2TjyVpg==
Sec-WebSocket-Version: 13
Upgrade: websocket

HTTP/1.1 101 Switching Protocols
Upgrade: websocket
Server: Microsoft-HTTPAPI/2.0
Connection: Upgrade
Sec-WebSocket-Accept: uGY1yScptmHNgZqrDnpq1Ws1xho=
Date: Sat, 15 Jul 2017 15:35:16 +08

..Hello !...lx!.	.M.LY..I am Server_Test...lx!.L.L./.H...~.	.U..You are carefree ......lx!.L.L...S.

D.LY..You are carefree ....!\O^O/  <-.<-  TAT  =.=#  -.-! .....h.R"K...(..f<.w|N.y}A.z.6.sj ...You are carefree ....!\O^O/  <-.<-  TAT  =.=#  -.-! .....n../M.B...\k:.9qH.7pG.4.0.=g&...You are carefree ....!\O^O/  <-.<-  TAT  =.=#  -.-! ........e.>.F.[.!.\.;.=.:.0.O.>.-.R..You are carefree ...

这里写图片描述

这是一张websocket通讯下,服务器和客户端交互时的数据抓包,图中红色、蓝色分别是客户端、服务器发出的数据。

websocket实现数据通讯的步骤:
1.client向server发送http请求,数据内容如同图中第一大段红色字符串,其中携带了3个参数。
①要调用server的接口的路径字符串(不明白先不管)
②服务器的IP和端口
③握手Key
大家可以把这当作一个模版放到代码里。

const char httpDemo[] = "GET %s HTTP/1.1\r\n"
                        "Connection: Upgrade\r\n"
                        "Host: %s:%d\r\n"
                        "Sec-WebSocket-Key: %s\r\n"
                        "Sec-WebSocket-Version: 13\r\n"
                        "Upgrade: websocket\r\n\r\n";

2.server收到http请求后,检查3个参数内容OK,然后返回携带参数的特定内容
④回应的握手Key
⑤时间字符串(格式: Date: 星期, 日 月 年 时:分:秒 时区)
同样大家可以作为模版使用

const char httpDemo[] = "HTTP/1.1 101 Switching Protocols\r\n"
                        "Upgrade: websocket\r\n"
                        "Server: Microsoft-HTTPAPI/2.0\r\n"
                        "Connection: Upgrade\r\n"
                        "Sec-WebSocket-Accept: %s\r\n"
                        "%s\r\n\r\n";  //时间打包 // 格式如 "Date: Tue, 20 Jun 2017 08:50:41 CST\r\n"

3.client收到返回后,检查返回状态(主要看"101")以及拿自己发出的握手Key (J2BJc+GQuSw34hi2TjyVpg==) 和收到server返回的Key (uGY1yScptmHNgZqrDnpq1Ws1xho=) 按照一套加密方式(这里用的sha1哈希加密)进行匹配OK,之后client和server就可以互发数据了

二、数据传输

  • 1、数据包格式:

通过上面抓包的截图还看到,建立连接之后互发的数据是比较奇怪的,很多字符并不是可见的ASCII码,这是因为Websocket协议里对数据的传输做了一些规定,简单说就是有一定的打包格式,其权威的解释请看这里draft-ietf-hybi-thewebsocketprotocol-13 - The WebSocket Protocol,不权威的解释请继续往下看

把上面的抓包数据按Hex16显示(这里只看后面互发数据的内容)

    000000C5  81 07 48 65 6c 6c 6f 20  21                        ..Hello  !
000000A1  81 87 c3 6c 78 21 8b 09  14 4d ac 4c 59            ...lx!.. .M.LY
    000000CE  81 10 49 20 61 6d 20 53  65 72 76 65 72 5f 54 65   ..I am S erver_Te
    000000DE  73 74                                              st
000000AE  81 90 c3 6c 78 21 8a 4c  19 4c e3 2f 14 48 a6 02   ...lx!.L .L./.H..
000000BE  0c 7e 97 09 0b 55                                  .~...U
    000000E0  81 14 59 6f 75 20 61 72  65 20 63 61 72 65 66 72   ..You ar e carefr
    000000F0  65 65 20 2e 2e 2e                                  ee ...
000000C4  81 8f c3 6c 78 21 8a 4c  19 4c e3 0f 19 53 a6 0a   ...lx!.L .L...S..
000000D4  0a 44 a6 4c 59                                     .D.LY
    000000F6  81 14 59 6f 75 20 61 72  65 20 63 61 72 65 66 72   ..You ar e carefr
    00000106  65 65 20 2e 2e 2e                                  ee ...
    0000010C  81 21 5c 4f 5e 4f 2f 20  20 3c 2d 2e 3c 2d 20 20   .!\O^O/   <-.<-  
    0000011C  54 41 54 20 20 3d 2e 3d  23 20 20 2d 2e 2d 21 20   TAT  =.= #  -.-! 
    0000012C  2e 2e 2e                                           ...
000000D9  81 9a 68 b6 52 22 4b 93  0c 01 28 f6 12 66 3c f1   ..h.R"K. ..(..f<.
000000E9  77 7c 4e 90 79 7d 41 9d  7a 08 36 93 73 6a 20 ff   w|N.y}A. z.6.sj .
    0000012F  81 14 59 6f 75 20 61 72  65 20 63 61 72 65 66 72   ..You ar e carefr
    0000013F  65 65 20 2e 2e 2e                                  ee ...
    00000145  81 21 5c 4f 5e 4f 2f 20  20 3c 2d 2e 3c 2d 20 20   .!\O^O/   <-.<-  
    00000155  54 41 54 20 20 3d 2e 3d  23 20 20 2d 2e 2d 21 20   TAT  =.= #  -.-! 
    00000165  2e 2e 2e                                           ...

额~还是先放一张官方的图。。。
这里写图片描述
翻译一下
这里写图片描述
这里写图片描述
简单概括一下,每包数据组成按字节顺序定义为:

数据头(1字节) + 是否掩码标志(1个二进制位) + 数据长度(半字节~8字节) + 掩码(4字节或没有) + 数据内容

如果觉得乱,看不明白,那这里用对应的颜色标注再解释
这里写图片描述
可以看到0x81(红色)是数据头:最高位FIN多数时候为1即0x80(表示这是完整1包),最低位用0x1代表该包数据类型为文本类,所以得到数据头0x81。

server打包的数据的第二位0x07(黄色):最高位表示是否使用掩码,这里为0表示不用;后7位表示数据长度,这里为0x07 = 7,也就是“48 65 6c 6c 6f 20 21(蓝色)”这7个数据的长度咯~

client打包的数据的第二位0x87(黄色):最高位这里为0x80表示使用掩码,后7位数据长度为0x07=7;由于有掩码,所以0x87后面紧跟着的是4字节是掩码“c3 6c 78 21(绿色)”,然后才是7个数据“8b 09 14 4d ac 4c 59(蓝色)”。

  • 2、关于数据长度的记录,有三种方式:

第一种: 0<数据长度<126(0x7E),使用半个字节来记录数据长度,也就是上面例子

81 07 48 65 6c 6c 6f 20 21  --  无掩码,7字节数据长度
81 8f c3 6c 78 21 8a 4c 19 4c e3 0f 19 53 a6 0a  0a 44 a6 4c 59  --  有掩码,15字节数据长度

第二种: 126=<数据长度<65536(0x10000),数据包的第二位强制为 0x7E(再并上掩码位),然后紧跟的2个字节表示数据长度,再接着才是掩码和数据

81 fe 00 a3 3a d2 f4 7c 59 b3 96 1a 0a ...   --  有掩码,使用2字节记录长度,163字节数据长度(即0xa3)

第三种: 65536=<数据长度<=0xFFFFFFFFFFFFFFFF(8个字节记录长度, 谁tm一包数据上G),数据包的第二位强制为 0x7F(再并上掩码位),然后紧跟的8个字节表示数据长度,再接着才是掩码和数据

81 ff 00 00 00 00 00 01 00 00 3a d2 f4 7c 59 b3 96 1a 0a ...   --  有掩码,使用8字节记录长度,65536字节数据长度(即0x10000)

注意
协议严格规定数据量不足的必须使用少字节的记录方式,也就是你不能明明只要2字节数据量却用8字节的记录长度方式

  • 3、关于掩码处理:

掩码固定4个字节长度,内容可以用随机数生成,这4个掩码会依次和要发送的数据进行异或处理,最后得到数据区里的数据,例如

81 87 c3 6c 78 21 8b 09 14 4d ac 4c 59

掩码为"c3 6c 78 21",数据区"8b 09 14 4d ac 4c 59",实际数据是"Hello !“即"48 65 6C 6C 6F 20 21”,打包过程为:

0x8b = 0xc3 ^ 0x48 // c语言代码中^符号代表异或运算
0x09 = 0x6c ^ 0x65
0x14 = 0x78 ^ 0x6c
0x4d = 0x21 ^ 0x6c
0xac = 0xc3 ^ 0x6f // 这里循环使用4个掩码
0x4c = 0x6c ^ 0x20
0x59 = 0x78 ^ 0x21

那么解码呢?异或的解码任然是异或
附带一提,掩码处理后的数据会出现大量的非可见ASCII字符,甚至数据0x00,不要随便使用0x00来判断数据结束。

注意
规定client发数据给server时必须使用掩码处理,而server下发数据给client时一般不进行掩码处理

关于为什么client发server需要掩码,可以参考下文中 九、数据掩码的作用
https://zhuanlan.zhihu.com/p/32731137

数据加密
对于发送端,数据处理顺序为:加密、掩码处理(可选)、打包、发出
对于接收端,数据处理顺序为:接收、解包、解掩码(可选)、解密

三、代码

github工程(能否打开看运气) https://github.com/wexiangis/websocket_for_linux
gitee工程 https://gitee.com/wexiangis/websocket_for_linux

Logo

为开发者提供学习成长、分享交流、生态实践、资源工具等服务,帮助开发者快速成长。

更多推荐