WebSocket协议

目录

升级过程

报文格式

常用接口

实现服务器

实现客户端与服务端通信


服务器和客户端在进行HTTP通信时,只有客户端能主动向服务端发送消息 ,而服务端无法主动向指定客户端发送消息。即服务端是被动方

当客户端想给客户端2发送消息时,需要由服务端转发,但服务端此时不能主动向客户端2发数据

需要由客户端2主动向服务端询问是否有新数据

这样就代表每个客户端都需要持续轮询 ,成本较高且不能得到及时响应

而WebSocket协议就是为了解决这个问题所诞生,当双方建立了WebSocket连接后,服务端可以主动向客户端发送消息 (准确来说是双方都可以主动发消息)

升级过程

WebSocket协议是基于HTTP升级上来的

双方首先建立HTTP连接

建立完成后,客户端会向服务端发送WebSocket升级报文:

bash 复制代码
Request Headers # 请求头信息

Accept-Encoding: gzip, deflate, sdch
Accept-Language: zh-CN,zh;q=0.8
Cache-Control: no-cache
Connection: Upgrade # 希望升级协议
Host: ***.***.***.***(打码)
Origin: http://127.0.0.1:8020
Pragma: no-cache
Sec-WebSocket-Extensions: permessage-deflate; client_max_window_bits
Sec-WebSocket-Key: 8M2YVNTrYpX1yPCdGhgC+g== # 该WebSocket与服务端通讯的"钥匙"
Sec-WebSocket-Version: 13 # 版本
Upgrade: websocket # 升级协议格式
User-Agent: Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko)

当服务端判断可行时,会向客户端发送响应:

bash 复制代码
Response Headers # 返回头信息
Connection: upgrade # 升级协议               
Date: Thu, 16 Mar 2017 12:10:42 GMT
Sec-WebSocket-Accept: H4JE024axXy53/RgSfHlmSoMhJo= # 服务端与该客户端通讯的"钥匙"
Sec-WebSocket-Extensions: permessage-deflate;client_max_window_bits=15
Server: Apache-Coyote/1.1
Upgrade: websocket # 升级的协议格式

之后双端就会以WebSocket通信

报文格式

FIN:WebSocket以消息为单位,一个消息会被拆为多个报文 ,FIN为1表示末尾帧

RSV:保留字段,只在扩展时使用

opcode:当前数据帧类型

  • 0x0 --> 延续帧
  • 0x1 --> 文本帧
  • 0x2 --> 二进制帧
  • 0x3-0x7 --> 保留位
  • 0x9 --> ping帧
  • 0xa --> pong帧
  • 0xb-0xf --> 保留位

mask:表示payload 数据是否被编码,若为1 ,则有Mask-Key ,用于解码Payload数据(仅客户端发送给服务端的消息需要设置。)

7位Payload长度:7位,范围为0~127

  • 若值为<126,该值就是Payload长度。
  • 若值=126,表示后面的2字节(16位Payload长度)才是Payload长度。
  • 若值=127,表示后面的2字节(16位Payload长度)+4字节(32位Payload长度)+2字节(16位Payload长度)总共8字节才是Payload长度

Payload数据:有效载荷

常用接口

  1. 日志相关接口:set_access_channels ,例如传入websocketpp::log::alevel::none 代表禁止打印所有日志

  2. 回调函数相关接口:针对不同事件设置不同的处理函数

websocketpp搭建了服务器之后,给不同的事件设置了不同的处理函数指针 。这些指针可以指向指定的函数,当服务器收到了指定的数据,触发了指定的事件后就会通过函数指针去调用这些函数 ,这时候,我们程序员就可以编写一些业务处理函数 ,将其设置为对应事件的业务处理函数

  • set_open_handler 设置websocket协议握手成功的回调函数
  • set_close_handler 设置websocket协议连接断开的回调函数
  • set_message_handler 设置为websocket协议消息处理的回调函数
  • set_http_handler 设置http请求的回调处理函数

通信连接相关接口:

  • send 给客户端发送消息
  • close 关闭连接
  • get_con_from_hdl 通过connection_hdl获取对应的connection_ptr

其他服务器搭建的接口:

  • init_asio() 初始化asio框架
  • set_reuse_addr 是否启动地址重用
  • start_accept 开始获取新建链接
  • listen 设置绑定监听端口
  • run 启动服务器
  • set_timer 设置定时任务

实现服务器

使用websocketpp搭建服务器的流程:

  1. 实例化server对象
  2. 设置日志输出等级
  3. 初始化asio框架中的调度器
  4. 设置业务处理回调函数(具体业务处理的函数由我们自己实现)
  5. 设置服务器监听端口
  6. 开始获取新建连接
  7. 启动服务器
