Windows 平台下的 TCP 客户端

📅 2026/7/27 18:48:10 👁️ 阅读次数 📝 编程学习
Windows 平台下的 TCP 客户端

本次实操验证核心目标:Windows 平台 TCP 客户端,能够正常连通 Linux TCP 服务端

我们已经完整讲解过 TCP 套接字基础写法,配套实验也完成过 Linux 机器与 Linux 机器之间同平台 TCP 通信。

socket 套接字 - TCPhttps://rosetea.blog.csdn.net/article/details/147653994此前我们实践过 Windows UDP 客户端连通 Linux 服务端,同理,TCP 通信也必须实现 Windows 客户端跨平台连通 Linux 服务端;

Windows 平台下的 UDP 客户端https://rosetea.blog.csdn.net/article/details/163220666如果无法完成跨平台互通,这套套接字通信方案就不具备实用价值,所以本次专门带大家完整落地这套跨平台 TCP 通信流程。

一、Windows TCP 客户端代码通用固定框架说明

Windows 下 Winsock 套接字基础流程和之前 UDP 完全一致,核心固定步骤没有任何区别:

  1. 引入头文件<winsock2.h>

  2. 通过#pragma comment(lib, "ws2_32.lib")链接 Winsock 库

  3. 调用WSAStartup()初始化 Winsock 版本环境,接收WSADATA版本信息

  4. 程序结束时调用WSACleanup()释放网络资源

头文件内置核心类型与函数

名称作用说明
WSADATA存储 Winsock 库初始化返回的系统信息
SOCKET套接字句柄,唯一标识一个网络套接字
sockaddr_inIPv4 专用地址结构体,存放 IP、端口等地址信息
socket()创建套接字对象
bind()将套接字绑定本地 IP 与端口
listen()把套接字切换为监听模式,等待客户端连接
accept()接收客户端连接,返回专用通信套接字

WSAStartup

函数定位:Windows 套接字库初始化函数,所有 Socket 相关 API 调用前必须优先执行,是 Windows 网络程序入口初始化步骤。

参数说明

  • wVersionRequested:指定程序需要使用的 Winsock 版本,固定搭配宏MAKEWORD(主版本, 次版本),示例代码使用MAKEWORD(2,2)代表 Winsock 2.2 版本;

  • lpWSAData:WSADATA 结构体指针,用于接收系统返回的初始化详情。

返回值规则

  • 返回0:初始化成功,可正常使用 Socket 接口;

  • 非 0 数字:初始化失败,数字为对应错误码。

核心功能:告知操作系统当前程序要使用指定版本的 Winsock 网络库,完成库资源初始化;初始化完成后,程序才能调用地址解析、连接、收发数据等全套网络功能,实现应用层与系统 TCP/IP 协议栈交互。

配套释放接口:程序不再使用 Socket 功能时,必须调用WSACleanup():解除程序与 Winsock 库绑定,释放系统占用的网络资源,避免内存 / 句柄泄漏。

本次重点对比:Windows 平台 TCP Client 代码 和 Linux 平台原生 TCP Client 代码的区别

结论:二者差异极小,99% 代码逻辑、流程完全一致。

1. 套接字创建流程

前置初始化完成后,直接固定写法创建套接字:

socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
  • 地址族参数:AF_INET

  • 套接字类型:SOCK_STREAM(面向连接 TCP 流)

  • 协议参数:可填0自动适配,也可显式填写IPPROTO_TCP,两种写法均可

Linux 平台套接字句柄为普通int类型;Windows 平台专用类型SOCKET,创建失败时会返回内置宏INVALID_SOCKET,程序中需要做失败判断处理。

2. 填充服务端地址与连接逻辑

客户端需要手动填充服务端端口、IP 地址信息,使用结构体sockaddr_in; 调用connect()发起连接,传入强制转换后的套接字地址指针:

connect(clientSocket, (SOCKADDR *)&serverAddr, sizeof(serverAddr));

Windows 平台相关类型、宏名称大多为大写(如SOCKADDRSOCKET),底层对应 Linux 里小写的sockaddr、文件描述符,仅语法标识区分,逻辑完全相同。

连接返回SOCKET_ERROR代表连接失败,需要关闭套接字、清理 Winsock 资源后退出程序。

3. 循环收发数据逻辑

建立连接后进入业务主循环,完整流程:

获取控制台用户输入,空输入直接跳过本次循环

调用send()向服务端发送数据:参数依次为套接字句柄、消息字符串、消息字节长度、发送标识0

定义buffer缓冲区,调用recv()读取服务端回传数据:参数为套接字、缓冲区、缓冲区长度减 1、读取标识0

判断recv()返回值:

  • 返回值 > 0:读取到有效数据,手动给缓冲区末尾追加\0,转为标准字符串打印服务端回显内容

  • 返回值≤0:读取失败、连接断开,打印错误提示

整体收发逻辑,和之前 UDP 客户端、Linux 原生 TCP 客户端完全统一,没有额外复杂逻辑,此处不重复展开。

完整客户端代码

