WebSocket 是一种在单个 TCP 连接上进行全双工通信的协议,适用于实时应用(如聊天、游戏、实时数据推送等)。以下是基于 C++ 实现 WebSocket 的核心步骤和示例代码:
1. 核心实现步骤
(1) 建立 TCP 服务端
- 使用 Socket API 监听端口(如
8080
)。 - 接受客户端连接,获取文件描述符(fd)。
(2) HTTP 升级到 WebSocket
- 客户端发送 HTTP Upgrade 请求:
GET /chat HTTP/1.1
Host: example.com
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==
Sec-WebSocket-Version: 13
- 服务端响应:
计算
Sec-WebSocket-Accept
:对客户端 Key + GUID258EAFA5-E914-47DA-95CA-C5AB0DC85B11
做 SHA1+Base64。 返回响应:
HTTP/1.1 Switching Protocols
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Accept: s3pPLMBiTxaQ9kYGzzhZRbK+xOo=
(3) WebSocket 数据帧解析
WebSocket 数据帧格式(RFC6455):
0 1 2 3
0 |1|2|3|4|5|6|7|8|9|0|1|2|3|4|
+-+-+-+-+-------+-+-------------+
FIN |RSV | OPCODE | MASKED? |
+-+-+-+-+-------+-+-------------+
Payload Length (7/16/64 bits)
Extended Payload Length (if needed)
Masking Key (4 bytes, if MASKED=1)
Payload Data (application data)
关键字段:
- OPCODE:
0x01
(文本)、0x02
(二进制)、0x08
(关闭连接)。 - MASKED:客户端消息必须掩码处理,服务端无需掩码。
2. C++ WebSocket Server Demo
以下是一个简化版实现(需自行处理错误检查和优化):
#include <iostream>
#include <string>
#include <sys/socket.h>
#include <netinet/in.h>
#include <unistd.h>
#include <openssl/sha.h>
#include <base64/base64.h> // Base64库需自行引入
// WebSocket握手响应生成函数
std::string generate_ws_accept(const std::string& client_key) {
const std::string magic = "258EAFA5-E914-47DA-95CA-C5AB0DC85B11";
std::string combined = client_key + magic;
unsigned char sha_hash[SHA_DIGEST_LENGTH];
SHA1((const unsigned char*)combined.c_str(), combined.size(), sha_hash);
return base64_encode(sha_hash, SHA_DIGEST_LENGTH);
}
int main() {
int server_fd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
sockaddr_in addr{AF_INET, htons(8080), INADDR_ANY};
bind(server_fd, (sockaddr*)&addr, sizeof(addr));
listen(server_fd, SOMAXCONN);
while (true) {
int client_fd = accept(server_fd, nullptr, nullptr);
char buffer[1024] = {0};
read(client_fd, buffer, sizeof(buffer));
// Step2: HTTP升级为WebSocket协议并回复握手包.
if (strstr(buffer,"Upgrade: websocket")){
//提取Sec - WebSoket - Key值.
std::string key(buffer);
size_t start=key.find("Sec - Websoket - Key : ") +19;
size_t end=key.find("\r\n",start);
std :: string client_key=key.substr(start,end-start);
//生成应答并发送HTTP101切换协议.
std :: string accept_key=generate_ws_accept(client_key);
std :: string response="HTTP /1 .1101 Switching Protocols\r\n"
"Upgrade : websoket\r\n"
"Connection : Upgrade\r\n"
"Sec - Websoket - Accept:"+accept_key+"\r\n\r\n";
send(client_fd ,response.c_str(),response.size(),MSG_NOSIGNAL);
//Step3 :处理后续的WS数据帧...
while(true){
uint8_t ws_header[14];//最大可能头部长度14字节.
recv(client_fd,&ws_header,sizeof(ws_header),MSG_PEEK);//偷看头部.
bool is_masked=(ws_header [l] &128)==128;//检查MASK位.
uint64_t payload_len=(uint8_t)( ws_header[l ] &127);//有效载荷长度.
if(payload_len ==126){//扩展16位长度.
payload_len=ntohs(*reinterpret_cast<uint16_t*>(& ws_header [2]));
}else if(payload_len ==127){//扩展64位长度.
payload_len=ntohll(*reinterpret_cast<uint64_t*>(& ws_header [2]));
}
//读取完整帧并处理掩码和数据...
uint32_t mask_key;
if(is_masked){
memcpy(&mask_key,& ws_header [payload_len >126 ?4 :2],sizeof(mask_key));//获取掩码键值.
}
...
}
}
close(client_fd);
}
}
3.推荐开源库
直接使用现有库更高效:
(a)轻量级方案
-Boost.Asio + Beast:参考文档
websocket::stream<tcp::socket> ws(ioc);
ws.accept(request); //自动完成握手和数据帧解析。
(b)高性能方案
-uWebSockets:GitHub
uWS::App().ws("/*", {...}).listen(9000);
4.Websocket关键点总结
协议升级 | 通过HTTP101状态码切换协议。 |
数据分帧 | 需按RFC6455解析掩码、长度和操作码。 |
心跳保活 | 定期发送Ping/Pong帧维持连接。 |
多线程优化 | 建议结合IO多路复用(epoll/kqueue)。 |
如果需要完整项目代码或深入某个环节,可以进一步讨论! 🚀
内容由零声教学AI助手提供,问题来源于学员提问