一、TCP编码
(1)核心过程详解
1. 建立连接:三次握手
在 connect()
和 accept()
之间,内核自动完成了 TCP 的三次握手,确保双方都准备好收发数据。
- 编码体现 :你无需在代码中手动实现握手,但必须理解
connect()
会阻塞直到握手成功或失败。
2. 数据传输:循环读写
这是编码的核心。TCP 是面向字节流的 ,它不保证 send()
和 recv()
的一次调用能处理完所有数据。
-
发送端 (
send
)-
关键 :
send()
的返回值表示实际成功放入内核发送缓冲区的字节数 ,这个值可能小于你要求发送的长度(len
)。 -
编码模式:必须循环发送,确保所有数据都被处理。
c
const char *data = "Hello, World!"; size_t total_sent = 0; size_t len = strlen(data) + 1; // 包括字符串结束符 while (total_sent < len) { ssize_t bytes_sent = send(conn_fd, data + total_sent, len - total_sent, 0); if (bytes_sent == -1) { perror("send"); break; } total_sent += bytes_sent; }
-
-
接收端 (
recv
)-
关键 :
recv()
的返回值表示实际从内核接收缓冲区读取到的字节数。-
> 0
:成功读取到的字节数。 -
= 0
:对方已优雅地关闭连接(FIN)。 -
-1
:出错。
-
-
编码模式:必须循环读取,直到收完预期长度的数据或连接关闭。
c
char buffer[1024]; size_t total_received = 0; size_t expected_len = 100; // 假设我们预期要接收100字节 while (total_received < expected_len) { ssize_t bytes_received = recv(conn_fd, buffer + total_received, sizeof(buffer) - total_received - 1, 0); if (bytes_received == 0) { printf("Peer has closed the connection.\n"); break; } else if (bytes_received == -1) { perror("recv"); break; } total_received += bytes_received; } buffer[total_received] = '\0'; // 如果当字符串使用,添加结束符
-
3. 关闭连接
TCP 连接是双全工的,可以独立关闭每一方的读写。
-
shutdown()
:更优雅地控制连接关闭方向。-
SHUT_WR
:关闭写端。发送 FIN 给对方,表明"我数据发完了,但还可以收"。这是优雅关闭的常见方式。 -
SHUT_RD
:关闭读端(较少用)。 -
SHUT_RDWR
:等同于先后调用SHUT_RD
和SHUT_WR
。
-
-
close()
:将套接字引用计数减1。当引用计数为0时,系统会发送 FIN 并完全关闭连接,释放资源。
二、TCP和UDP的对比
特性 | TCP | UDP |
---|---|---|
连接类型 | 面向连接(三次握手) | 无连接 |
可靠性 | 可靠传输(确认、重传、排序) | 不可靠传输(不保证数据到达) |
延迟 | 高延迟(连接建立和可靠传输的开销) | 低延迟(无连接建立和可靠传输的开销) |
数据边界 | 面向字节流(不保留数据边界) | 面向数据报(保留数据边界) |
流量控制 | 有(滑动窗口机制) | 无 |
拥塞控制 | 有(拥塞控制算法) | 无 |
适用场景 | 文件传输、网页浏览、电子邮件等 | 视频流、语音通信、实时游戏等 |