STTNet 0.7.0

第 9 章:UDP

本章说明 datagram 边界、接收超时、退出流程以及 UDP 不提供的可靠性。

1. UDP 与 TCP 的核心差异

UDPTCP
保留 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 7070

5. 生产环境边界

  • UDP 不保证 datagram 一定到达,也不保证只到达一次。
  • 可靠性可由业务协议补充,例如序号、重试、去重与超时。
  • 单个报文大小上限可降低 IP 分片和内存滥用风险。
  • UDP 来源地址可能被伪造,不适合作为独立认证凭据。