浅析TCP之SACK(选择性确认)

1、SACK

Selective Acknowledgement,选择性确认。

2、功能

TCP收到乱序数据后,会将其放入乱序队列中,然后发送重复ACK给对端。对端如果收到多个重复的ACK,认为发生丢包,TCP会重传最后确认的包开始的后续包。这样原先已经正确传输的包可能会重复发送,降低了TCP性能。为改善这种情况,发展出SACK技术,使用SACK选项可以告知发包方收到了哪些数据,发包方收到这些信息后就会知道哪些数据丢失,然后立即重传丢失的部分。

需要注意的是只有收到失序的分组时才会可能会发送SACK,TCP的ACK还是建立在累积确认的基础上的。也就是说如果收到的报文段与期望收到的报文段的序号相同就会发送累积的ACK,SACK只是针对失序到达的报文段的。

3、格式

SACK包括了两个TCP选项,一个选项用于标识是否支持SACK(SACK_permitted),是在TCP连接建立时时发送;另一种选项则包含了具体的SACK信息。

1)SACK_permitted选项

该选项只允许在TCP连接建立时,有SYN标志的包中设置,也即TCP握手的前两个包中,分别表示通信的两方各自是否支持SACK。

2)SACK信息选项

SACK信息选项用于通告对端接收数据的信息。

该选项参数告诉对方已经接收到并缓存的不连续的数据块,注意都是已经接收的,发送方可
根据此信息检查究竟是哪个块丢失,从而发送相应的数据块。

   *    Left Edge of Block
        不连续块的第一个数据的序列号
   *    Right Edge of Block
        不连续块的最后一个数据的序列号之后的序列号


  • 1
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
以下是一个简单的TCP选择性应答的源代码示例,基于Linux平台和C语言: ``` #include <stdio.h> #include <string.h> #include <stdlib.h> #include <errno.h> #include <sys/socket.h> #include <arpa/inet.h> #include <unistd.h> #include <netinet/in.h> #include <netinet/tcp.h> #define SERVER_PORT 8888 #define SERVER_ADDR "127.0.0.1" #define BUFFER_SIZE 1024 int main(int argc, char *argv[]) { int sockfd; struct sockaddr_in addr; char send_buf[BUFFER_SIZE]; char recv_buf[BUFFER_SIZE]; int read_len, send_len; int i, j, k; int seq_num = 0, ack_num = 0; int window_size = 1; int flag = 0; struct tcp_info info; socklen_t info_len = sizeof(info); // 创建套接字 sockfd = socket(AF_INET, SOCK_STREAM, 0); if (sockfd == -1) { perror("socket"); exit(1); } memset(&addr, 0, sizeof(addr)); addr.sin_family = AF_INET; addr.sin_port = htons(SERVER_PORT); addr.sin_addr.s_addr = inet_addr(SERVER_ADDR); // 连接服务器 if (connect(sockfd, (struct sockaddr *)&addr, sizeof(addr)) == -1) { perror("connect"); exit(1); } // 开启TCP选择性应答 if (setsockopt(sockfd, IPPROTO_TCP, TCP_SACK, &flag, sizeof(flag)) != 0) { perror("setsockopt"); exit(1); } while (1) { // 读取用户输入 printf("请输入要发送的数据:"); fgets(send_buf, BUFFER_SIZE, stdin); send_len = strlen(send_buf); // 发送数据 if (send(sockfd, send_buf, send_len, 0) != send_len) { perror("send"); exit(1); } // 接收数据 read_len = recv(sockfd, recv_buf, BUFFER_SIZE, 0); if (read_len == -1) { perror("recv"); exit(1); } else if (read_len == 0) { printf("服务器关闭连接\n"); break; } // 读取TCP信息 if (getsockopt(sockfd, IPPROTO_TCP, TCP_INFO, &info, &info_len) == 0) { seq_num = info.tcpi_snd_nxt; ack_num = info.tcpi_rcv_nxt; window_size = info.tcpi_snd_cwnd; } // 输出收到的数据、序列号、确认号和窗口大小 printf("收到数据:%.*s,序列号:%d,确认号:%d,窗口大小:%d\n", read_len, recv_buf, seq_num, ack_num, window_size); // 模拟丢包,只发送部分数据 if (send_len > 5) { send_len = 5; } // 发送部分数据 for (i = 0; i < send_len; i += window_size) { k = (send_len - i) > window_size ? window_size : (send_len - i); if (send(sockfd, send_buf + i, k, 0) != k) { perror("send"); exit(1); } // 接收确认 read_len = recv(sockfd, recv_buf, BUFFER_SIZE, 0); if (read_len == -1) { perror("recv"); exit(1); } else if (read_len == 0) { printf("服务器关闭连接\n"); break; } // 输出收到的确认号和窗口大小 if (getsockopt(sockfd, IPPROTO_TCP, TCP_INFO, &info, &info_len) == 0) { ack_num = info.tcpi_rcv_nxt; window_size = info.tcpi_snd_cwnd; } printf("收到确认确认号:%d,窗口大小:%d\n", ack_num, window_size); } } // 关闭套接字 close(sockfd); return 0; } ``` 这个示例程序实现了一个简单的TCP客户端,可以与服务器进行双向通信。在程序中开启了TCP选择性应答,并且模拟了丢包的情况,以测试TCP选择性应答的效果。程序中的注释可以帮助理解代码。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值