今天同事突然问起:“TCP是怎么进行socket通信的?UDP能进行socket通信吗?” 这看似基础的问题,竟让我一时语塞。这提醒我,越是核心的概念,越有必要系统地梳理一遍。因此,我将自己的理解整理成文,若有不当之处,还请大家不吝指正。
Socket与C/S架构
要理解Socket,首先得了解互联网的经典架构——C/S(客户端/服务器)架构。这个模型无处不在,以大家熟悉的《王者荣耀》为例:腾讯的云服务器是服务端,而我们手机上的游戏App就是客户端。二者之间要通信,就需要一个通用的“接口”,这就是Socket。
那么,Socket在网络协议栈中究竟处于什么位置呢?它就像是连接应用程序与底层网络协议的桥梁。

简单来说:
- 在网络通信中:Socket端点由IP地址和端口号唯一标识。
- 在编程中:Socket是操作系统提供的一个接口,它封装了复杂的网络通信能力,让开发者可以像操作文件一样进行网络读写。
TCP协议:可靠的“连接”
TCP(传输控制协议) 是一种网络协议。协议可以理解为通信双方必须共同遵守的约定和规则集合。就像两个人交谈必须使用同一种语言,网络中的两台机器也必须遵循相同的协议才能正确交换信息。
说到TCP,最广为人知的特点就是“面向连接”和“可靠传输”,其核心机制便是三次握手与四次挥手。
TCP的三次握手(建立连接)

建立连接需要三步握手,目的是同步双方的初始序列号(ISN),并确认彼此的收发能力正常。
- SYN:客户端发送一个SYN包(seq=x),说:“服务端你好,我想和你建立一条链接通道。”
- SYN-ACK:服务端收到后,回复SYN-ACK包(seq=y, ACK=x+1),意思是:“好的,我同意你的请求。另外,我也想和你建立一条链接通道。”
- ACK:客户端最后回复一个ACK包(ACK=y+1),确认:“好的,我也同意。”
至此,双向通信通道建立成功,双方进入ESTABLISHED状态。
TCP的可靠传输机制
连接建立后,TCP通过确认应答(ACK)机制来保证可靠性。

如图所示,客户端发送“老铁你真帅”(携带序列号和确认号),服务端收到后必须回复一个确认包(ACK x+2),告知客户端“我已收到”。如果客户端在一定时间内没收到这个确认,就会认为数据包丢失并重发。这正是TCP被称为可靠传输协议的由来。
TCP的四次挥手(断开连接)

断开连接为何需要四步?因为TCP连接是全双工的,即数据可以双向独立传输。因此,关闭连接需要每一方向单独关闭自己的数据通道。
- FIN:客户端主动发起关闭,发送FIN包(seq=x+2),表示:“服务端你好,我准备关闭我对你发送信息的通道。” 客户端进入
FIN_WAIT_1状态。
- ACK:服务端收到后,回复ACK包,说:“我知道了。” 服务端进入
CLOSE_WAIT状态,客户端进入FIN_WAIT_2状态。此时,客户端到服务端的通道关闭。
- FIN:当服务端也准备关闭时,发送自己的FIN包(seq=y+1),表示:“客户端你好,我准备关闭我对你发送信息的通道。” 服务端进入
LAST_ACK状态。
- ACK:客户端收到后,回复最后一个ACK包确认。客户端进入
TIME_WAIT状态,等待一段时间后彻底关闭。服务端收到ACK后连接关闭。
- 状态简析:
FIN_WAIT_1/2:主动关闭方等待对方确认或关闭。
TIME_WAIT:主动关闭方发送最后ACK后等待,防止包丢失导致异常。
CLOSE_WAIT:被动关闭方收到FIN后等待应用层处理关闭。
LAST_ACK:被动关闭方发送自己的FIN后等待最终确认。
UDP协议:高效的“报文”
与TCP的字节流模式不同,UDP(用户数据报协议) 是面向消息的。

UDP每次发送的都是一个完整的、有头有尾的数据包(称为数据报),接收方也是以整个报文为单位接收。更关键的是,UDP是无连接的。

如图所示,客户端无需事先“握手”建立连接,可以直接向服务器地址发送数据包。服务器收到后,可以选择是否回复。整个过程没有连接建立和拆除的开销。UDP发送数据后便不再过问,不保证对方一定能收到,也不保证顺序。
TCP vs UDP:核心差异一览
为了更直观地对比,我们用一个表格总结:

