C++ libwebsockets搭建WebSocket服务端及Http客户端、服务端

发布时间 2023-10-10 13:54:47作者: 阿风小子
https://blog.csdn.net/fantasysolo/article/details/88908948
 
 
概念
WebRTC
WebRTC,名称源自网页即时通信(英语:Web Real-Time Communication)的缩写,是一个支持网页浏览器进行实时语音对话或视频对话的API。它于2011年6月1日开源并在Google、Mozilla、Opera支持下被纳入万维网联盟的W3C推荐标准。
WebRTC不是html5标准。
WebRTC提供了视频会议的核心技术,包括音视频的采集、编解码、网络传输、显示等功能,并且还支持跨平台:windows,linux,mac,android。
WebSocket是WebRTC的基础,WebSocket为WebRTC负责客服端发现和数据转发。基本上能实现WebSocket,就能实现WebRTC(浏览器支持方面有差异,目前IE10以上浏览器、Opera/Firefox/Chrome支持WebSocket,但各版本IE不支持WebRTC)。
 
浏览器支持情况
桌上PC端
    Google Chrome23
    Mozilla Firefox22
    Opera18
    Safari11(仍处于开发者预览阶段)
Android端
    Google Chrome 28(从版本29开始默认开启)
    Mozilla Firefox 24
    Opera Mobile 12
Google Chrome OS
Firefox OS
iOS 11
Blackberry 10 内置浏览器
    Bowser
 
WebSocket
WebSocket是一种在单个TCP连接上进行全双工通信的协议。允许服务端主动向客户端推送数据。在WebSocket API中,浏览器和服务器只需要完成一次握手,两者之间就直接可以创建持久性的连接,并进行双向数据传输。
WebSocket API也被W3C定为标准。
WebSocket 是独立的、创建在 TCP 上的协议。
Websocket 通过HTTP/1.1 协议的101状态码进行握手。
为了创建Websocket连接,需要通过浏览器发出请求,之后服务器进行回应,这个过程通常称为“握手”(handshaking)。
 
背景
现在,很多网站为了实现推送技术,所用的技术都是轮询。轮询是在特定的的时间间隔(如每1秒),由浏览器对服务器发出HTTP请求,然后由服务器返回最新的数据给客户端的浏览器。这种传统的模式带来很明显的缺点,即浏览器需要不断的向服务器发出请求,然而HTTP请求可能包含较长的头部,其中真正有效的数据可能只是很小的一部分,显然这样会浪费很多的带宽等资源。
而比较新的技术去做轮询的效果是Comet。这种技术虽然可以双向通信,但依然需要反复发出请求。而且在Comet中,普遍采用的长链接,也会消耗服务器资源。
在这种情况下,HTML5定义了WebSocket协议,能更好的节省服务器资源和带宽,并且能够更实时地进行通讯。
 
开源库
uWebSockets、Crow、websocketpp、Beast、libwebsockets 、POCO 库的 WebSocket 模块。
 
uWebSockets
https://github.com/uNetworking/uWebSockets
uWebSockets,µWS (“microWS”) 是一个客户端和服务器的 WebSocket 和 HTTP 实现。它简单、高效且轻量级。
这个库在底层依赖于 libuv 库 ( libuv是一个高性能的,事件驱动的I/O库,并且提供了跨平台的API。),作为异步网络 I/O 库。
 
localhost
部分转自https://blog.csdn.net/liuruiqun/article/details/45645091
在计算机网络中,localhost 代表了本主机,通过使用localhost可以访问自己主机的网络服务。注意,使用localhost的时候,使用的是回环网络接口,这会绕过本地网络接口硬件,独立于任何网络配置(不受网络防火墙和网卡相关的的限制)。
本地回环地址不属于任何一个有类别地址类,它代表设备的本地虚拟接口。发送到发送到环回地址的任何数据包在 TCP/IP 协议栈的链路层被执行,这些数据包不会交付给任何网络接口控制器(NIC)和设备驱动程序,即数据流不会被发送到一个真正的网络接口。wireshark抓包抓不到。
 
libWebSockets
uWebSockets是C++17写的,编译器支持不好,而且相关资料太少,改用libWebSockets。libWebSockets是个纯C库,轻量级,同样支持百万级别并发连接。
 
安装
下载https://github.com/warmcat/libwebsockets
cmake生成vs工程,提示找不到openssl,安装OpenSSL,参考:https://blog.csdn.net/yuiiiii/article/details/81297880
本来准备编译安装,看了下太麻烦,尝试懒人版安装成功,暂时没发现问题,安装版本为Win64 OpenSSL v1.0.2r,下载连接:http://slproweb.com/products/Win32OpenSSL.html
再次cmake生成vs工程,直接cmd
mkdir build
cd build
cmake .. -G "Visual Studio 15" -DOPENSSL_ROOT_DIR="安装目录\OpenSSL-Win64"
 
