Windows 平台下的 UDP 客户端

本次核心目标:验证Windows 平台 UDP 客户端 ,能够正常向Linux 平台 UDP 服务端发送、接收消息,完成跨系统 UDP 双向通信。

之前文章的实操、测试,都是使用本地 Linux 客户端,连接我们自己提供的 UDP 服务端。

但实际开发、日常使用场景里,Windows 才是客户端最主流的运行载体,因此我们必须验证:Windows 编写的 UDP 套接字代码,能够和 Linux 服务端正常连通,这是我们的核心验证目标。

在正式写代码、做测试前,先给大家铺垫底层网络原理: 不同操作系统(Windows、Linux)底层的TCP/IP 协议栈标准完全统一 ,UDP、TCP、IP 协议都内置在操作系统内核中,这是跨系统通信的基础。 各系统的驱动、进程管理等模块差异很大,但网络编程接口、系统调用逻辑高度一致;仅存在少量平台专属特殊 API,整体差别很小。下面我们直接进入实操验证环节。

我已经提前给大家准备好整套 UDP 实验代码,分为两部分:

Linux UDP 服务端:代码已提前就绪,我们先在 Linux 服务器启动服务端程序,监听端口等待客户端接入;

UDP 服务端https://rosetea.blog.csdn.net/article/details/147465441Windows UDP 客户端 :配套客户端代码提前编写完成,接下来我们逐行阅读代码,区分跨平台代码的通用逻辑Windows 专属差异点

cpp 复制代码
#include <iostream>
#include <cstdio>
#include <thread>
#include <string>
#include <cstdlib>
#include <WinSock2.h>
#include <Windows.h>

#pragma warning(disable : 4996)
#pragma comment(lib, "ws2_32.lib")

std::string server_ip = "192.168.10.129";
uint16_t server_port = 8888;

int main() {
    WSADATA wsd;
    // 初始化Winsock
    if (WSAStartup(MAKEWORD(2, 2), &wsd) != 0)
    {
        std::cout << "WSAStartup 初始化失败" << std::endl;
        return 1;
    }

    struct sockaddr_in server;
    memset(&server, 0, sizeof(server));
    server.sin_family = AF_INET;
    server.sin_port = htons(server_port);
    server.sin_addr.s_addr = inet_addr(server_ip.c_str());

    SOCKET sockfd = socket(AF_INET, SOCK_DGRAM, 0);
    if (sockfd == SOCKET_ERROR)
    {
        std::cout << "socket 创建失败, 错误码:" << WSAGetLastError() << std::endl;
        WSACleanup();
        return 1;
    }

    std::string message;
    char buffer[1024];
    while (true)
    {
        std::cout << "Please Enter@ ";
        std::getline(std::cin, message);
        if (message.empty())
            continue;
        // 增加退出逻辑:输入exit关闭程序
        if (message == "exit")
            break;

        // 发送UDP报文
        int sendRet = sendto(sockfd, message.c_str(), (int)message.size(), 0, (struct sockaddr*)&server, sizeof(server));
        if (sendRet == SOCKET_ERROR)
        {
            std::cout << "sendto 发送失败,错误码:" << WSAGetLastError() << std::endl;
            continue;
        }

        struct sockaddr_in temp;
        int len = sizeof(temp);
        // 等待服务端回包
        int s = recvfrom(sockfd, buffer, 1023, 0, (struct sockaddr*)&temp, &len);
        if (s > 0)
        {
            buffer[s] = '\0';
            std::cout << "服务端返回: " << buffer << std::endl;
        }
        else if (s == 0)
        {
            std::cout << "对方关闭连接" << std::endl;
        }
        else
        {
            // 无数据不报错,UDP无回包属于正常情况
            // std::cout << "未收到回包" << std::endl;
        }
    }

    // 正常释放资源
    closesocket(sockfd);
    WSACleanup();
    return 0;
}

Windows 与 Linux 套接字通用代码逻辑

定义填充 struct sockaddr_in 结构体,写入目标 Linux 服务器 IP、监听端口,示例默认端口 8888(需要和 Linux 服务端绑定端口保持统一);

创建 UDP 套接字:调用 socket(AF_INET, SOCK_DGRAM, 0),返回套接字标识; Windows 中套接字类型为 SOCKET(本质是整数,是 Windows 对 Linux 文件描述符的封装);如果创建失败,会返回 SOCKET_ERROR 错误标识;

客户端主循环收发逻辑:

  • 从控制台读取输入消息,空输入直接跳过不发送;

  • 调用 sendto() 发送数据:参数包含套接字、消息字符串、消息长度、发送标识、服务端地址结构体;

  • UDP 客户端无需手动 bind 绑定本地端口,首次发送报文时系统会自动分配临时端口;

服务端回包接收逻辑: 定义 struct sockaddr_in 存储对端地址,获取地址长度,调用 recvfrom() 读取服务端返回数据,同时拿到服务端 IP 与端口信息;接收缓冲区收到数据后手动补字符串结束符 \0,打印输出。

