第 9 章:UDP
本章说明 datagram 边界、接收超时、退出流程以及 UDP 不提供的可靠性。
1. UDP 与 TCP 的核心差异
| UDP | TCP |
|---|---|
| 保留 datagram 边界 | 只提供字节流 |
| 无连接握手,可能丢包、乱序、重复 | 可靠、有序,但存在队头阻塞 |
| 一次 sendData 对应一个 datagram | 一次 send 不对应一次 recv |
| 适合遥测、发现、实时容忍丢包场景 | 适合需要可靠传输的协议 |
2. 完整 UDP Echo
#include <sttnet.h>
#include <atomic>
#include <iostream>
#include <thread>
int main()
{
using namespace stt::network;
using stt::system::ServerSetting;
if(!ServerSetting::blockTerminationSignals())
return 1;
// UDP socket 使用一秒接收超时,便于循环
// 在不使用危险异步 handler 的情况下检查退出标志。
UdpServer server(7070, false, 1, true);
if(server.getFD() < 0)
{
std::cerr << "failed to bind UDP port 7070\n";
return 2;
}
std::atomic<bool> running{true};
std::thread signalThread([&running] {
ServerSetting::waitForTerminationSignal();
running.store(false, std::memory_order_release);
});
std::cout << "UDP echo listening on 127.0.0.1:7070\n";
while(running.load(std::memory_order_acquire))
{
std::string data;
std::string peerIP;
int peerPort = 0;
const int received = server.recvData(data, 64 * 1024, peerIP, peerPort);
if(received == -100)
continue; // 接收超时:重新检查退出标志。
if(received <= 0)
continue;
// UDP 保留报文边界;把当前报文回发给发送端。
const int sent = server.sendData(data, peerIP, peerPort);
if(sent <= 0)
std::cerr << "failed to reply to " << peerIP << ':' << peerPort << '\n';
}
server.close();
signalThread.join();
return 0;
}3. 为什么 Demo 使用接收超时
UDP 示例是显式循环,不使用 STTNet Server Reactor。它把 SIGINT/SIGTERM 阻塞后交给一个同步等待线程,并把 Socket 接收超时设为 1 秒,让循环能定期检查退出标志。
4. 测试
./build/examples/sttnet_udp_echo
printf 'hello UDP' | nc -u -w 2 127.0.0.1 70705. 生产环境边界
- UDP 不保证 datagram 一定到达,也不保证只到达一次。
- 可靠性可由业务协议补充,例如序号、重试、去重与超时。
- 单个报文大小上限可降低 IP 分片和内存滥用风险。
- UDP 来源地址可能被伪造,不适合作为独立认证凭据。