目录
- 一、前言
- [二、Windows 网络编程环境](#二、Windows 网络编程环境)
- [三、TCP Server 通信流程](#三、TCP Server 通信流程)
- 四、完整代码实现
- 五、增加回复计数功能
- 六、总结
- 七、结尾
一、前言
大家好,这里是 Hello_Embed 。前两篇我们学习了网络通信的基本概念和常用 API,本篇将正式进入实战,编写一个完整的 TCP Server 程序。通过这个例子,你将理解 TCP 通信的完整流程,为后续编写客户端程序打下基础。
在开始编程之前需要事先安装好 VS Code 或 Visual Studio。
二、Windows 网络编程环境
对于 Windows 网络编程,需要使用特定的头文件和库文件。
必要的头文件和库配置:
cpp
#include <WinSock2.h>
#include <ws2tcpip.h>
#pragma comment(lib,"ws2_32.lib")
#define close closesocket
初始化 WinSock:
cpp
WSADATA wsa;
if (WSAStartup(MAKEWORD(2, 2), &wsa) != 0) {
return -1; // 初始化失败
}
WSAStartup必须在使用任何网络函数之前调用,用于初始化 Windows Socket 库。
三、TCP Server 通信流程
按照 TCP 通信流程,Server 端需要经过以下步骤:
1. open socket(socket)
创建套接字,返回文件描述符。
cpp
int iSocketServer = (int)socket(AF_INET, SOCK_STREAM, 0);
// AF_INET: IPv4 协议族
// SOCK_STREAM: TCP 流式套接字
2. name the socket(bind)
绑定 IP 地址和端口号。
cpp
struct sockaddr_in tSocketServerAddr;
tSocketServerAddr.sin_family = AF_INET;
tSocketServerAddr.sin_port = htons(SERVER_PORT); // 端口号转换为网络字节序
tSocketServerAddr.sin_addr.s_addr = INADDR_ANY; // 监听所有网卡
memset(tSocketServerAddr.sin_zero, 0, 8);
bind(iSocketServer, (const struct sockaddr*)&tSocketServerAddr, sizeof(struct sockaddr));
3. listen for incoming connections(listen)
启动监听,等待客户端连接请求。
cpp
listen(iSocketServer, BACKLOG); // BACKLOG 是等待队列长度
4. accept client connections(accept)
接收客户端连接,返回新的套接字用于通信。
cpp
int iAddrLen = sizeof(struct sockaddr);
struct sockaddr_in tSocketClientAddr;
int iSocketClient = (int)accept(iSocketServer, (struct sockaddr*)&tSocketClientAddr, &iAddrLen);
5. send and receive data(send/recv)
收发数据。
cpp
int iRecvLen = recv(iSocketClient, (char*)ucRecvBuf, 1024, 0);
if(iRecvLen > 0) {
// 处理接收到的数据
}
6. close connection(close)
关闭连接。
cpp
close(iSocketClient);
四、完整代码实现
以下是一个完整的 TCP Server 程序,监听端口 8888,接收客户端数据并打印:
cpp
#define _WIN32_WINNT 0x0600
#include<iostream>
#include<WinSock2.h>
#include<ws2tcpip.h>
#pragma comment(lib, "ws2_32.lib")
#define close closesocket
#define SERVER_PORT 8888
#define BACKLOG 10
int main() {
WSADATA wsa;
int iSocketServer, iSocketClient, iAddrLen, iRecvLen, iClientNum = 0;
struct sockaddr_in tSocketServerAddr, tSocketClientAddr;
if(WSAStartup(MAKEWORD(2,2), &wsa) != 0) return -1;
iSocketServer = (int)socket(AF_INET, SOCK_STREAM, 0);
tSocketServerAddr.sin_family = AF_INET;
tSocketServerAddr.sin_port = htons(SERVER_PORT);
tSocketServerAddr.sin_addr.s_addr = INADDR_ANY;
memset(tSocketServerAddr.sin_zero, 0, 8);
bind(iSocketServer, (const struct sockaddr*)&tSocketServerAddr, sizeof(struct sockaddr));
listen(iSocketServer, BACKLOG);
while (1) {
iAddrLen = sizeof(struct sockaddr);
iSocketClient = (int)accept(iSocketServer, (struct sockaddr*)&tSocketClientAddr, &iAddrLen);
if(-1 != iSocketClient) {
char ip[20];
inet_ntop(AF_INET, &tSocketClientAddr.sin_addr, ip, 20);
printf("client ip:%s, port:%d\n", ip, iClientNum++);
while(1) {
char ucRecvBuf[1024];
iRecvLen = recv(iSocketClient, (char*)ucRecvBuf, 1024, 0);
if(iRecvLen > 0) {
ucRecvBuf[iRecvLen] = '\0';
printf("Get Msg From Client: %d %s\n", iClientNum, ucRecvBuf);
} else {
close(iSocketClient);
break;
}
}
}
}
return 0;
}
关键注意事项:
_WIN32_WINNT宏定义的值0x0600表示 Windows Vista 或更高版本,这是inet_ntop函数在 Windows 上可用的最低版本要求。- 确保这个宏定义在所有 Windows 头文件之前,否则可能不会生效。
运行效果:
先运行程序,再打开串口软件选择 TCP Client,然后设置远程 IP 与本地 IP 一致,Server 端口号 8888,可正常发送数据。

五、增加回复计数功能
在上面的基础上,增加一个功能:Server 接收到客户端消息后,自动回复一条计数消息。
cpp
#define _WIN32_WINNT 0x0600
#include<iostream>
#include<WinSock2.h>
#include<ws2tcpip.h>
#pragma comment(lib, "ws2_32.lib")
#define close closesocket
#define SERVER_PORT 8888
#define BACKLOG 10
int main() {
WSADATA wsa;
int iSocketServer, iSocketClient, iAddrLen, iRecvLen, iClientNum = 0, msg_cnt = 0;
struct sockaddr_in tSocketServerAddr, tSocketClientAddr;
if(WSAStartup(MAKEWORD(2,2), &wsa) != 0) return -1;
iSocketServer = (int)socket(AF_INET, SOCK_STREAM, 0);
tSocketServerAddr.sin_family = AF_INET;
tSocketServerAddr.sin_port = htons(SERVER_PORT);
tSocketServerAddr.sin_addr.s_addr = INADDR_ANY;
memset(tSocketServerAddr.sin_zero, 0, 8);
bind(iSocketServer, (const struct sockaddr*)&tSocketServerAddr, sizeof(struct sockaddr));
listen(iSocketServer, BACKLOG);
while (1) {
iAddrLen = sizeof(struct sockaddr);
iSocketClient = (int)accept(iSocketServer, (struct sockaddr*)&tSocketClientAddr, &iAddrLen);
if(-1 != iSocketClient) {
char ip[20];
inet_ntop(AF_INET, &tSocketClientAddr.sin_addr, ip, 20);
printf("client ip:%s, port:%d\n", ip, iClientNum++);
msg_cnt = 0;
while(1) {
char ucRecvBuf[1024];
iRecvLen = recv(iSocketClient, (char*)ucRecvBuf, 1024, 0);
if(iRecvLen > 0) {
ucRecvBuf[iRecvLen] = '\0';
printf("Get Msg From Client: %d %s\n", iClientNum, ucRecvBuf);
snprintf(ucRecvBuf, 1024, "server msg %d", msg_cnt++); // 生成回复消息
send(iSocketClient, ucRecvBuf, strlen(ucRecvBuf), 0); // 发送回复
} else {
close(iSocketClient);
break;
}
}
}
}
return 0;
}
运行效果:
发送消息之后自动回复信息,计算消息数并累加。

六、总结
- socket:创建套接字
- bind:绑定 IP 和端口
- listen:启动监听
- accept:接收客户端连接
- recv/send:收发数据
- close:关闭连接
TCP Server 的核心是 "监听 → 接收 → 处理 → 回复",理解这个流程后,编写客户端程序就相对容易了。
七、结尾
本篇我们实现了一个完整的 TCP Server 程序,下一篇我们将学习 TCP 客户端编程,实现与 Server 的双向通信。
Hello_Embed 继续带你从原理到实践,掌握嵌入式上位机开发的核心技能,敬请关注~