目录
什么是websocket:
WebSocket 是一种网络传输协议,可在单个 TCP 连接上进行全双工通信,位于 OSI 模型的应用层。WebSocket 协议在 2011 年由 IETF 标准化为 RFC 6455,后由 RFC 7936 补充规范。
WebSocket 使得客户端和服务器之间的数据交换变得更加简单,允许服务端主动向客户端推送数据。在 WebSocket API 中,浏览器和服务器只需要完成一次握手,两者之间就可以创建持久性的连接,并进行双向数据传输。
为什么需要websocket:
HTTP是一种能够获取如 HTML 这样的网络资源的 protocol(通讯协议)。它是在 Web 上进行数据交换的基础,是一种 client-server 协议,也就是说,http协议只能完成客户端主动向服务器请求,服务器响应回复的通讯模式。
这种传统的模式带来很明显的缺点,即浏览器需要不断的向服务器发出请求,然而 HTTP 请求与响应可能会包含较长的头部,其中真正有效的数据可能只是很小的一部分,所以这样会消耗很多带宽资源。另外最重要的就是如果服务器需要主动向客户端推送数据,使用HTPP协议就难以实现了
c/c++中支持websocket的库:
1.uWebSockets (重点项目:仅依赖libuv、不依赖boost asio , github 15.5k)
2.libwebsockets (纯C实现,github 3.9k)
3.Poco Websocket (github 7.1k)
4.Crow (依赖boost asio,github 7.2k)
5.websocketpp (依赖boost asio, github 6.2k)
6.Beast (依赖boost asio ,github 3.8k)
libwebsocket的安装(centos7):
首先下载openssl和openssl-devel
yum install -y openssl
yum install -y openssl-devel
然后直接yum下载即可
yum install -y libwebsocket
libwebsocket基本使用:
初始化结构体:
libwebsocket的回调设置较为特殊,是使用结构体来初始化确定回调函数的!
struct TSessionData
{
long lContext;
u8byIsHttp; // 是否是Http
};
struct libwebsocket_protocols protocols[] = {
{
"http", /* 协议名:其与Sec-Websockets-Protocol字段对应 */
ProtobufCB, /* 回调函数:协议对应的回调处理函数 */
DATA_BUFSIZE + LWS_PRE, /* 自定义数据空间大小:每个ws连接均会分配一个自定义数据空间 */
DATA_BUFSIZE, /* 首发缓存 */
NULL, /*固定格式*/
&Tsession /*上下文,自定义结构体*/
},
{
"ws", /* 协议名:其与Sec-Websockets-Protocol字段对应 */
ProtobufCB, /* 回调函数:协议对应的回调处理函数 */
DATA_BUFSIZE + LWS_PRE, /* 自定义数据空间大小:每个ws连接均会分配一个自定义数据空间 */
DATA_BUFSIZE, /* 首发缓存 */
NULL, /*固定格式*/
&Tsession /*上下文,自定义结构体*/
},
{NULL, NULL, 0, 0} /* 结束标识 */
};
libwebsocket的回调函数原型:
ws_service_callback(
strcut lws *wsi,
enum lws_callback_reasons reason,
void *user,
void *in,
size_t len
)
参数 | 数据类型 | 含义 |
wsi | struct lws* | 全局上下文,负责ws连接的维护和管理 |
reason | enum lws_callback_reasons | 调用回调回调函数的事件 |
user | void* | 上下文,用户自定义结构 |
in | void* | 输入数据 |
len | size_t | 输入数据长度 |
libwebsocket的reason枚举:
LWS_CALLBACK_WSI_CREATE:
正在创建ws连接对象注:此时回调函数中的参数wsi对象与user对象依然为空指针,因此还不能初始化用户自定义对象回调函数的参数含义:
context: 全局上下文 wsi: 空指针 user: 空指针 in: 空指针 len: 0
LWS_CALLBACK_FILTER_PROTOCOL_CONNECTION:
此reason可以用来过滤协议注:在此处返回非0值时,lws库将会关闭该链接;该处返回0时,表示ws连接已经建立成功。此时表1中的wsi对象和user对象已不为空,因此,此时可以对用户自定义对象user进行初始化处理。
LWS_CALLBACK_LOCK_POLL:
添加保护ws连接状态的互斥锁
注:当采用的是多线程编程,则在此添加互斥锁保护ws连接相关状态,防止冲突。如果是单进程方式,则无需做任何操作。
LWS_CALLBACK_UNLOCK_POLL:
解除保护ws连接状态的互斥锁
注:当采用的是多线程编程,则在此解除互斥锁。如果是单进程方式,则无需做任何操作。
LWS_CALLBACK_CLIENT_RECEIVE:
数据出现在客户端连接的服务器上时,可以在通过*in处找到,长度为len字节。
LWS_CALLBACK_RECEIVE:
收到一帧完整数据注:此时表 in表示收到的数据,len表示收到的数据长度。需要注意的是:指针in的回收、释放始终由LWS框架管理,只要出了回调函数,该空间就会被LWS框架回收。因此,开发者若想将接收的数据进行转发,则必须对该数据进行拷贝。
LWS_CALLBACK_SERVER_WRITEABLE:
此ws连接为可写状态注:表示wsi对应的ws连接当前处于可写状态,即:可发送数据至客户端。
LWS_CALLBACK_CLOSED:
ws连接已经断开
注:不能在此释放内存空间,否则存在内存泄漏的风险!!!因为连接断开时,并不总是会回调LWS_CALLBACK_CLOSED的处理!
LWS_CALLBACK_WSI_DESTROY:
正在销毁ws连接对象
注:表示libwebsockets框架即将销毁wsi对象。此时如果用户自定义对象中存在动态分配的空间,则需要在此时进行释放。
LWS_CALLBACK_HTTP:
收到http消息头
LWS_CALLBACK_HTTP_BODY:
收到http消息体
libwebsocket的几个常用函数:
lws_get_peer_write_allowance
该ws连接允许发送的字节数
备注:如果有发送字节限制,则返回正数;如果无发送字节限制,则返回-1
lws_send_pipe_choked
判断ws连接是否阻塞
备注:如果ws连接阻塞,则返回1,否则返回0
lws_write
将数据发送给对端
备注:函数参数说明
wsi: ws连接对象
buf: 需要发送数据的起始地址。
注意:必须在指针buf前预留长度为LWS_SEND_BUFFER_PRE_PADDING的空间,同时在指针buf+len后预留长度为LWS_SEND_BUFFER_POST_PADDING的空间。
len: 需要发送数据的长度
protocol: 如果该连接是http连接,则该参数的值为LWS_WRITE_HTTP;如果该连接是ws连接,则该参数的值为LWS_WRITE_BINARY,但如果第一次发送的数据长度n < len,则发送后续长度为(len - n)字节的数据时,该参数值改为LWS_WRITE_HTTP
lws_http_transaction_completed
当前连接为http连接,而非ws连接时,如果当前http请求的应答数据发送完毕,则可使用该函数重置http连接的相关状态,只有收到新的http请求才能激活该http连接。
备注:如果当前连接为ws连接,则千万不要调用此函数,其将导致服务端则无法再激活可写事件
lws_callback_on_writable
将ws连接加入可写事件监听
lws_callback_on_writable_all_protocol
将某个协议的所有ws连接加入可写事件监听
注:在网络中存在各种情况可能导致服务端并不知道与客户端的连接已经断开:比如客户端掉电。为了应对这种情况的存在。需要每隔一段事件执行该函数,再在协议的回调函数的LWS_CALLBACK_SERVER_WRITEABLE事件中判断一下是否超时,如果超时则返回非零值