当前位置: 首页 > news >正文

C++ 网络服务端实战:TCP + UDP + WebSocket 三协议支持

本文详细介绍如何使用纯 C++ 实现一个支持 TCP、UDP、WebSocket 三种协议的网络服务端与客户端。无需第三方库,从零实现 WebSocket 握手协议和 SHA1 哈希算法,适合网络编程学习者参考。

## 一、项目简介

本项目是一个基于 C++17 的高性能网络通信框架,支持以下三种通信协议:

- **TCP**:面向连接的可靠传输协议

- **UDP**:无连接的高速传输协议

- **WebSocket**:全双工通信协议,支持浏览器连接

### 技术特点

- ✅ 纯 C++ 实现,无第三方依赖

- ✅ 支持多客户端并发连接

- ✅ 完整的 WebSocket 握手协议实现

- ✅ 内置 SHA1 哈希和 Base64 编码

- ✅ 跨平台支持(Linux/macOS/Windows)

- ✅ 回调函数机制,易于集成

---

## 二、项目结构

```

game_server/

├── CMakeLists.txt # CMake 构建配置

├── include/

│ ├── tcp_server.h # TCP 服务端头文件

│ ├── tcp_client.h # TCP 客户端头文件

│ ├── udp_server.h # UDP 服务端头文件

│ ├── udp_client.h # UDP 客户端头文件

│ ├── websocket_server.h # WebSocket 服务端头文件

│ └── websocket_client.h # WebSocket 客户端头文件

├── src/

│ ├── main.cpp # 统一入口

│ ├── tcp/

│ │ ├── tcp_server.cpp

│ │ └── tcp_client.cpp

│ ├── udp/

│ │ ├── udp_server.cpp

│ │ └── udp_client.cpp

│ └── websocket/

│ ├── websocket_server.cpp

│ └── websocket_client.cpp

└── build/

└── game_server # 编译后的可执行文件

```

## 三、核心实现

### 3.1 TCP 服务端实现

```cpp

boolTcpServer::start(conststd::string& host,intport) {

server_fd_ = socket(AF_INET, SOCK_STREAM, 0);

// 设置地址复用

intopt = 1;

setsockopt(server_fd_, SOL_SOCKET, SO_REUSEADDR, &opt,sizeof(opt));

// 绑定地址

structsockaddr_in address {};

address.sin_family = AF_INET;

address.sin_addr.s_addr = inet_addr(host.c_str());

address.sin_port = htons(port);

bind(server_fd_, (structsockaddr*)&address,sizeof(address));

listen(server_fd_, 10);

// 启动接受线程

accept_thread_ = std::thread(&TcpServer::acceptLoop,this);

returntrue;

}

```

### 3.2 WebSocket 握手实现

WebSocket 协议的关键是握手过程,需要计算 `Sec-WebSocket-Accept`:

```cpp

std::string computeAcceptKey(conststd::string& client_key) {

// 拼接 GUID

std::string concat = client_key + "258EAFA5-E914-47DA-95CA-C5AB0DC85B11";

// 计算 SHA1 哈希

std::string hash_hex = computeSHA1(concat);

// 转换为字节并 Base64 编码

std::vector<uint8_t> hash_bytes;

for(size_t i = 0; i < hash_hex.length(); i += 2) {

uint8_t byte =static_cast<uint8_t>(

std::stoul(hash_hex.substr(i, 2),nullptr, 16)

);

hash_bytes.push_back(byte);

}

returnbase64Encode(hash_bytes.data(), hash_bytes.size());

}

```

### 3.3 SHA1 哈希算法实现

```cpp

std::string computeSHA1(conststd::string& data) {

uint32_t h0 = 0x67452301, h1 = 0xEFCDAB89, h2 = 0x98BADCFE,

h3 = 0x10325476, h4 = 0xC3D2E1F0;

// 填充消息

std::vector<uint8_t> msg(data.begin(), data.end());

uint64_t bit_len = msg.size() * 8;

msg.push_back(0x80);

while((msg.size() % 64) != 56) {

msg.push_back(0x00);

}

// 添加长度信息

for(inti = 7; i >= 0; i--) {

msg.push_back((bit_len >> (i * 8)) & 0xFF);

}

// 处理每个 512 位块

for(size_t chunk = 0; chunk < msg.size(); chunk += 64) {

// ... 80 轮迭代计算

}

return哈希结果;

}

```

### 3.4 WebSocket 帧编解码

```cpp

// 编码 WebSocket 帧

std::string encodeFrame(conststd::string& data) {

std::vector<uint8_t> frame;

frame.push_back(0x81); // Text frame, FIN=1

size_t len = data.size();

if(len < 126) {

frame.push_back(static_cast<uint8_t>(len));

}elseif(len < 65536) {

frame.push_back(126);

frame.push_back((len >> 8) & 0xFF);

frame.push_back(len & 0xFF);

}

for(charc : data) {

frame.push_back(static_cast<uint8_t>(c));

}

returnstd::string(frame.begin(), frame.end());

}

```

---

## 四、编译与运行

### 4.1 编译步骤

```bash

# 创建构建目录

mkdir -p build &&cdbuild

# 运行 CMake 配置

cmake ..

# 编译

make -j4

```

### 4.2 启动服务端

```bash

# TCP 服务端(端口 8088)

./build/game_server tcp_server 8088

# UDP 服务端(端口 9000)

./build/game_server udp_server 9000

# WebSocket 服务端(端口 8081)

./build/game_server ws_server 8081

```