sendto()recvfrom() 两个核心收发接口,参数定义、使用流程和 Linux 平台完全相同,数据处理逻辑无区别,这部分不再重复讲解。

Windows 套接字专属差异化代码

Windows 编写套接字程序,和 Linux 存在三处核心差异,全部是平台专属固定写法:

差异 1:头文件与依赖库引入

WinSock2.h 是 Windows Sockets API 专用头文件,用于 Windows 平台网络编程,包含 Windows Sockets 2 所需全部数据类型、函数声明、结构体定义,是 Windows 套接字开发必备头文件。

配套依赖库:ws2_32.lib 程序编译链接阶段必须引入该库,否则运行时会找不到 Winsock 相关 API 函数,代码中通过 #pragma comment(lib, "ws2_32.lib") 自动链接。

WinSock2.h 核心类型与函数

  • WSADATA:结构体,存储 Winsock 库初始化后的返回信息;

  • SOCKET:套接字句柄类型,唯一标识程序内的网络套接字;

  • sockaddr_in:IPv4 专用地址结构体,存放 IP、端口、地址族等信息;

  • socket():创建套接字;

  • bind():将套接字绑定本地 IP 与端口;

  • listen():TCP 服务端专用,设置套接字监听客户端连接;

  • accept():TCP 服务端专用,接收客户端连接并返回通信套接字。

所以 Linux 网络编程依赖 <sys/socket.h><netinet/in.h> 等系统头文件;Windows 不提供这类头文件,必须做两件事:

  • 引入头文件:<WinSock2.h><Windows.h>,其中 <WinSock2.h> 是 Windows 套接字 API 核心头文件;

  • 链接静态库:预处理指令 #pragma comment(lib, "ws2_32.lib"),VS 环境默认自带该库,这条语句为固定写法,作用是编译时自动链接套接字依赖库;

  • 屏蔽安全警告:#pragma warning(disable : 4996),VS 会默认判定 cinscanf 等 IO 接口不安全,该语句直接关闭 4996 号警告,不影响程序功能。

头文件与库文件一一对应,缺一不可,是 Windows 套接字程序运行的基础。

差异 2:Winsock 库初始化 WSAStartup()

所有 Windows 套接字 API 调用前,必须先执行该函数,负责初始化 Winsock 网络库。

参数说明

  • wVersionRequested:指定需要使用的 Winsock 版本,常用宏 MAKEWORD(主版本, 次版本),示例 MAKEWORD(2,2)

  • lpWSADataWSADATA 结构体指针,接收初始化返回的库信息。

**返回值:**调用成功返回 0;非 0 值代表初始化失败,无法使用网络接口。

**作用:**告知操作系统当前程序使用的 Winsock 版本,完成网络库初始化,初始化后才能调用地址转换、套接字收发、连接管理等网络功能,实现程序与底层网络协议栈交互。

Linux 无需手动初始化网络库;Windows 使用套接字 API 前,必须手动指定 Winsock 库版本并初始化:

  • 定义 WSADATA 结构体,用于接收库初始化返回信息;

  • 调用 WSAStartup(MAKEWORD(2, 2), &wsd):指定使用 Winsock 2.2 版本,编译器会自动在 ws2_32.lib 中匹配对应版本库; 所有套接字收发操作,必须在 WSAStartup() 初始化成功后执行。

差异 3:套接字关闭与资源释放
  • 套接字关闭:Linux 使用 close() 关闭文件描述符;Windows 专用接口 closesocket() 关闭套接字句柄;

  • 库资源清理:程序全部网络操作结束后,调用 WSACleanup(),释放 WSAStartup() 加载的 Winsock 库资源,避免句柄、内存泄漏。

总结:Windows UDP 套接字编程,99% 业务逻辑和 Linux 通用,仅需额外处理头文件、库链接、库初始化、资源释放 四组 Windows 专属代码。课件中已经对 WinSock2.hWSADATASOCKET 等类型做完整注释说明,大家课后自行查阅,课堂不再逐字朗读。

实操:

打开 VS2022,新建空项目 ,项目名称自定义,示例命名 test_udp_client

在项目「源文件」目录,新建 C++ 源文件,命名 udp_client.cc

将上面准备好的 Windows UDP 客户端完整代码粘贴至文件内;

修改代码关键参数:

  • serverip 变量:填写你自己的云服务器公网 IP

  • serverport 保持 8888,和 Linux 服务端监听端口统一;

保存代码,按下 F5 编译运行客户端程序。

Linux 端提前启动 UDP echo 服务端程序:./udp_server 8888

Windows 客户端启动后,控制台提示输入消息,输入内容(例如 "你好")发送;

双向通信现象:

  • Windows 客户端:收到 Linux 服务端原样回显的消息;

  • Linux 服务端:打印收到的消息,同时输出 Windows 客户端对应的公网 IP、临时端口号; 由此证明:Windows 客户端与 Linux 服务端双向 UDP 通信完全正常。

