目标:认识理解websocket协议、websocket切换过程和websocket协议格式。认识和学会使用websocketpp库常用接口。了解websocketpp库搭建服务器流程,认识和学会使用websocketpp库bin接口,最后使用websocketpp库搭建服务器。
初识websocket
平时我们在逛某宝,点击商品查看商品信息,从HTTP角度来看,就是客户端向某宝的服务器发送了一次HTTP请求,服务器接收到请求后,就将HTTP响应发送给客户端,这种情况下,服务器不会主动向客户端发送一次消息,就好像你的女神永远不会给你主动发一次信息一样。
当我们在逛网页的时候,突然发现网页的边边角角上弹出一个框框,上面写着:系兄弟就来砍我,我在沙场等你!点击进去后,我们注册用户,进入到游戏后,发现有一直怪在攻击我们。像这样的我们全程每动过一次鼠标键盘,服务器就会将怪物的移动数据和攻击数据源源不断地发送给我们客户端的情况,其实看起来就是服务器在主动向客户端发送消息。
HTTP定时轮询会有以下两个问题:①当我们使用f12打开页面时,就会发现满屏幕的HTTP请求,这就会消耗带宽,并且增加服务器的负担。②在最快的情况,用户扫码后,都需要等待1到2秒的时候,前端才会再次发送一次HTTP请求,然后才跳转页面,用户会感到明显的卡顿或延迟。
不管是HTTP定时轮询还是长轮询机制,本质都是客户端主动向服务器发送消息,服务器才会响应客户端,像扫码这样的场景还可以去用,但是如果是网页游戏呢?一般而言,游戏都会有大量的数据需要服务器主动推送给客户端,此时,需要websocket了。
WebSocket是从HTML5开始支持的一种网页端和服务端保持长连接的消息推送机制。
在传统的Web程序都属于是“一问一答”的形式,即客户端给服务器发送了HTTP请求,服务端才会给客户端返回一个HTTP响应。在这种情况下,服务端属于被动的一方,如果客户端不给服务端发送HTTP请求,服务端是不会主动给客户端发送HTTP响应的。
而像在网页即时聊天或者五子棋对战中这种程序中,都是非常依赖“消息推送”的,即需要服务端主动推送消息给客户端。因此,只是使用原生的HTTP协议,想要实现消息推送一般需要通过轮询的方式实现。
基于上述两个问题,就产生了WebSocket协议。WebSocket更接近于TCP这种级别的通信方式,一旦连接建立完成客户端或者服务器都可以主动地向对方发送数据。
原理解析
从HTTP协议切换到websocket协议
在建立TCP连接后(三次握手后),客户端向服务端发送一个HTTP请求,希望可以切换协议,切换成websocket协议。在HTTP请求当中,包含的重要信息有:
HTTP请求行GET/wsHTTP/1.1希望切换协议Connection:Upgrade切换的协议格式Upgrade:Websocket切换的协议的版本Sec-WebSocket-Version:xxx通信的钥匙Sec-WebSocket-Key:xxx
服务端收到请求后,会查看客户端想要切换的协议和版本自己是否支持,如果支持,那么就会同意切换,并且发送HTTP响应给客户端,HTTP响应中包含的重要信息有:
响应行HTTP/1.1101xxx101表示切换协议的响应切换协议Connection:Upgrade切换的协议格式Upgrade:Websocket通信的钥匙,也表示同意切换Sec-WebSocket-Accept:xxx
切换完成,后续客户端和服务端直接就可以使用websocket协议进行通信,服务端可以主动给客户端推送请求了。
WebSocket协议格式
websocketpp库常用接口
WebSocketpp是个跨平台的开源(BSD许可证)头部专用C++库,它实现了RFC6455(WebSocket协议)和RFC7692(WebSocketCompressionExtensions)。它允许将WebSocket客端和服务器功能集成到C++程序中。在最常见的配置中,全功能络I/O由Asio络库提供。
WebSocketpp的主要特性包括:
·事件驱动的接口·持HTTP/HTTPS、WS/WSS、IPv6·灵活的依赖管理—Boost库/C++11标准库·可移植性:Posix/Windows、32/64bit、Intel/ARM·线程安全
WebSocketpp同时支持HTTP和Websocket两种网络协议,比较适用于我们本次的项目,所以我们选用该库作为项目的依赖库用来搭建HTTP和WebSocket服务器。
下面是websocketpp的常用接口,用于在写项目时做参考:
lib::weak_ptr:弱指针类,用于表示指向对象的非拥有引用。lib::shared_ptr:共享指针类,用于管理动态分配的对象的所有权。lib::asio::steady_timer:基于asio的稳定定时器类,用于定时触发事件。lib::function:函数对象类,用于保存和调用可调用对象。那么在上面提供的类和函数接口中,有用到lib命名空间的有:
使用websocketpp搭建服务器
搭建服务器的基本流程
1.实例化server对象。2.设置日志输出等级。3.初始化asio框架种的调度器。4.设置业务处理回调函数(具体业务处理的函数由我们自己实现)。5.设置服务器监听端口。6.开始获取新建连接。7.启动服务器。
bind的使用
C++11中的bind,作用是用于实现对函数进行参数绑定的功能。
比如:我们实现了一个print函数:
voidprint(char*str)fstd:cout< 如果选择使用bind将函数和参数进行绑定,那么就不需要传参数了。 autofunc=std:.bind(print,"nihao");对print函数进行参数绑定并生成了一个新的可调用对象funcfunc();函数调用等价于print("nihao");此外,如果还有参数传入,比如: voidprint(char*str,intnum){std:cout< autofunc=std.bind(print,"nihao",std:placeholders:._1);对print函数进行参数绑定并生成了一个新的可调用对象funcfunc(10);函数调用等价于print("nihao",10);示例代码: 使用websocketpp搭建简单服务器 通过上面搭建服务器的基本流程,我们可以逐一实现出来: 从websocketpp的常用接口的介绍中可以看到,server类继承endpoint类,需要传入模板参数websocketpp中的config,而需要用到asio框架。 /*定义server类的类型,可变参数为websocketpp::config::asio,因为server继承的endpoint类需要传入这个模板参数*/typedefwebsocketpp::server 总结一点就是: HTTP请求回调处理函数主要是处理来自客户端的HTTP请求,它从连接对象中获取HTTP请求的正文,并通过请求对象获取URI和方法等信息,然后根据不同的方法和URI来进行相应的处理,最后构建HTTP响应对象并发送回客户端。HTTP是一种无状态协议,每个请求都是独立的。WebSocket消息处理回调函数主要是处理来自客户端的WebSocket消息,它从连接对象中获取WebSocket消息的内容,并进行相应的处理逻辑。不像HTTP请求那样需要获取URI和方法等信息,WebSocket是一种双向通信协议,服务器和客户端可以在持久连接上进行实时双向通信。这个回调函数通过使用连接对象的send方法直接将响应消息发送回客户端。