TCP数据接收之ACK的处理

本文详细介绍了TCP协议在接收到ACK时的处理过程,包括发送窗口的更新、ACK参数解析以及tcp_ack()函数的核心操作。重点讨论了在收到ACK后如何更新发送窗口,并分析了快速路径和慢速路径下的不同处理策略。
摘要由CSDN通过智能技术生成

在TCP输入数据段的处理过程中,如果发现输入段携带了ACK信息,则会调用tcp_ack()进行ACK相关的处理。实际中,ACK信息总是会携带的,因为携带ACK不需要任何的附加开销,所以对于输入的每一个段(输了RST等特殊段),这个过程总是要执行的,这篇笔记就来看看TCP对ACK确认的处理过程。

1. 发送窗口

收到确认后,一项重要的工作就是更新发送窗口,所以这里再来认识一下发送窗口相关的几个关键字段,如下图所示:

在这里插入图片描述
注意,图中的发送窗口,指的是接收方通知给发送方的接收窗口大小,即流量控制窗口,并非拥塞窗口。

2. ACK处理

2.1 参数flag

tcp_ack()有个非常重要的参数flag,其贯穿整个ACK的处理过程,它记录了从输入段中能够获取到的任何信息(比如是否携带了数据、是否重复ACK、是否是SACK等),供后面的拥塞控制、RTT采样等操作参考。flag可能是如下值的组合。

flag 描述
FLAG_DATA 0x01 ACK段中携带了数据
FLAG_WIN_UPDATE 0x02 收到ACK段后更新了发送窗口,可能更新了左边界,也有可能更新了右边界(通告窗口变大)
FLAG_DATA_ACKED 0x04 ACK段确认了新数据
FLAG_RETRANS_DATA_ACKED 0x08 ACK段携带的数据已经收到过了
FLAG_SYN_ACKED 0x10 ACK段确认了SYN段
FLAG_DATA_SACKED 0x20 ACK段确认了新的数据
FLAG_ECE 0x40 该ACK段携带了ECE标志
FLAG_DATA_LOST 0x80 SACK检测到了数据丢失
FLAG_SLOWPATH 0x100 该ACK段是由慢速路径处理的
FLAG_ONLY_ORIG_SACKED 0x200
FLAG_SND_UNA_ADVANCED 0x400 ACK段更新了snd_una,即收到ACK后,发送窗口左边界可以右移
FLAG_DSACKING_ACK 0x800 ACK段中包含有DSACK信息
FLAG_NONHEAD_RETRANS_ACKED 0x1000
FLAG_SACK_RENEGING 0x2000 检测到之前SACK确认过的数据段被对端丢弃了(这是协议允许的)

此外,还定义了一些基本flag的组合:

#define FLAG_ACKED		(FLAG_DATA_ACKED|FLAG_SYN_ACKED)
//用于判断输入的数据段是否为重复段
#define FLAG_NOT_DUP		(FLAG_DATA|FLAG_WIN_UPDATE|FLAG_ACKED)
#define FLAG_CA_ALERT		(FLAG_DATA_SACKED|FLAG_ECE)
#define FLAG_FORWARD_PROGRESS	(FLAG_ACKED|FLAG_DATA_SACKED)
#define FLAG_ANY_PROGRESS	(FLAG_FORWARD_PROGRESS|FLAG_SND_UNA_ADVANCED)

2.2 tcp_ack()

/* This routine deals with incoming acks, but not outgoing ones. */
static int tcp_ack(struct sock *sk, struct sk_buff *skb, int flag)
{
   
	struct inet_connection_sock *icsk = inet_csk(sk);
	struct tcp_sock *tp = tcp_sk(sk);
	//TCB中尚未被确认的最小序号
	u32 prior_snd_una = tp->snd_una;
	//ACK段中的序号
	u32 ack_seq = TCP_SKB_CB(skb)->seq;
	//ACK段中的确认号
	u32 ack = TCP_SKB_CB(skb)->ack_seq;
	u32 prior_in_flight;
	u32 prior_fackets;
	int prior_packets;
	int frto_cwnd = 0;

	/* If the ack is newer than sent or older than previous acks
	 * then we can probably ignore it.
	 */
	//确认的是还没有发送的数据,这是无意义的确认,直接返回
	if (after(ack, tp->snd_nxt))
		goto uninteresting_ack;
	//该确认号已经收到过了。这种可能是重复ACK,也有可能是正常的,比如该AC段有延时。
	//这种ACK有可能还携带了有效的SACK信息
	if (before(
  • 3
    点赞
  • 17
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
以下是一个基于 Linux C 的 TCP 快速接收超大数据的示例代码: ```c #include <stdio.h> #include <stdlib.h> #include <string.h> #include <unistd.h> #include <arpa/inet.h> #include <sys/socket.h> #include <netinet/in.h> #include <netinet/tcp.h> #define BUF_SIZE 1024 * 1024 * 10 // 10MB int main(int argc, char *argv[]) { if (argc != 2) { printf("Usage: %s [port]\n", argv[0]); return -1; } int sock = socket(AF_INET, SOCK_STREAM, 0); if (sock == -1) { perror("socket() error"); return -1; } struct sockaddr_in server_addr, client_addr; memset(&server_addr, 0, sizeof(server_addr)); server_addr.sin_family = AF_INET; server_addr.sin_addr.s_addr = htonl(INADDR_ANY); server_addr.sin_port = htons(atoi(argv[1])); if (bind(sock, (struct sockaddr*)&server_addr, sizeof(server_addr)) == -1) { perror("bind() error"); return -1; } if (listen(sock, 5) == -1) { perror("listen() error"); return -1; } socklen_t client_addr_size = sizeof(client_addr); int conn_sock = accept(sock, (struct sockaddr*)&client_addr, &client_addr_size); if (conn_sock == -1) { perror("accept() error"); return -1; } char *buf = (char*)malloc(BUF_SIZE); if (buf == NULL) { perror("malloc() error"); return -1; } // 关闭 Nagle 算法,禁用 TCP_NODELAY 选项 int flag = 1; setsockopt(conn_sock, IPPROTO_TCP, TCP_NODELAY, (char*)&flag, sizeof(flag)); // 开启 TCP_QUICKACK 选项 flag = 1; setsockopt(conn_sock, IPPROTO_TCP, TCP_QUICKACK, (char*)&flag, sizeof(flag)); // 读取数据 int n = 0; while (n < BUF_SIZE) { int len = read(conn_sock, buf + n, BUF_SIZE - n); if (len == -1) { perror("read() error"); return -1; } else if (len == 0) { break; } n += len; } printf("Received data size: %d bytes\n", n); free(buf); close(conn_sock); close(sock); return 0; } ``` 这个示例代码实现了如下功能: 1. 创建一个 TCP 套接字,并绑定到指定的端口; 2. 监听连接请求,等待客户端连接; 3. 分配一个 10MB 大小的缓冲区,用于存储接收到的数据; 4. 关闭 Nagle 算法,禁用 TCP_NODELAY 选项,避免接收大块数据时出现延迟; 5. 开启 TCP_QUICKACK 选项,尽快发送 ACK 响应,避免接收方等待发送方的 ACK 响应; 6. 使用 read() 函数从套接字中读取数据,直到所有数据都被接收完毕,并统计接收到的数据大小。 注意:这个示例代码只是简单地演示了如何快速接收超大数据,实际应用中需要根据具体情况进行调整和优化。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值