#include <winsock2.h> #include <ws2tcpip.h> // 新增,用于inet_pton #include <iostream> #include <string> #pragma warning(disable : 4996) #pragma comment(lib, "ws2_32.lib") std::string serverip = "113.132.212.215"; // 替换为你的云服务器ip uint16_t serverport = 8888; // 填写你的云服务开放的端口号 int main() { WSADATA wsaData; int result = WSAStartup(MAKEWORD(2, 2), &wsaData); if (result != 0) { std::cerr << "WSAStartup failed: " << result << std::endl; return 1; } SOCKET clientSocket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); if (clientSocket == INVALID_SOCKET) { std::cerr << "socket failed" << std::endl; WSACleanup(); return 1; } sockaddr_in serverAddr{}; serverAddr.sin_family = AF_INET; serverAddr.sin_port = htons(serverport); // 替换废弃的inet_addr,使用安全的inet_pton if (inet_pton(AF_INET, serverip.c_str(), &serverAddr.sin_addr) <= 0) { std::cerr << "IP地址格式错误" << std::endl; closesocket(clientSocket); WSACleanup(); return 1; } result = connect(clientSocket, (SOCKADDR *)&serverAddr, sizeof(serverAddr)); if (result == SOCKET_ERROR) { std::cerr << "connect failed" << std::endl; closesocket(clientSocket); WSACleanup(); return 1; } bool runFlag = true; while (runFlag) { std::string message; std::cout << "Please Enter@ "; std::getline(std::cin, message); if(message.empty()) continue; // 增加send发送结果校验 int sendRet = send(clientSocket, message.c_str(), message.size(), 0); if (sendRet == SOCKET_ERROR) { std::cerr << "send 发送失败,连接断开" << std::endl; break; } char buffer[1024] = {0}; int bytesReceived = recv(clientSocket, buffer, sizeof(buffer) - 1, 0); if (bytesReceived > 0) { buffer[bytesReceived] = '\0'; std::cout << "Received from server: " << buffer << std::endl; } else { // recv返回0=服务端关闭连接;返回负数=网络异常 std::cerr << "recv failed,服务端已断开连接" << std::endl; runFlag = false; } } // 循环退出后正常释放资源 closesocket(clientSocket); WSACleanup(); std::cout << "客户端正常退出" << std::endl; return 0; }

二、实操验证完整流程

阶段 1:提前启动 Linux TCP Echo 服务端

我们提前准备好配套 TCP 服务端源码,端口统一使用8888。 服务端本质是标准 Echo 回显服务器,核心逻辑:

  • 创建套接字 → bind 绑定端口 → listen 开启监听

  • 通过accept()阻塞等待客户端新连接,获取专属通信套接字、客户端 IP 与端口信息

  • 循环读取客户端发送的数据,读完后原样写回客户端(echo 回显)

服务端代码基础流程之前文章已经完整,本次不再细读源码,直接启动 Linux 服务端程序,保持后台监听状态。

阶段 2:VS20XX 创建 Windows TCP 客户端工程实操

打开 Visual Studio,新建空 C++ 项目,自定义项目名称(示例:test)

右键项目添加新建源文件,命名为tcp_client.cpp/tcp_client.cc均可

复制课件内完整 Windows TCP Client 代码粘贴至源文件,注意区分 TCP、UDP 代码,避免粘贴错误

修改代码内两处关键配置:

  • serverip:填写你自己的云服务器公网 IP(演示使用讲师云服务器 IP,学员需替换为自身机器地址)

  • serverport:固定 8888,与 Linux 服务端监听端口保持一致

阶段 3:编译运行、双向通信测试

代码配置完成后,按下F5启动客户端程序:

程序启动自动执行WSAStartup、创建套接字、调用connect连接 Linux 服务端

Linux 服务端日志会立刻打印新接入客户端的公网 IP、随机临时端口,代表跨平台 TCP 连接建立成功

在 Windows 客户端控制台输入任意文本消息,例如hello你好在吗等:

  • Windows 客户端通过 TCP 将数据发送至 Linux 服务端

  • Linux 服务端读取数据后原样回传

  • Windows 客户端recv()读取回显内容并打印,双向通信验证完成

IP 地址、端口开放、防火墙放行等注意事项,UDP 相关中已经重点强调,本次不再重复说明。

三、核心断线检测逻辑:客户端退出,服务端自动感知

TCP 协议具备连接状态感知能力,这是本篇重点补充的关键特性: Windows 客户端主动关闭程序 / 断开套接字时,Linux 服务端执行recv()读取操作会直接返回0,服务端可以精准识别客户端下线。

服务端断线处理逻辑说明

服务端处理新连接的循环中,对recv()返回值做分支判断:

  • 返回值 > 0:正常读取到客户端消息,执行 echo 回显

  • 返回值≤0:判定客户端主动断开连接,打印日志格式:[客户端IP:端口] client quit

演示效果:关闭 Windows 客户端程序后,Linux 服务端控制台立刻输出对应客户端退出日志,证明跨平台 TCP 连接断开事件可被两端正常识别。

四、实操总结

本次完整实操验证了:基于 Winsock 实现的 Windows TCP 客户端,能够和 POSIX 标准 Linux TCP 服务端完成稳定跨平台 TCP 双向通信,连接建立、数据收发、断线检测全流程均可正常运行。

Windows 与 Linux 套接字仅初始化、资源释放、类型标识存在少量平台专属差异,通信核心逻辑完全通用,大家后续开发跨平台 TCP 程序可直接复用这套代码框架。