bash 复制代码
[2026-07-27 02:44:43] [INFO] [26081] [../Common/UdpServer.hpp] [37] - create socket success, sockfd = 3
[2026-07-27 02:44:43] [INFO] [26081] [../Common/UdpServer.hpp] [61] - bind socket success, port = 8888
[2026-07-27 02:45:03] [DEBUG] [26081] [../Common/UdpServer.hpp] [80] - peer_ip = 192.168.10.1, buffer = 你好

细节 1:中文消息编码显示不一致问题

Windows 控制台、Linux 终端默认字符编码不同:Windows 多为 GBK 编码,Linux 系统多为 UTF-8 编码。 发送中文 "你好" 时,Windows 本地回显正常,但 Linux 服务端打印中文会出现乱码。 注意:乱码仅为终端显示问题,不影响数据传输。服务端仅原样转发数据,原始字节完整传输,客户端接收后仍可正常展示中文,无需修改传输逻辑。

细节 2:服务端打印的客户端 IP 不是本机局域网 IP

有同志会疑惑:在 Windows CMD 执行 ipconfig 查到的本机局域网 IP,和 Linux 服务端打印的客户端 IP 不一致,这里给大家简单解释原理:

bash 复制代码
Ethernet adapter VMware Network Adapter VMnet8:

   Connection-specific DNS Suffix  . :
   Link-local IPv6 Address . . . . . : fe80::3f62:3c5d:5be0:97f8%6
   IPv4 Address. . . . . . . . . . . : 192.168.10.1
   Subnet Mask . . . . . . . . . . . : 255.255.255.0
   Default Gateway . . . . . . . . . :

Wireless LAN adapter WLAN:

   Connection-specific DNS Suffix  . :
   IPv6 Address. . . . . . . . . . . : 2409:8a34:4e32:86e0:bcae:d879:265d:f95f
   Temporary IPv6 Address. . . . . . : 2409:8a34:4e32:86e0:99a4:400e:119e:e46a
   Link-local IPv6 Address . . . . . : fe80::47a9:cd49:ac0c:71ed%17
   IPv4 Address. . . . . . . . . . . : 192.168.1.22
   Subnet Mask . . . . . . . . . . . : 255.255.255.0
   Default Gateway . . . . . . . . . : fe80::1%17
                                       192.168.1.1
  • 我们家用、校园网环境下,Windows 主机处于局域网,内网 IP 仅局域网内生效;

  • 报文向外网云服务器发送时,会经过运营商(移动 / 联通 / 电信)网关、出口路由器做 NAT 地址转换;

  • Linux 云服务器收到报文时,记录的是运营商出口公网 IP,而非 Windows 本机局域网 IP;

  • 运营商网关会记录内网主机与公网端口映射关系,服务端回包可通过该映射原路传回 Windows 客户端。

现阶段大家只需记住:二者 IP 不一致属于正常现象,后续接触到 NAT、内网穿透、代理服务器时,会完整拆解该机制。

本次实验完成验证:Windows UDP 客户端可以正常向 Linux 服务端发起 UDP 数据交互。 现实业务场景举例:直播平台使用 UDP 传输实时数据流,后端服务全部部署在 Linux 服务器,我们日常使用 Windows 电脑观看直播、发送弹幕,底层就是本次验证的跨平台 UDP 通信逻辑。

很多同志复制同款代码可能依旧无法连通,90% 原因是云服务器端口未放行 : 云服务器默认安全组、防火墙会拦截所有未主动开放端口,示例使用端口 8888,需要前往阿里云 / 腾讯云 / 华为云后台,找到「安全组」配置,手动放行 8888 端口 入站流量。大家可以自行搜索引擎查询云服务器安全组配置教程,建议大家独立尝试操作。

相关推荐
磨十三8 小时前
U-Boot
arm开发·嵌入式硬件
笔夏8 小时前
【移芯平台CAT1模组】烧写固件
stm32·单片机·学习
十月的皮皮8 小时前
STM32从零到量产开发:四路继电器工业控制模块开发— 继电器模块bsp_di.h与 bsp_di.c文件
c语言·stm32·stm32cubemx·hal库
无垠的广袤8 小时前
【RA-Eco-RA4M2开发板】步进电机的串口控制和 LabVIEW 数据采集
单片机·嵌入式硬件·labview
茯苓gao10 小时前
嵌入式开发笔记:QSerialPort 完整使用指南——从基础 API 到工程实战
笔记·嵌入式硬件·qt·学习
wuyk55510 小时前
67.嵌入式C语言进阶:结构体指针实战指南——STM32外设、传感器数据访问的高效技巧
c语言·开发语言·stm32·单片机
Mr+范10 小时前
单片机 ADC 电压与电流检测及电阻选型指南
单片机·嵌入式硬件
西城微科方案开发11 小时前
LCD智能婴儿秤方案
单片机·嵌入式硬件
三佛科技-1341638421212 小时前
灭蚊灯MCU方案开发,FT60E112-RB作为灭蚊灯主控芯片优势
单片机·嵌入式硬件·物联网·智能家居·pcb工艺