实战:Socket通信代码示例
理论说完了,来看看它们如何通过 Socket接口 实现通信。简单类比:
- TCP Socket:面向连接,可靠传输,像打电话。
- UDP Socket:无连接,不可靠传输,像发短信。
TCP Socket通信示例
服务端代码 (C语言)
#include <stdio.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <unistd.h>
int main() {
// 1. 创建 TCP Socket
int server_fd = socket(AF_INET, SOCK_STREAM, 0);
// 2. 绑定 IP 和端口
struct sockaddr_in addr;
addr.sin_family = AF_INET;
addr.sin_addr.s_addr = INADDR_ANY; // 监听所有网卡
addr.sin_port = htons(8080); // 端口号
bind(server_fd, (struct sockaddr*)&addr, sizeof(addr));
// 3. 开始监听
listen(server_fd, 5); // 允许最多 5 个客户端排队
// 4. 接受客户端连接
int client_fd = accept(server_fd, NULL, NULL);
// 5. 发送数据
char msg[] = "Hello TCP Client!";
send(client_fd, msg, sizeof(msg), 0);
// 6. 关闭连接
close(client_fd);
close(server_fd);
return 0;
}
客户端代码
#include <stdio.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
int main() {
// 1. 创建 TCP Socket
int sock = socket(AF_INET, SOCK_STREAM, 0);
// 2. 连接服务器
struct sockaddr_in server_addr;
server_addr.sin_family = AF_INET;
server_addr.sin_port = htons(8080);
inet_pton(AF_INET, "127.0.0.1", &server_addr.sin_addr); // 服务器 IP
connect(sock, (struct sockaddr*)&server_addr, sizeof(server_addr));
// 3. 接收数据
char buffer[1024] = {0};
recv(sock, buffer, 1024, 0);
printf("Server says: %s\n", buffer);
// 4. 关闭连接
close(sock);
return 0;
}
TCP Socket 特点小结
- 优点:可靠传输、保证顺序、有流量控制。
- 缺点:建立连接延迟高、头部开销大、不适合实时性要求极高的场景(如音视频通话)。
UDP Socket通信示例
服务端代码 (C语言)
#include <stdio.h>
#include <sys/socket.h>
#include <netinet/in.h>
int main() {
// 1. 创建 UDP Socket
int sock = socket(AF_INET, SOCK_DGRAM, 0);
// 2. 绑定 IP 和端口
struct sockaddr_in addr;
addr.sin_family = AF_INET;
addr.sin_addr.s_addr = INADDR_ANY;
addr.sin_port = htons(8080);
bind(sock, (struct sockaddr*)&addr, sizeof(addr));
// 3. 接收数据(无连接,直接收)
char buffer[1024];
struct sockaddr_in client_addr;
socklen_t addr_len = sizeof(client_addr);
recvfrom(sock, buffer, 1024, 0, (struct sockaddr*)&client_addr, &addr_len);
// 4. 发送数据(无连接,直接发)
char msg[] = "Hello UDP Client!";
sendto(sock, msg, sizeof(msg), 0, (struct sockaddr*)&client_addr, addr_len);
// 5. 关闭 Socket
close(sock);
return 0;
}
客户端代码
#include <stdio.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
int main() {
// 1. 创建 UDP Socket
int sock = socket(AF_INET, SOCK_DGRAM, 0);
// 2. 发送数据(无需 connect)
struct sockaddr_in server_addr;
server_addr.sin_family = AF_INET;
server_addr.sin_port = htons(8080);
inet_pton(AF_INET, "127.0.0.1", &server_addr.sin_addr);
char msg[] = "Hello Server!";
sendto(sock, msg, sizeof(msg), 0, (struct sockaddr*)&server_addr, sizeof(server_addr));
// 3. 接收数据
char buffer[1024] = {0};
recvfrom(sock, buffer, 1024, 0, NULL, NULL);
printf("Server says: %s\n", buffer);
// 4. 关闭 Socket
close(sock);
return 0;
}
UDP Socket 特点小结
- 优点:无连接延迟低、头部开销极小、支持广播/多播。
- 缺点:不保证可靠到达、不保证顺序、无流量控制。
总结
TCP和UDP是传输层的两大支柱,它们与Socket编程接口的结合,构成了现代网络应用的基石。理解它们的核心区别——TCP的可靠连接与UDP的高效无连接——是进行网络/系统编程和后端服务设计的关键。在实际的C/C++网络编程中,选择SOCK_STREAM还是SOCK_DGRAM,就决定了你使用的是哪一套通信模型。
希望通过以上的原理图解和代码示例,能帮助你更清晰地掌握TCP与UDP的Socket通信机制。网络编程的世界深邃而有趣,欢迎在云栈社区继续交流探讨。
