一、什么是WebSocket?
WebSocket 是一种在单个TCP连接上进行全双工通信的协议。WebSocket 使得客户端和服务器之间的数据交换变得更加简单,允许服务端主动向客户端推送数据。
在 WebSocket API 中,浏览器和服务器只需要完成一次握手,两者之间就直接可以创建持久性的连接, 并进行双向数据传输。(维基百科)
WebSocket本质上一种计算机网络应用层的协议,用来弥补http协议在持久通信能力上的不足。
WebSocket 协议在2008年诞生,2011年成为国际标准。现在最新版本浏览器都已经支持了。
它的最大特点就是,服务器可以主动向客户端推送信息,客户端也可以主动向服务器发送信息,是真正的双向平等对话,属于服务器推送技术的一种。
WebSocket 的其他特点包括:
(1)建立在 TCP 协议之上,服务器端的实现比较容易。
(2)与 HTTP 协议有着良好的兼容性。默认端口也是80和443,并且握手阶段采用 HTTP 协议,因此握手时不容易屏蔽,能通过各种 HTTP 代理服务器。
(3)数据格式比较轻量,性能开销小,通信高效。
(4)可以发送文本,也可以发送二进制数据。
(5)没有同源限制,客户端可以与任意服务器通信。
(6)协议标识符是ws(如果加密,则为wss),服务器网址就是 URL。
二、WebSocket工作原理
握手阶段
浏览器先通过一个特殊的 HTTP 请求(带有 Upgrade: websocket 头)向服务器发起连接请求。
如果服务器支持 WebSocket,就会返回一个确认响应(101 Switching Protocols),表示协议升级成功。
建立连接
握手完成后,HTTP 协议升级为 WebSocket 协议。
此时,客户端与服务器之间形成一个持久连接,双方都可以随时主动发送数据。
双向通信
客户端可以向服务器主动发送消息。
服务器也可以主动向客户端推送数据。
所有消息都通过这个持续存在的连接传输,无需频繁建立连接。
关闭连接
当一方发送关闭信号时,连接被终止。
三、WebSocket与HTTP的区别
HTTP(传统方式)
一次请求 → 一次响应 → 连接关闭
必须客户端先发请求
适合:网页加载、接口请求
WebSocket(实时通信)
建立连接后一直保持
客户端和服务器都可以主动发消息
低延迟、高效率
WebSocket 的特点
双向通信(客户端 ⇄ 服务器)
实时性强(延迟低)
长连接(不会频繁断开)
减少开销(不需要反复建立连接)
| 对比 | HTTP | WebSocket |
|---|---|---|
| 通信方式 | 请求响应 | 双向通信 |
| 是否实时 | 差 | 强 |
| 连接方式 | 短连接 | 长连接 |
| 延迟 | 较高 | 很低 |
| 开销 | 大 | 小 |
| 开发复杂度 | 低 | 高 |
| 适合 | CRUD | 实时系统 |
四、WebSocket的优点和缺点
WebSocket 的优点
真正实时通信(最大优点)
HTTP:
客户端请求一次
服务器回复一次
WebSocket:
服务器可以主动发消息
例如:
聊天消息
机器人状态
游戏同步
都能实时推送。
双向通信
HTTP:
只能客户端主动请求
WebSocket:
客户端 ⇄ 服务端
双方都能主动发送
这是本质区别。
长连接(性能高)
HTTP 每次都:
建立TCP
发送
断开
WebSocket:
建立一次
一直保持
所以:
延迟低
CPU 消耗小
网络开销小
非常适合高频通信
比如:
位置更新 30 次
机器人每秒:
如果用 HTTP:
30 次请求
太浪费。
WebSocket:
一个连接持续发
效率极高。
支持二进制数据
不仅能传字符串。
还能传:
图片
音频
视频
protobuf
激光雷达数据
延迟非常低
因为:
不用频繁握手
所以特别适合:
游戏
机器人控制
实时监控
WebSocket的缺点
长连接占资源(最大缺点)
HTTP:
请求结束连接关闭
WebSocket:
连接一直存在
如果:
10万个客户端
服务器就要维护:
10万个 socket
这很吃:
内存
文件描述符
CPU
服务器开发复杂
HTTP:
请求 → 响应
很简单。
WebSocket:
需要处理:
长连接
心跳
重连
断线检测
并发
异步IO
复杂很多。
五、用C++写基础的WebSocket服务端和客户端
Websocket的服务端
server.cpp
cpp
#include <websocketpp/config/asio_no_tls.hpp>
#include <websocketpp/server.hpp>
#include <iostream>
typedef websocketpp::server<websocketpp::config::asio> server;
using websocketpp::connection_hdl;
class WebSocketServer
{
public:
WebSocketServer()
{
// 初始化 ASIO
m_server.init_asio();
// 设置消息回调
m_server.set_message_handler(
[this](connection_hdl hdl, server::message_ptr msg)
{
on_message(hdl, msg);
});
}
void run(uint16_t port)
{
// 监听端口
m_server.listen(port);
// 开始接受连接
m_server.start_accept();
std::cout << "WebSocket Server start at port "
<< port << std::endl;
// 启动服务
m_server.run();
}
private:
server m_server;
void on_message(connection_hdl hdl,
server::message_ptr msg)
{
std::cout << "收到消息: "
<< msg->get_payload()
<< std::endl;
// 回复客户端
m_server.send(hdl,
"服务器收到消息: " + msg->get_payload(),
websocketpp::frame::opcode::text);
}
};
int main()
{
WebSocketServer server;
server.run(9002);
return 0;
}
Websocket的客户端
Client.cpp
cpp
#include <websocketpp/config/asio_no_tls_client.hpp>
#include <websocketpp/client.hpp>
#include <iostream>
typedef websocketpp::client<websocketpp::config::asio_client> client;
using websocketpp::connection_hdl;
class WebSocketClient
{
public:
WebSocketClient()
{
m_client.init_asio();
// 连接成功回调
m_client.set_open_handler(
[this](connection_hdl hdl)
{
on_open(hdl);
});
// 消息回调
m_client.set_message_handler(
[this](connection_hdl hdl,
client::message_ptr msg)
{
on_message(hdl, msg);
});
}
void connect(const std::string& uri)
{
websocketpp::lib::error_code ec;
auto con = m_client.get_connection(uri, ec);
if (ec)
{
std::cout << "连接失败: "
<< ec.message()
<< std::endl;
return;
}
m_client.connect(con);
m_client.run();
}
private:
client m_client;
void on_open(connection_hdl hdl)
{
std::cout << "连接成功" << std::endl;
// 发送消息
m_client.send(hdl,
"你好,我是客户端",
websocketpp::frame::opcode::text);
}
void on_message(connection_hdl hdl,
client::message_ptr msg)
{
std::cout << "收到服务端消息: "
<< msg->get_payload()
<< std::endl;
}
};
int main()
{
WebSocketClient client;
client.connect("ws://localhost:9002");
return 0;
}