Websocketpp的介绍与使用
老师讲解
websocket协议
使用: 客户端使用http同服务器发起协议切换请求; 服务器同意切换, 接下来就会转为websocket的长连接通信(不会因为长时间无通信而关闭连接,且随意收发信息)
切换过程:
http request:
GET /ws HTTP/1.1
Connection: Upgrade
Upgrade: websocket
Sec-WebSocket-Key: xxxxxxxxxxxxxx密钥字符串
http reponse:
HTTP/1.1 101 switching protocol
Connection: Upgrade
Upgrade: websocket
Sec-WebSocket-Accept: xxxxxxxxxxxxxxxx
客户端发送的密钥 + 258EAFA5-E914-47DA-95CA-C5AB0DC85B11 进行SHA-1计算
Websocketpp的简单介绍:
基于boost库的asio框架实现
Websocket介绍
WebSocket 是从 HTML5 开始⽀持的⼀种⽹⻚端和服务端保持⻓连接的 消息推送机制。
• 传统的 web 程序都是属于 "⼀问⼀答" 的形式,即客⼾端给服务器发送了⼀个 HTTP 请求,服务器给客⼾端返回⼀个 HTTP 响应。这种情况下服务器是属于被动的⼀⽅,如果客⼾端不主动发起请求服务器就⽆法主动给客⼾端响应
• 像⽹⻚即时聊天或者我们做的五⼦棋游戏这样的程序都是⾮常依赖 "消息推送" 的, 即需要服务器主动推动消息到客⼾端。如果只是使⽤原⽣的 HTTP 协议,要想实现消息推送⼀般需要通过 "轮询" 的⽅式实现, ⽽轮询的成本⽐较⾼并且也不能及时的获取到消息的响应。
基于上述两个问题, 就产⽣了WebSocket协议。WebSocket 更接近于 TCP 这种级别的通信⽅式,⼀旦连接建⽴完成客⼾端或者服务器都可以主动的向对⽅发送数据。
原理解析
WebSocket 协议本质上是⼀个基于 TCP 的协议。为了建⽴⼀个 WebSocket 连接,客⼾端浏览器⾸先要向服务器发起⼀个 HTTP 请求,这个请求和通常的 HTTP 请求不同,包含了⼀些附加头信息,通过这个附加头信息完成握⼿过程并升级协议的过程。
具体协议升级的过程如下:
报文格式
报文字段比较多,我们重点关注这几个字段:
• FIN: WebSocket传输数据以消息为概念单位,⼀个消息有可能由⼀个或多个帧组成,FIN字段为1 表⽰末尾帧。
• RSV1~3: 保留字段,只在扩展时使⽤,若未启⽤扩展则应置1,若收到不全为0的数据帧,且未协商扩展则⽴即终⽌连接。
• opcode: 标志当前数据帧的类型
◦ 0x0: 表⽰这是个延续帧,当 opcode 为 0 表⽰本次数据传输采⽤了数据分⽚,当前收到的帧为其中⼀个分⽚
◦ 0x1: 表⽰这是⽂本帧
◦ 0x2: 表⽰这是⼆进制帧
◦ 0x3-0x7: 保留,暂未使⽤
◦ 0x8: 表⽰连接断开
◦ 0x9: 表⽰ ping 帧
◦ 0xa: 表⽰ pong 帧
◦ 0xb-0xf: 保留,暂未使⽤
• mask: 表⽰Payload数据是否被编码,若为1则必有Mask-Key,⽤于解码Payload数据。仅客⼾端
发送给服务端的消息需要设置。
• Payload length: 数据载荷的⻓度,单位是字节, 有可能为7位、7+16位、7+64位。假设Payload length = x
◦ x为0~126:数据的⻓度为x字节
◦ x为126:后续2个字节代表⼀个16位的⽆符号整数,该⽆符号整数的值为数据的⻓度
◦ x为127:后续8个字节代表⼀个64位的⽆符号整数(最⾼位为0),该⽆符号整数的值为数据的⻓度
• Mask-Key: 当mask为1时存在,⻓度为4字节,解码规则: DECODED[i] = ENCODED[i] ^ MASK[i % 4]
• Payload data: 报⽂携带的载荷数据
Websocketpp介绍
WebSocketpp是⼀个跨平台的开源(BSD许可证)头部专⽤C++库,它实现了RFC6455(WebSocket协议)和RFC7692(WebSocketCompression Extensions)。它允许将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服务器。
下⾯是该项⽬的⼀些常⽤⽹站, ⼤家多去学习。
• github:github.com/zaphoyd/web…
• ⽤⼾⼿册: docs.websocketpp.org/
Websocketpp使用
websocketpp常用接口介绍:
namespace websocketpp {
typedef lib::weak_ptr<void> connection_hdl;
template <typename config>
class endpoint : public config::socket_type {
typedef lib::shared_ptr<lib::asio::steady_timer> timer_ptr;
typedef typename connection_type::ptr connection_ptr;
typedef typename connection_type::message_ptr message_ptr;
typedef lib::function<void(connection_hdl)> open_handler;
typedef lib::function<void(connection_hdl)> close_handler;
typedef lib::function<void(connection_hdl)> http_handler;
typedef lib::function<void(connection_hdl,message_ptr)> message_handler;
/* websocketpp::log::alevel::none 禁⽌打印所有⽇志*/
void set_access_channels(log::level channels);/*设置⽇志打印等级*/
void clear_access_channels(log::level channels);/*清除指定等级的⽇志*/
/*设置指定事件的回调函数*/
void set_open_handler(open_handler h);/*websocket握⼿成功回调处理函数*/
void set_close_handler(close_handler h);/*websocket连接关闭回调处理函数*/
void set_message_handler(message_handler h);/*websocket消息回调处理函数*/
void set_http_handler(http_handler h);/*http请求回调处理函数*/
/*发送数据接⼝*/
void send(connection_hdl hdl, std::string& payload, frame::opcode::value op);
void send(connection_hdl hdl, void* payload, size_t len, frame::opcode::value op);
/*关闭连接接⼝*/
void close(connection_hdl hdl, close::status::value code, std::string& reason);
/*获取connection_hdl 对应连接的connection_ptr*/
connection_ptr get_con_from_hdl(connection_hdl hdl);
/*websocketpp基于asio框架实现,init_asio⽤于初始化asio框架中的io_service调度器*/
void init_asio();
/*设置是否启⽤地址重⽤*/
void set_reuse_addr(bool value);
/*设置endpoint的绑定监听端⼝*/
void listen(uint16_t port);
/*对io_service对象的run接⼝封装,⽤于启动服务器*/
std::size_t run();
/*websocketpp提供的定时器,以毫秒为单位*/
timer_ptr set_timer(long duration, timer_handler callback);
};
template <typename config>
class server : public endpoint<connection<config>,config> {
/*初始化并启动服务端监听连接的accept事件处理*/
void start_accept();
};
template <typename config>
class connection
: public config::transport_type::transport_con_type
, public config::connection_base
{
/*发送数据接⼝*/
error_code send(std::string&payload, frame::opcode::value op=frame::opcode::text);
/*获取http请求头部*/
std::string const & get_request_header(std::string const & key)
/*获取请求正⽂*/
std::string const & get_request_body();
/*设置响应状态码*/
void set_status(http::status_code::value code);
/*设置http响应正⽂*/
void set_body(std::string const & value);
/*添加http响应头部字段*/
void append_header(std::string const & key, std::string const & val);
/*获取http请求对象*/
request_type const & get_request();
/*获取connection_ptr 对应的 connection_hdl */
connection_hdl get_handle();
};
namespace http {
namespace parser {
class parser {
std::string const & get_header(std::string const & key)
}
class request : public parser {
/*获取请求⽅法*/
std::string const & get_method()
/*获取请求uri接⼝*/
std::string const & get_uri()
};
}};
namespace message_buffer {
/*获取websocket请求中的payload数据类型*/
frame::opcode::value get_opcode();
/*获取websocket中payload数据*/
std::string const & get_payload();
};
namespace log {
struct alevel {
static level const none = 0x0;
static level const connect = 0x1;
static level const disconnect = 0x2;
static level const control = 0x4;
static level const frame_header = 0x8;
static level const frame_payload = 0x10;
static level const message_header = 0x20;
static level const message_payload = 0x40;
static level const endpoint = 0x80;
static level const debug_handshake = 0x100;
static level const debug_close = 0x200;
static level const devel = 0x400;
static level const app = 0x800;
static level const http = 0x1000;
static level const fail = 0x2000;
static level const access_core = 0x00003003;
static level const all = 0xffffffff;
};
}
namespace http {
namespace status_code {
enum value {
uninitialized = 0,
continue_code = 100,
switching_protocols = 101,
ok = 200,
created = 201,
accepted = 202,
non_authoritative_information = 203,
no_content = 204,
reset_content = 205,
partial_content = 206,
multiple_choices = 300,
moved_permanently = 301,
found = 302,
see_other = 303,
not_modified = 304,
use_proxy = 305,
temporary_redirect = 307,
bad_request = 400,
unauthorized = 401,
payment_required = 402,
forbidden = 403,
not_found = 404,
method_not_allowed = 405,
not_acceptable = 406,
proxy_authentication_required = 407,
request_timeout = 408,
conflict = 409,
gone = 410,
length_required = 411,
precondition_failed = 412,
request_entity_too_large = 413,
request_uri_too_long = 414,
unsupported_media_type = 415,
request_range_not_satisfiable = 416,
expectation_failed = 417,
im_a_teapot = 418,
upgrade_required = 426,
precondition_required = 428,
too_many_requests = 429,
request_header_fields_too_large = 431,
internal_server_error = 500,
not_implemented = 501,
bad_gateway = 502,
service_unavailable = 503,
gateway_timeout = 504,
http_version_not_supported = 505,
not_extended = 510,
network_authentication_required = 511
};}}
namespace frame {
namespace opcode {
enum value {
continuation = 0x0,
text = 0x1,
binary = 0x2,
rsv3 = 0x3,
rsv4 = 0x4,
rsv5 = 0x5,
rsv6 = 0x6,
rsv7 = 0x7,
close = 0x8,
ping = 0x9,
pong = 0xA,
control_rsvb = 0xB,
control_rsvc = 0xC,
control_rsvd = 0xD,
control_rsve = 0xE,
control_rsvf = 0xF,
};}}
}
Simple http/websocket服务器
使⽤Websocketpp实现⼀个简单的http和websocket服务器
#include <iostream>
#include <websocketpp/config/asio_no_tls.hpp>
#include <websocketpp/server.hpp>
using namespace std;
typedef websocketpp::server<websocketpp::config::asio> websocketsvr;
typedef websocketsvr::message_ptr message_ptr;
using websocketpp::lib::placeholders::_1;
using websocketpp::lib::placeholders::_2;
using websocketpp::lib::bind;
// websocket连接成功的回调函数
void OnOpen(websocketsvr *server,websocketpp::connection_hdl hdl){
cout<<"连接成功"<<endl;
}
// websocket连接成功的回调函数
void OnClose(websocketsvr *server,websocketpp::connection_hdl hdl){
cout<<"连接关闭"<<endl;
}
// websocket连接收到消息的回调函数
void OnMessage(websocketsvr *server,websocketpp::connection_hdl hdl,message_ptr msg){
cout << "收到消息" << msg->get_payload() << endl;
// 收到消息将相同的消息发回给websocket客⼾端
server->send(hdl, msg->get_payload(), websocketpp::frame::opcode::text);
}
// websocket连接异常的回调函数
void OnFail(websocketsvr *server,websocketpp::connection_hdl hdl){
cout<<"连接异常"<<endl;
}
// 处理http请求的回调函数 返回⼀个html欢迎⻚⾯
void OnHttp(websocketsvr *server,websocketpp::connection_hdl hdl){
cout<<"处理http请求"<<endl;
websocketsvr::connection_ptr con = server->get_con_from_hdl(hdl);
std::stringstream ss;
ss << "<!doctype html><html><head>"
<< "<title>hello websocket</title><body>"
<< "<h1>hello websocketpp</h1>"
<< "</body></head></html>";
con->set_body(ss.str());
con->set_status(websocketpp::http::status_code::ok);
}
int main(){
// 使⽤websocketpp库创建服务器
websocketsvr server;
// 设置websocketpp库的⽇志级别
// all表⽰打印全部级别⽇志
// none表⽰什么⽇志都不打印
server.set_access_channels(websocketpp::log::alevel::none);
/*初始化asio*/
server.init_asio();
// 注册http请求的处理函数
server.set_http_handler(bind(&OnHttp, &server, ::_1));
// 注册websocket请求的处理函数
server.set_open_handler(bind(&OnOpen, &server, ::_1));
server.set_close_handler(bind(&OnClose, &server, _1));
server.set_message_handler(bind(&OnMessage,&server,_1,_2));
// 监听8888端⼝
server.listen(8888);
// 开始接收tcp连接
server.start_accept();
// 开始运⾏服务器
server.run();
return 0;
}
Http客户端
使⽤浏览器作为http客⼾端即可, 访问服务器的8888端⼝。
WS客户端
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Test Websocket</title>
</head>
<body>
<input type="text" id="message">
<button id="submit">提交</button>
<script>
// 创建 websocket 实例
// ws://192.168.51.100:8888
// 类⽐http
// ws表⽰websocket协议
// 192.168.51.100 表⽰服务器地址
// 8888表⽰服务器绑定的端⼝
let websocket = new WebSocket("ws://192.168.51.100:8888");
// 处理连接打开的回调函数
websocket.onopen = function() {
console.log("连接建⽴");
}
// 处理收到消息的回调函数
// 控制台打印消息
websocket.onmessage = function(e) {
console.log("收到消息: " + e.data);
}
// 处理连接异常的回调函数
websocket.onerror = function() {
console.log("连接异常");
}
// 处理连接关闭的回调函数
websocket.onclose = function() {
console.log("连接关闭");
}
// 实现点击按钮后, 通过 websocket实例 向服务器发送请求
let input = document.querySelector('#message');
let button = document.querySelector('#submit');
button.onclick = function() {
console.log("发送消息: " + input.value);
websocket.send(input.value);
}
</script>
</body>
</html>
在控制台中我们可以看到连接建⽴、客⼾端和服务器通信以及断开连接的过程(关闭服务器就会看到断开连接的现象)
注: 通过f12 或者 fn + f12打开浏览器的调试模式
Websocketpp的介绍与使用
老师讲解
websocket协议
使用: 客户端使用http同服务器发起协议切换请求; 服务器同意切换, 接下来就会转为websocket的长连接通信(不会因为长时间无通信而关闭连接,且随意收发信息)
切换过程:
http request:
GET /ws HTTP/1.1
Connection: Upgrade
Upgrade: websocket
Sec-WebSocket-Key: xxxxxxxxxxxxxx密钥字符串
http reponse:
HTTP/1.1 101 switching protocol
Connection: Upgrade
Upgrade: websocket
Sec-WebSocket-Accept: xxxxxxxxxxxxxxxx
客户端发送的密钥 + 258EAFA5-E914-47DA-95CA-C5AB0DC85B11 进行SHA-1计算
Websocketpp的简单介绍:
基于boost库的asio框架实现
Websocket介绍
WebSocket 是从 HTML5 开始⽀持的⼀种⽹⻚端和服务端保持⻓连接的 消息推送机制。
• 传统的 web 程序都是属于 "⼀问⼀答" 的形式,即客⼾端给服务器发送了⼀个 HTTP 请求,服务器给客⼾端返回⼀个 HTTP 响应。这种情况下服务器是属于被动的⼀⽅,如果客⼾端不主动发起请求服务器就⽆法主动给客⼾端响应
• 像⽹⻚即时聊天或者我们做的五⼦棋游戏这样的程序都是⾮常依赖 "消息推送" 的, 即需要服务器主动推动消息到客⼾端。如果只是使⽤原⽣的 HTTP 协议,要想实现消息推送⼀般需要通过 "轮询" 的⽅式实现, ⽽轮询的成本⽐较⾼并且也不能及时的获取到消息的响应。
基于上述两个问题, 就产⽣了WebSocket协议。WebSocket 更接近于 TCP 这种级别的通信⽅式,⼀旦连接建⽴完成客⼾端或者服务器都可以主动的向对⽅发送数据。
原理解析
WebSocket 协议本质上是⼀个基于 TCP 的协议。为了建⽴⼀个 WebSocket 连接,客⼾端浏览器⾸先要向服务器发起⼀个 HTTP 请求,这个请求和通常的 HTTP 请求不同,包含了⼀些附加头信息,通过这个附加头信息完成握⼿过程并升级协议的过程。
具体协议升级的过程如下:
报文格式
报文字段比较多,我们重点关注这几个字段:
• FIN: WebSocket传输数据以消息为概念单位,⼀个消息有可能由⼀个或多个帧组成,FIN字段为1 表⽰末尾帧。
• RSV1~3: 保留字段,只在扩展时使⽤,若未启⽤扩展则应置1,若收到不全为0的数据帧,且未协商扩展则⽴即终⽌连接。
• opcode: 标志当前数据帧的类型
◦ 0x0: 表⽰这是个延续帧,当 opcode 为 0 表⽰本次数据传输采⽤了数据分⽚,当前收到的帧为其中⼀个分⽚
◦ 0x1: 表⽰这是⽂本帧
◦ 0x2: 表⽰这是⼆进制帧
◦ 0x3-0x7: 保留,暂未使⽤
◦ 0x8: 表⽰连接断开
◦ 0x9: 表⽰ ping 帧
◦ 0xa: 表⽰ pong 帧
◦ 0xb-0xf: 保留,暂未使⽤
• mask: 表⽰Payload数据是否被编码,若为1则必有Mask-Key,⽤于解码Payload数据。仅客⼾端
发送给服务端的消息需要设置。
• Payload length: 数据载荷的⻓度,单位是字节, 有可能为7位、7+16位、7+64位。假设Payload length = x
◦ x为0~126:数据的⻓度为x字节
◦ x为126:后续2个字节代表⼀个16位的⽆符号整数,该⽆符号整数的值为数据的⻓度
◦ x为127:后续8个字节代表⼀个64位的⽆符号整数(最⾼位为0),该⽆符号整数的值为数据的⻓度
• Mask-Key: 当mask为1时存在,⻓度为4字节,解码规则: DECODED[i] = ENCODED[i] ^ MASK[i % 4]
• Payload data: 报⽂携带的载荷数据
Websocketpp介绍
WebSocketpp是⼀个跨平台的开源(BSD许可证)头部专⽤C++库,它实现了RFC6455(WebSocket协议)和RFC7692(WebSocketCompression Extensions)。它允许将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服务器。
下⾯是该项⽬的⼀些常⽤⽹站, ⼤家多去学习。
• github:github.com/zaphoyd/web…
• ⽤⼾⼿册: docs.websocketpp.org/
Websocketpp使用
websocketpp常用接口介绍:
namespace websocketpp {
typedef lib::weak_ptr<void> connection_hdl;
template <typename config>
class endpoint : public config::socket_type {
typedef lib::shared_ptr<lib::asio::steady_timer> timer_ptr;
typedef typename connection_type::ptr connection_ptr;
typedef typename connection_type::message_ptr message_ptr;
typedef lib::function<void(connection_hdl)> open_handler;
typedef lib::function<void(connection_hdl)> close_handler;
typedef lib::function<void(connection_hdl)> http_handler;
typedef lib::function<void(connection_hdl,message_ptr)> message_handler;
/* websocketpp::log::alevel::none 禁⽌打印所有⽇志*/
void set_access_channels(log::level channels);/*设置⽇志打印等级*/
void clear_access_channels(log::level channels);/*清除指定等级的⽇志*/
/*设置指定事件的回调函数*/
void set_open_handler(open_handler h);/*websocket握⼿成功回调处理函数*/
void set_close_handler(close_handler h);/*websocket连接关闭回调处理函数*/
void set_message_handler(message_handler h);/*websocket消息回调处理函数*/
void set_http_handler(http_handler h);/*http请求回调处理函数*/
/*发送数据接⼝*/
void send(connection_hdl hdl, std::string& payload, frame::opcode::value op);
void send(connection_hdl hdl, void* payload, size_t len, frame::opcode::value op);
/*关闭连接接⼝*/
void close(connection_hdl hdl, close::status::value code, std::string& reason);
/*获取connection_hdl 对应连接的connection_ptr*/
connection_ptr get_con_from_hdl(connection_hdl hdl);
/*websocketpp基于asio框架实现,init_asio⽤于初始化asio框架中的io_service调度器*/
void init_asio();
/*设置是否启⽤地址重⽤*/
void set_reuse_addr(bool value);
/*设置endpoint的绑定监听端⼝*/
void listen(uint16_t port);
/*对io_service对象的run接⼝封装,⽤于启动服务器*/
std::size_t run();
/*websocketpp提供的定时器,以毫秒为单位*/
timer_ptr set_timer(long duration, timer_handler callback);
};
template <typename config>
class server : public endpoint<connection<config>,config> {
/*初始化并启动服务端监听连接的accept事件处理*/
void start_accept();
};
template <typename config>
class connection
: public config::transport_type::transport_con_type
, public config::connection_base
{
/*发送数据接⼝*/
error_code send(std::string&payload, frame::opcode::value op=frame::opcode::text);
/*获取http请求头部*/
std::string const & get_request_header(std::string const & key)
/*获取请求正⽂*/
std::string const & get_request_body();
/*设置响应状态码*/
void set_status(http::status_code::value code);
/*设置http响应正⽂*/
void set_body(std::string const & value);
/*添加http响应头部字段*/
void append_header(std::string const & key, std::string const & val);
/*获取http请求对象*/
request_type const & get_request();
/*获取connection_ptr 对应的 connection_hdl */
connection_hdl get_handle();
};
namespace http {
namespace parser {
class parser {
std::string const & get_header(std::string const & key)
}
class request : public parser {
/*获取请求⽅法*/
std::string const & get_method()
/*获取请求uri接⼝*/
std::string const & get_uri()
};
}};
namespace message_buffer {
/*获取websocket请求中的payload数据类型*/
frame::opcode::value get_opcode();
/*获取websocket中payload数据*/
std::string const & get_payload();
};
namespace log {
struct alevel {
static level const none = 0x0;
static level const connect = 0x1;
static level const disconnect = 0x2;
static level const control = 0x4;
static level const frame_header = 0x8;
static level const frame_payload = 0x10;
static level const message_header = 0x20;
static level const message_payload = 0x40;
static level const endpoint = 0x80;
static level const debug_handshake = 0x100;
static level const debug_close = 0x200;
static level const devel = 0x400;
static level const app = 0x800;
static level const http = 0x1000;
static level const fail = 0x2000;
static level const access_core = 0x00003003;
static level const all = 0xffffffff;
};
}
namespace http {
namespace status_code {
enum value {
uninitialized = 0,
continue_code = 100,
switching_protocols = 101,
ok = 200,
created = 201,
accepted = 202,
non_authoritative_information = 203,
no_content = 204,
reset_content = 205,
partial_content = 206,
multiple_choices = 300,
moved_permanently = 301,
found = 302,
see_other = 303,
not_modified = 304,
use_proxy = 305,
temporary_redirect = 307,
bad_request = 400,
unauthorized = 401,
payment_required = 402,
forbidden = 403,
not_found = 404,
method_not_allowed = 405,
not_acceptable = 406,
proxy_authentication_required = 407,
request_timeout = 408,
conflict = 409,
gone = 410,
length_required = 411,
precondition_failed = 412,
request_entity_too_large = 413,
request_uri_too_long = 414,
unsupported_media_type = 415,
request_range_not_satisfiable = 416,
expectation_failed = 417,
im_a_teapot = 418,
upgrade_required = 426,
precondition_required = 428,
too_many_requests = 429,
request_header_fields_too_large = 431,
internal_server_error = 500,
not_implemented = 501,
bad_gateway = 502,
service_unavailable = 503,
gateway_timeout = 504,
http_version_not_supported = 505,
not_extended = 510,
network_authentication_required = 511
};}}
namespace frame {
namespace opcode {
enum value {
continuation = 0x0,
text = 0x1,
binary = 0x2,
rsv3 = 0x3,
rsv4 = 0x4,
rsv5 = 0x5,
rsv6 = 0x6,
rsv7 = 0x7,
close = 0x8,
ping = 0x9,
pong = 0xA,
control_rsvb = 0xB,
control_rsvc = 0xC,
control_rsvd = 0xD,
control_rsve = 0xE,
control_rsvf = 0xF,
};}}
}
Simple http/websocket服务器
使⽤Websocketpp实现⼀个简单的http和websocket服务器
#include <iostream>
#include <websocketpp/config/asio_no_tls.hpp>
#include <websocketpp/server.hpp>
using namespace std;
typedef websocketpp::server<websocketpp::config::asio> websocketsvr;
typedef websocketsvr::message_ptr message_ptr;
using websocketpp::lib::placeholders::_1;
using websocketpp::lib::placeholders::_2;
using websocketpp::lib::bind;
// websocket连接成功的回调函数
void OnOpen(websocketsvr *server,websocketpp::connection_hdl hdl){
cout<<"连接成功"<<endl;
}
// websocket连接成功的回调函数
void OnClose(websocketsvr *server,websocketpp::connection_hdl hdl){
cout<<"连接关闭"<<endl;
}
// websocket连接收到消息的回调函数
void OnMessage(websocketsvr *server,websocketpp::connection_hdl hdl,message_ptr msg){
cout << "收到消息" << msg->get_payload() << endl;
// 收到消息将相同的消息发回给websocket客⼾端
server->send(hdl, msg->get_payload(), websocketpp::frame::opcode::text);
}
// websocket连接异常的回调函数
void OnFail(websocketsvr *server,websocketpp::connection_hdl hdl){
cout<<"连接异常"<<endl;
}
// 处理http请求的回调函数 返回⼀个html欢迎⻚⾯
void OnHttp(websocketsvr *server,websocketpp::connection_hdl hdl){
cout<<"处理http请求"<<endl;
websocketsvr::connection_ptr con = server->get_con_from_hdl(hdl);
std::stringstream ss;
ss << "<!doctype html><html><head>"
<< "<title>hello websocket</title><body>"
<< "<h1>hello websocketpp</h1>"
<< "</body></head></html>";
con->set_body(ss.str());
con->set_status(websocketpp::http::status_code::ok);
}
int main(){
// 使⽤websocketpp库创建服务器
websocketsvr server;
// 设置websocketpp库的⽇志级别
// all表⽰打印全部级别⽇志
// none表⽰什么⽇志都不打印
server.set_access_channels(websocketpp::log::alevel::none);
/*初始化asio*/
server.init_asio();
// 注册http请求的处理函数
server.set_http_handler(bind(&OnHttp, &server, ::_1));
// 注册websocket请求的处理函数
server.set_open_handler(bind(&OnOpen, &server, ::_1));
server.set_close_handler(bind(&OnClose, &server, _1));
server.set_message_handler(bind(&OnMessage,&server,_1,_2));
// 监听8888端⼝
server.listen(8888);
// 开始接收tcp连接
server.start_accept();
// 开始运⾏服务器
server.run();
return 0;
}
Http客户端
使⽤浏览器作为http客⼾端即可, 访问服务器的8888端⼝。
WS客户端
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Test Websocket</title>
</head>
<body>
<input type="text" id="message">
<button id="submit">提交</button>
<script>
// 创建 websocket 实例
// ws://192.168.51.100:8888
// 类⽐http
// ws表⽰websocket协议
// 192.168.51.100 表⽰服务器地址
// 8888表⽰服务器绑定的端⼝
let websocket = new WebSocket("ws://192.168.51.100:8888");
// 处理连接打开的回调函数
websocket.onopen = function() {
console.log("连接建⽴");
}
// 处理收到消息的回调函数
// 控制台打印消息
websocket.onmessage = function(e) {
console.log("收到消息: " + e.data);
}
// 处理连接异常的回调函数
websocket.onerror = function() {
console.log("连接异常");
}
// 处理连接关闭的回调函数
websocket.onclose = function() {
console.log("连接关闭");
}
// 实现点击按钮后, 通过 websocket实例 向服务器发送请求
let input = document.querySelector('#message');
let button = document.querySelector('#submit');
button.onclick = function() {
console.log("发送消息: " + input.value);
websocket.send(input.value);
}
</script>
</body>
</html>
在控制台中我们可以看到连接建⽴、客⼾端和服务器通信以及断开连接的过程(关闭服务器就会看到断开连接的现象)
注: 通过f12 或者 fn + f12打开浏览器的调试模式