cpp 复制代码
#include <iostream>
#include <websocketpp/server.hpp>
#include <websocketpp/config/asio.hpp>

typedef websocketpp::server<websocketpp::config::asio> wssvr_t;

using namespace std;

int main()
{
    // 实例化server对象
    wssvr_t wssvr;
    // 设置日志等级为none
    wssvr.set_access_channels(websocketpp::log::alevel::none);
    // 初始化asio调度器
    wssvr.init_asio();
    // 设置回调函数
    wssvr.set_open_handler([&wssvr](websocketpp::connection_hdl hdl){  });
    wssvr.set_close_handler([&wssvr](websocketpp::connection_hdl hdl){  });
    wssvr.set_http_handler([&wssvr](websocketpp::connection_hdl hdl){  });
    wssvr.set_message_handler([&wssvr](websocketpp::connection_hdl hdl, wssvr_t::message_ptr msg_ptr){  });
    // 设置监听端口
    wssvr.listen(8080);
    // 开始获取新连接
    wssvr.start_accept();
    // 启动服务器
    wssvr.run();

    return 0;
}

通过Lambda表达式 ,将4个handler的参数隐式带上wssvr,这样后面要发消息等操作时才可以找到实例化对象server

例如,一个简单的业务,返回一个html:

cpp 复制代码
// 设置回调函数
wssvr.set_open_handler([&wssvr](websocketpp::connection_hdl hdl)
                       { cout << "客户端加入\n"; });
wssvr.set_close_handler([&wssvr](websocketpp::connection_hdl hdl)
                        { cout << "客户端退出\n"; });
wssvr.set_http_handler([&wssvr](websocketpp::connection_hdl hdl)
                       {
                               // 返回一个简单http界面

                               // 获取该连接的connection对象
                               wssvr_t::connection_ptr conn = wssvr.get_con_from_hdl(hdl);
                               // 该请求的正文也可以直接用conn获取
                               // cout << "正文:" << conn->get_request_body() << endl;

                               // 获取该连接的请求对象
                               websocketpp::http::parser::request req = conn->get_request();

                               cout << "正文:" << req.get_body() << endl;
                               cout << "类型:" << req.get_method() << endl;
                               cout << "路径:" << req.get_uri() << endl;

                               // 设置响应正文(html)
                               string body = "<html><body><h1>Hello World.</h1></body></html>";
                               // 设置正文
                               conn->set_body(body);
                               // 添加报头字段
                               conn->append_header("Content-Type","text/html");
                               // 设置http状态码
                               conn->set_status(websocketpp::http::status_code::ok); });
wssvr.set_message_handler([&wssvr](websocketpp::connection_hdl hdl, wssvr_t::message_ptr msg_ptr)
                          {
                                // 当有消息过来时,获取该连接的connection_ptr
                                wssvr_t::connection_ptr conn = wssvr.get_con_from_hdl(hdl);cout << "消息:" << msg_ptr->get_payload() << endl;
                                // 要返回的内容
                                string rsp = "client say:" + msg_ptr->get_payload();
                                // 发送数据,第二个参数为发送数据的类型
                                conn->send(rsp, websocketpp::frame::opcode::text); });

实现客户端与服务端通信

服务端写好后,用AI随便写一个html客户端(客户端内的地址要改成自己的云服务器),既可以测试通信了

相关推荐
桌面运维家1 小时前
旧电脑跑不动课程软件,VDI云桌面怎样接着用?
运维·服务器·虚拟化·pve·vdi·云桌面
zhang133830890751 小时前
传统机械三杯式风速传感器易磨损冻堵?CG-88金属超声波风速风向传感器给出免运维方案
运维·服务器·网络·人工智能·自动化
知已难寻2 小时前
第 2 篇 · 云的地基:服务器、存储、网络三大基础设施
运维·服务器·网络·云原生·云计算·负载均衡·1024程序员节
IpdataCloud3 小时前
批量查IP的API接口怎么选?免费API配额瓶颈与离线库本地批量方案实测
网络·网络协议·tcp/ip
2601_949847754 小时前
bcrypt.js 跨端三方库仓颉适配全流程
linux·运维·ubuntu
m0_639906944 小时前
Linux 操作系统入门:目录结构、服务排查常用命令、Shell 脚本基础
linux·运维·后端
今天重构了吗5 小时前
Linux 上 rm -rf 删错了能找回吗
运维·服务器
ITyunwei09875 小时前
售后夜间值守怎么落地:从告警到交接的工单系统配置笔记
网络
Smoothcloud润云5 小时前
GPU远程纳管安全吗?最小权限、网络边界与日志审计实战
服务器·网络·人工智能·安全·gpu算力·gpu