### 4.3 启动客户端

```bash

# TCP 客户端

./build/game_server tcp_client 127.0.0.1 8088

# UDP 客户端

./build/game_server udp_client 127.0.0.1 9000

# WebSocket 客户端

./build/game_server ws_client 127.0.0.1 8081 /

```

---

## 五、测试验证

### 5.1 TCP 测试结果

**服务端输出:**

```

TCP Server started on 0.0.0.0:8088

Client 1 connected from 127.0.0.1:64794

[TCP] Received from client 1: Hello TCP Server #1

[TCP] Received from client 1: Hello TCP Server #2

```

**客户端输出:**

```

TCP Client connected to 127.0.0.1:8088

[TCP Client] Sending: Hello TCP Server #1

[TCP Client] Received: Server echo: Hello TCP Server #1

```

### 5.2 WebSocket 测试结果

**服务端输出:**

```

WebSocket Server started on 0.0.0.0:8081

Client 1: Handshake completed, key=mk3PP+a3yQeW0baCOSrhwQ==

[WS] Received from client 1: Hello WebSocket Server #1

```

**客户端输出:**

```

WebSocket Client connected to 127.0.0.1:8081/

[WS Client] Received: Server echo: Hello WebSocket Server #1

```

---

## 六、常见问题解决

### 6.1 端口被占用

```

Failed to bind: Address already in use

```

**解决方案:** 更换端口号或查找占用进程

```bash

# 查看端口占用

lsof -i :8080

# 杀死占用进程

kill -9 <PID>

```

### 6.2 WebSocket 握手失败

确保 `Sec-WebSocket-Accept` 计算正确:

1. 拼接 client_key 和 GUID

2. 计算 SHA1 哈希(十六进制字符串)

3. 将十六进制转换为字节

4. Base64 编码

### 6.3 UDP 收不到数据

UDP 客户端调用 `connect()` 后应使用 `write()` 而非 `sendto()`:

```cpp

// 正确做法

intbytes_sent = write(socket_fd_, data.c_str(), data.size());

```

## 七、扩展建议

1. **添加 SSL/TLS 支持**:使用 OpenSSL 实现 WSS

2. **线程池优化**:使用线程池管理客户端连接

3. **异步 IO**:使用 epoll/kqueue 提高并发性能

4. **协议扩展**:支持二进制帧、ping/pong 心跳

5. **日志系统**:集成 spdlog 等日志库

**参考资料:**

- [RFC 6455 - The WebSocket Protocol](https://tools.ietf.org/html/rfc6455)

- [Beej's Guide to Network Programming](https://beej.us/guide/bgnet/)

- [C++ Network Programming](https://www.amazon.com/ACE-Development-Experience-Addison-Wesley-Professional/dp/0321533690)

**关注公众号【技术前沿】,获取更多 C++ 和网络编程干货!**

http://www.jsqmd.com/news/543077/

相关文章:

  • 别再死磕代码了!用AutoSAR-CP/AP分层架构,让你的汽车软件开发效率翻倍
  • 抖音视频批量下载器:3分钟掌握高效内容收集技术
  • 4个维度构建无缝远程体验:Sunshine跨设备串流全指南
  • 在线几何画板推荐:不用下载的几何作图软件
  • 情侣宅家过节外卖仪式感营造全指南 - 速递信息
  • tcp与udp的区别
  • 快速处理山东一卡通回收:详细流程和建议 - 团团收购物卡回收
  • 极好锁相环电路设计,fractional -N PLL,2.4G用于蓝牙,模拟集成电路设计
  • Python并发面试挂点TOP3:GIL本质、无锁替代方案、真实QPS压测结果(附可复现benchmark代码)
  • FUTURE POLICE语音模型Python爬虫数据播报:实时舆情语音监控系统
  • 情侣宅家下午茶外卖点单全攻略|不踩雷搭配+省钱技巧汇总 - 速递信息
  • 智能穿搭-技术实现
  • ImageGlass:革新Windows图像浏览体验的智能解决方案
  • 【数据结构与算法】第8篇:线性表(四):双向链表与循环链表
  • s2-pro企业应用方案:批量语音生成+音色统一管理生产环境实践
  • 减脂期外卖点单全攻略:控热量、低负担、高性价比指南 - 速递信息
  • 碳化硅石墨坩埚哪家强?2026年口碑厂家深度剖析,技术好的坩埚产品怎么选择百顿坩埚引领行业标杆 - 品牌推荐师
  • 国内顶级期刊
  • C++虚函数陷阱
  • 基于springboot的旅游景点门票信息系统设计与实现-vue
  • Navicat连接密码的AES-CBC加解密实战
  • RimWorld开局定制神器:EdB Prepare Carefully深度应用指南
  • TMS320F28P550SJ9实战解析:Sysconfig高效配置SCI多处理器通信模式
  • Gemini提示词反推教程!“图生图”来了
  • 如何解决CH340串口转USB设备断开连接后重连提示Unable to set the serial port state的问题
  • 朋友圈发图像素太低,被吐槽像座机拍的。调整照片像素,再也不怕被嘲。
  • 3个技巧快速掌握Mermaid在线编辑器:免费制作专业图表终极指南
  • OpenClaw备份策略:Qwen3.5-9B配置与技能的安全保存
  • Python将Parquet文件转换为JSONL格式文件
  • 多代同堂家庭外卖点单指南:宝妈实用备注技巧+全口味适配方案 - 速递信息