参考:http://slproweb.com/products/Win32OpenSSL.html
https://answers.unrealengine.com/questions/115684/no-ssl-in-libwebsockets.html?sort=oldest
 
生成test-client test-server websockets_shared三个项目报错,发现是OPENSSL用的64位的,目标是32位,重新懒人安装32位的再cmake成功。
demo测试
接下来测试自带demo。
在生成的VS目录,test-server项目里,LOCAL_RESOURCE_PATH 定义的资源目录,改为libwebsockets-test-server文件夹的目录,我的是"./…/share/libwebsockets-test-server"。重新编译运行,在浏览器访问http://127.0.0.1:7681,进行demo测试。
 
搭建自己的ws server
参考:linux下libwebsockets编译及实例:
https://blog.csdn.net/yuanwei1314/article/details/76228495
利用libwebsockets写ws、wss服务端和客户端:
https://blog.csdn.net/weixin_39510813/article/details/86728804
libwebsockets之简单服务器代码:
https://blog.csdn.net/qq_39101111/article/details/79025438
 
封装
参考:封装利用libwebsockets写出的客户端、服务端程序为客户端服务端类
https://blog.csdn.net/weixin_39510813/article/details/86728916
 
https://blog.csdn.net/qq_36972985/article/details/82897868
 
API
部分转自:
总结使用libwebsockets开发接入层:https://blog.csdn.net/qifengzou/article/details/50281545 libwebsockets(三)实现简易websocket服务器:https://blog.csdn.net/u013780605/article/details/79489197
相关中文资料很少,感谢博主。结合说明对照自带的demo查看。
 
注册回调协议
终端向WS服务器发起ws连接请求时,一般会在协议头中通过Sec-WebSockets-Protocol指明协议名。而开源libwebsockets库通过对外提供注册协议回调的接口为用户自定义协议提供服务,注册协议回调的接口中将会指明协议名、以及对应的处理回调、自定义数据的大小等字段。 官方demo中有提到第一个protocol必须是http。
 
/* 注册协议回调配置表 */
struct libwebsocket_protocols g_aws_protocols[] =
{
// 官方demo中有提到第一个protocol必须是http
/* first protocol must always be HTTP handler */
/*http服务器库中已做实现,直接使用lws_callback_http_dummy即可*/
    { "http", lws_callback_http_dummy, 0, 0 },
    {
        "chat",                                         /* 协议名:其与Sec-Websockets-Protocol字段对应 */
        aws_callback_im_hdl,                            /* 回调函数:协议对应的回调处理函数 */
        sizeof(aws_im_session_data_t),                  /* 自定义数据空间大小:每个ws连接均会分配一个自定义数据空间 */
        0,                                              /* max frame size / rx buffer */
    },
    {
        "push",                                         /* 协议名 */
        aws_callback_push_hdl,                          /* 回调函数 */
        sizeof(aws_push_session_data_t),                /* 自定义数据空间大小 */
        0,                                              /* max frame size / rx buffer */
    },
    { NULL, NULL, 0, 0 }                                /* 结束标识 */
};
 
 
回调函数参数
回调函数用于处理对应协议各阶段的数据:
 
回调函数参数中的reason指明了调用回调函数的原因,其也代表了ws连接正处于哪个处理阶段或状态。比如:在ws连接创建成功后,应该进行自定义数据的初始化;在ws连接销毁阶段,应该释放自定义数据中用户分配的空间等。因此,要正确的编写协议回调函数就必须对reason各状态值有正确的理解。以下将对服务器端开发者需要关心的reason状态值的进行解释:
 
LWS_CALLBACK_PROTOCOL_INIT,协议初始化,只调用一次
 
重要函数说明
 
 
其他
使用时发现,单一lws会一直因为原因LWS_CALLBACK_SERVER_WRITEABLE调用回调。
 
参考
client端html5
https://blog.csdn.net/qq_39364032/article/details/79744309?utm_source=blogxgwz0
 
搭建Http客户端
http post相关知识
即post要在http header中定义编码格式Content-Type,我使用的服务端还需要Content-length等。而这些东西在libwebsockets中需要自己手填。
 
ref:
四种常见的 POST 提交数据方式:
https://www.cnblogs.com/softidea/p/5745369.html
Form表单、四种常见的POST请求提交数据方式:
https://blog.csdn.net/bigtree_3721/article/details/82809459
 
http client发送post请求
在libwebsockets的Github中已经有50个例子:
https://libwebsockets.org/git/libwebsockets/tree/minimal-examples
但是相关的中文资料太少了,看例子的注释和使用方法也是看得不明不白。
例子中,我有一个minimal-http-client-post.c,内容是发送表单和文件。
在研究了一下一些HTTP基础知识,简化一下这个example,只发送post请求和body内自定义内容。