
TCP(Transmission Control Protocol,传输控制协议)是互联网中的一种面向连接的、可靠的、基于字节流的传输层通信协议。在嵌入式Linux应用开发中,网络编程是重要的一环,而TCP协议则是实现网络通信的基础之一。
TCP是一种面向连接的、可靠的、基于字节流的传输层通信协议,在嵌入式 Linux 应用开发的网络编程中起着至关重要的作用。它确保数据在网络中的可靠传输,适用于对数据准确性要求较高的场景,如文件传输、电子邮件、网页浏览等。
TCP协议的数据传输流程包括连接建立、数据传输和连接终止三个阶段。

SYN 包,其中 Seq=x 表示客户端的初始序列号 x,这个包用于请求建立连接。SYN 包后,向客户端发送一个 SYN + ACK 包。Seq=y 是服务器的初始序列号,Ack=x + 1 表示服务器确认收到了客户端序列号为 x 的 SYN 包,期望下一个收到的序列号是 x + 1。SYN + ACK 包后,向服务器发送一个 ACK 包。Seq=x + 1 表示客户端的下一个序列号,Ack=y + 1 表示客户端确认收到了服务器序列号为 y 的 SYN 包,期望下一个收到的序列号是 y + 1。此时,连接建立成功。Seq=X1 和确认号 Ack=Y1。ACK 确认包,包含自己的序列号 Seq=Y2 和确认号 Ack=X2,表示确认收到了客户端序列号为 X1 的数据段,期望下一个收到的序列号是 X2。这个过程会根据实际需要循环多次。FIN 包,Seq=X3 和 Ack=Y3 表示客户端希望关闭连接,同时包含当前的序列号和确认号。FIN 包后,向客户端发送一个 ACK 包,Seq=Y4 和 Ack=X4 表示确认收到客户端的关闭请求。FIN 包,Seq=Y5 和 Ack=X5 表示服务器也希望关闭连接。FIN 包后,向服务器发送一个 ACK 包,Seq=X6 和 Ack=Y6 表示确认收到服务器的关闭请求。此时,连接释放完成。socket()函数创建一个 TCP 套接字。bind()函数将套接字绑定到指定的 IP 地址和端口号。listen()函数开始监听客户端的连接请求。accept()函数接受客户端的连接请求,并返回一个新的套接字用于与客户端通信。read()和write()函数在新的套接字上进行数据的读写操作。close()函数关闭套接字,释放资源。socket()函数创建一个 TCP 套接字。connect()函数连接到服务器的指定 IP 地址和端口号。read()和write()函数在套接字上进行数据的读写操作。close()函数关闭套接字,释放资源。服务器端代码:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <unistd.h>
#define PORT 8888
int main() {
int server_fd, new_socket;
struct sockaddr_in address;
int opt = 1;
int addrlen = sizeof(address);
char buffer[1024] = {0};
const char *hello = "Hello from server";
// 创建socket文件描述符
if ((server_fd = socket(AF_INET, SOCK_STREAM, 0)) == 0) {
perror("socket failed");
exit(EXIT_FAILURE);
}
// 设置socket选项
if (setsockopt(server_fd, SOL_SOCKET, SO_REUSEADDR | SO_REUSEPORT, &opt, sizeof(opt))) {
perror("setsockopt");
exit(EXIT_FAILURE);
}
address.sin_family = AF_INET;
address.sin_addr.s_addr = INADDR_ANY;
address.sin_port = htons(PORT);
// 绑定socket到指定地址和端口
if (bind(server_fd, (struct sockaddr *)&address, sizeof(address)) < 0) {
perror("bind failed");
exit(EXIT_FAILURE);
}
// 监听连接
if (listen(server_fd, 3) < 0) {
perror("listen");
exit(EXIT_FAILURE);
}
// 接受连接
if ((new_socket = accept(server_fd, (struct sockaddr *)&address, (socklen_t*)&addrlen)) < 0) {
perror("accept");
exit(EXIT_FAILURE);
}
// 读取客户端发送的数据
read(new_socket, buffer, 1024);
printf("Client: %s\n", buffer);
// 发送响应数据给客户端
send(new_socket, hello, strlen(hello), 0);
printf("Hello message sent\n");
// 关闭连接
close(new_socket);
close(server_fd);
return 0;
}首先创建一个 TCP 套接字,然后将其绑定到本地的指定端口并开始监听客户端连接。当有客户端连接时,接受连接并读取客户端发送的数据,最后发送响应数据给客户端,完成通信后关闭套接字。
客户端代码:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <unistd.h>
#define PORT 8888
int main() {
int sock = 0;
struct sockaddr_in serv_addr;
char *hello = "Hello from client";
char buffer[1024] = {0};
// 创建socket文件描述符
if ((sock = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
perror("socket creation error");
return -1;
}
serv_addr.sin_family = AF_INET;
serv_addr.sin_port = htons(PORT);
// 将IPv4地址从文本转换为二进制形式
if (inet_pton(AF_INET, "127.0.0.1", &serv_addr.sin_addr) <= 0) {
perror("Invalid address/ Address not supported");
return -1;
}
// 连接到服务器
if (connect(sock, (struct sockaddr *)&serv_addr, sizeof(serv_addr)) < 0) {
perror("Connection Failed");
return -1;
}
// 发送数据到服务器
send(sock, hello, strlen(hello), 0);
printf("Hello message sent\n");
// 读取服务器响应数据
read(sock, buffer, 1024);
printf("Server: %s\n", buffer);
// 关闭连接
close(sock);
return 0;
}创建一个 TCP 套接字,连接到服务器的指定 IP 地址和端口,发送数据给服务器,读取服务器的响应数据,最后关闭套接字。
①调整 TCP 参数
/proc/sys/net/ipv4/tcp_window_scaling 来启用窗口缩放选项,同时调整 /proc/sys/net/ipv4/tcp_rmem 和 /proc/sys/net/ipv4/tcp_wmem 分别设置接收和发送窗口的大小范围。/proc/sys/net/ipv4/tcp_retries1 和 /proc/sys/net/ipv4/tcp_retries2 来调整重传次数和重传间隔。②优化网络拓扑
①批量数据传输
②异步 I/O 操作
epoll 机制,使应用程序在等待数据读写时可以同时处理其他任务,提高并发处理能力。在高并发的服务器应用中,使用 epoll 可以显著提升性能。③多线程或多进程处理
优化方向 | 关键技术 | 嵌入式实现方法 | 适用场景 |
|---|---|---|---|
连接建立 | TCP Fast Open (TFO) | setsockopt(fd, SOL_TCP, TCP_FASTOPEN, &enable, sizeof(enable)) | 高频短连接(如HTTP API) |
传输效率 | Zero-Copy传输 | sendfile(sockfd, file_fd, &offset, filesize) | OTA固件升级 |
拥塞控制 | BBR算法 | setsockopt(fd, SOL_TCP, TCP_CONGESTION, "bbr", 3) | 高延迟网络(5G车载通信) |
内存管理 | 动态缓冲区调整 | setsockopt(fd, SOL_SOCKET, SO_RCVBUF, &bufsize, sizeof(bufsize)) | 内存受限设备 |
可靠性 | 选择性确认(SACK) | echo 1 > /proc/sys/net/ipv4/tcp_sack | 高丢包率网络(工业现场) |
实时性 | 禁用Nagle算法 | int flag = 1; setsockopt(fd, IPPROTO_TCP, TCP_NODELAY, &flag, sizeof(int)) | 实时控制指令传输 |
连接复用 | Keep-Alive机制 | setsockopt(fd, SOL_TCP, TCP_KEEPIDLE, (void*)&keepalive_time, sizeof(int)) | 长期连接的IoT设备 |
应用层数据 -> 用户态缓冲区 -> 内核缓冲区 -> 网卡DMA
↓ ↓
应用层直接操作硬件 (DPDK)嵌入式实现:
io_uring异步IO
sendfile()
splice()+vmsplice()
// 预分配内存池管理
#define POOL_SIZE 32
struct {
uint8_t buffer[1500]; // MTU大小
uint16_t len;
bool used;
} packet_pool[POOL_SIZE];
// 获取内存块时进行边界检查
uint8_t* alloc_buffer() {
for(int i=0; i<POOL_SIZE; i++) {
if(!packet_pool[i].used) {
packet_pool[i].used = true;
return packet_pool[i].buffer;
}
}
return NULL; // 内存耗尽处理
}普通Linux内核 vs 实时化改造
-------------------------- ----------------------
| 应用进程 | | Xenomai实时域 |
| 普通网络协议栈 | | 实时网络驱动 |
| 通用中断处理 | | 低延迟中断路由 |
-------------------------- ----------------------
平均延迟1-5ms <200μs实现方法:
# 测量TCP吞吐量
iperf3 -c 192.168.1.100 -t 30 -J > report.json
# 时延抖动检测
ping -D 192.168.1.100 | awk '{print $1,$7}' | tsconv# 实时查看TCP重传率
watch -n 1 "grep 'TcpRetransSegs' /proc/net/netstat"
# 内存使用监控
cat /proc/net/sockstat | grep "TCP"Wireshark捕获 -> 导出CSV -> Python分析脚本 -> Grafana可视化
↓
故障模式识别(重传风暴、窗口冻结等)现象 | 可能原因 | 解决方案 |
|---|---|---|
连接超时 | 防火墙阻挡/路由问题 | 使用traceroute检查网络路径 |
数据传输不完整 | 未处理粘包问题 | 添加协议头长度字段 |
系统频繁重启 | 网络阻塞导致看门狗超时 | 在select()循环中增加喂狗点 |
内存持续增长 | Socket未关闭导致泄漏 | 使用valgrind --leak-check=yes检测 |