window环境使用C++实现WebSocket
最近因为项目需求,需要使用C++实现WebSocket与浏览器进行通讯,针对WebSocket有很多开源的框架,但是本人比较懒,所以干脆手写一个算了.
https://download.csdn.net/download/weixin_34196559/10750141开源库
废话不说,直接上代码
1. 整体代码截图
全部代码,基本400行以内可以搞定
项目结构,因为实现WebSocket需要涉及到加密解密,所以从网上某大神哪里拿来了代码
以上是整体的代码结构, 下面我简单介绍下WebSocket的流程
先贴一张图 WebSocket的基本结构 想详细了解协议内容,看这位大神的博客 https://www.cnblogs.com/songwenjie/p/8575579.html
第一步 客户端首先发送握手请求(看下图, 他长这样), 在这里面最重要的就是那个Sec-WebSocket-Key 数值, 这玩意每次来都是随机,你需要对他按照固定方式加密然后返回给客户端, 多说一句,这请求头的意思打算把协议升级成WebSocket协议, 你要是同意就按照规矩加密数据传回来.
第二步 服务端返回握手响应给客户端(看下图, 他长这样)Sec-WebSocket-Accept: 就是加密后的Sec-WebSocket-Key. 这里多费几句话 WebSocket是以"\r\n\r\n"结尾的 这里一定要多加注意, 因为之前有些兄嘚后面加了一个莫名其妙的\0 导致链接会自己断开
同时报异常: failed: A server must not mask any frames that it sends to the client.
第三步 上面一顿操作猛如虎, 握手动作就完成了, 接下来就是普通的Socket通讯了, 唯一不同的就是来的数据用掩码处理过, 去的时候不能用掩码处理 不能用掩码处理 不能用掩码处理 重要的事情说三遍(看下图), 因为只要违反规则链接就会立马断开并且报异常 failed: A server must not mask any frames that it sends to the client.
客户端发往服务端的数据 其中Mask 为true 表示数据经过掩码处理
服务端发往客户端的数据 其中Mask为false 表示没有经过掩码处理
如上便是WebSocket的通讯基本流程了
2. 代码分析
main 函数,程序入口
Initsocket 函数 主要作用是创建Socket, 开启监听, 等待连接, 拿到连接后开启子线程处理具体的管道通讯
WorkThread 函数 线程函数相当于java内的Runnable的实现类,主要作用有两个,
1.进行客户端服务端握手
2.管道通讯
下面按照WorkThread 函数流程向下解释
2.1握手动作
第一个函数requestInfo
他的主要作用是获取客户端发过来的握手请求,然后解析Sec-WebSocket-Key字段进行解析,然后生成握手响应数据
下面我们来看一下requestInfo函数细节
上图是requestInfo函数代码
首先通过recv 函数获取到客户端发送过来的数据
然后去请求头中查找Sec-WebSocket-Key字段(看下图)
获取成功后调用getKey 函数 来生成握手响应数据(看下图)
图中那个258开头的字符串是固定的 将客户端发送过来的Sec-WebSocket-Key字段与他拼接,然后进行加密, 这样响应给客户端,这样就可以完成整个握手动作,这也是握手动作的关键所在,.
到此我们的响应数据已经封装完毕了, 只要将他返回给客户端就可以完成握手行为了(看图)
2.2 数据通信
其实WebSocket在本质上就是Socket, 只不过要先通过握手动作来验证身份(协议升级, http升级到SwebSocket协议)
剩余的通讯其实跟socket就一毛一样了,唯一的不通点是客户端给服务端发送的数据是经过加密的,所以拿到数据后需要解密才可以看到具体的数据, 但是服务端向客户端发送的数据却是不能加密的(看下图, 典型的socket通讯)
首先, 我们来看接受数据代码, 在图中就是简单的recv函数, 这样我们就拿到了数据, 但是刚才我们讲到过数据经过加密, 所以需要使用 getClientInfo 函数来解密数据(关于getClientInfo函数是如何解密的,我不懂,代码是别的大神写的,很好用), 这里只展示下片段,具体代码,可以看demo
经过上面一顿操作我们就可以看到客户端发送的数据了
下面就是我们需要给客户端发送数据了, 虽然WebSocket协议规定了不需要加密数据, 但是还是要有点其他的操作的(看responseInfo函数–> respondClient 函数)
调用respondClient函数, 继续看
respondClient函数实际上是封装响应数据的主要函数, 根据WebSocket协议, 发送给客户端的数据其实分三种, 不同的数据长度是不一样的, 这也就是为什么要声明realDateLength变量了
当数据长度小于126的时候,头部多了两个长度, 当 小于65536的时候头部多了 4个长度, 其他的数据(大于65536)头部多了10个长度(具体这玩意叫啥可以去看WebSocket协议, 我记不清了)
这样我们就可以把数据响应到客户端了
以上我们就完成了WebSocket的所有代码了
资料链接信息在我另外一篇博客