8.6 TCP Fast Open(TFO)

TCP Fast Open (TFO) 是一种优化TCP连接的技术,允许在连接建立阶段传输数据,从而减少延迟。它涉及到客户端和服务器之间的cookie交换。在第二次连接时,客户端可以携带cookie和数据进行SYN请求,服务器验证cookie后直接传输数据,实现快速打开连接。TFO功能在Linux内核2.6.34版本中引入,通过内核配置和特定的系统调用启用。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

  TFO(TCP Fast Open)是一种能够在TCP连接建立阶段传输数据的机制。使用这种机制可以将数据交互提前,降低应用层事务的延迟。其基本步骤如下:

1、客户端发送一个SYN包到服务器,这个包中携带了Fast Open Cookie请求的TCP选项;

2、服务器生成一个cookie,这个cookie是通过使用密钥加密客户端的IP地址生成的。服务器给客户端发送SYN|ACK响应,在响应包的选项中包含了这个cookie;

3、客户端存储这个cookie以便将来再次与这个服务器的IP建立TFO连接时使用;

  也就是说,第一次TCP连接只是交换cookie信息,无法在SYN包中携带数据。在完成上述步骤后,接下来的TCP连接就可以在SYN中携带数据了。流程如下:

1、客户端发送一个携带应用数据和以TCP选项方式存储的Fast Open cookie的SYN包;

2、服务器验证这个cookie,如果合法,服务器发送一个SYN|ACK确认SYN和数据,然后数据被传递到应用进程;如果不合法,服务器丢弃数据,发送一个SYN|ACK只确认SYN,接下来走三次握手的普通流程;

3、如果接收了SYN包中的数据,服务器在接收到客户端的第一个ACK前可以发送其它响应数据;

4、客户端发送ACK确认了服务器的SYN;如果客户端的数据没有被确认,数据会在ACK包中重传;

5、下面的流程与普通的TCP交互流程无异。

  客户端使用TFO的方法:内核功能选项sysctl -w net.ipv4.tcp_fastopen=1;客户端代码:

    int sockfd, n;
    char recvline[4096], sendline[4096];
    struct sockaddr_in servaddr;
    char buf[20] = {"aaabbbccc"};
    int ret = 0;

    if ((sockfd = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
        printf ("create socket error: %s(errno: %d)\n", strerror (errno),
                errno);
        exit (0);
    }

    memset (&servaddr, 0, sizeof (servaddr));
    servaddr.sin_family = AF_INET;
    servaddr.sin_port = htons (6666);
    servaddr.sin_addr.s_addr = inet_addr("127.0.0.1");

    ret = sendto(sockfd, buf, strlen(buf), MSG_FASTOPEN,
            (struct sockaddr *)&servaddr, sizeof(servaddr));
    if (ret < 0) {
        printf ("send msg error: %s(errno: %d)\n", strerror (errno), errno);
    }

    close (sockfd);
   即客户端在发送数据时,生成socket后直接使用sendto发送数据,不用connect系统调用。第一次交互时只是向服务器申请一个TFO cookie,数据并不在连接建立过程中送达;TFO cookie交互完成后,以后客户端每次用同样方式发送数据时都会在SYN包中携带数据。

  服务器端开启TFO功能的方法:内核功能选项sysctl -w net.ipv4.tcp_fastopen=2;服务器端代码:

    int listenfd, connfd, fd;
    struct sockaddr_in servaddr;
    char buff[MAXLINE];
    int n;

    if ((listenfd = socket (AF_INET, SOCK_STREAM, 0)) == -1) {
        printf ("create socket error: %s(errno: %d)\n", strerror (errno), errno);
        exit (0);
    }

    memset (&servaddr, 0, sizeof (servaddr));
    servaddr.sin_family = AF_INET;
    servaddr.sin_addr.s_addr = inet_addr("127.0.0.1");
    servaddr.sin_port = htons (6666);

    if (bind (listenfd, (struct sockaddr *) &servaddr, sizeof (servaddr)) == -1) {
        printf ("bind socket error: %s(errno: %d)\n", strerror (errno), errno);
        exit (0);
    }

    if (listen (listenfd, 10) == -1) {
        printf ("listen socket error: %s(errno: %d)\n", strerror (errno), errno);
        exit (0);
    }

    int qlen = 5;
    ret = setsockopt(listenfd, 6, TCP_FASTOPEN, &qlen, sizeof(qlen));
    if (ret < 0) {
        printf ("setsockopt error: %s(errno: %d)\n", strerror (errno), errno);
    }
    while (1)
    {
        printf("Before accpet!\n");
        if ((connfd = accept (listenfd, (struct sockaddr *) NULL, NULL)) == -1)
...
  如果想要同时开启客户端和服务端的TFO功能,可以用“ sysctl -w net.ipv4.tcp_fastopen=3”。

  TFO功能在Linux 2.6.34内核中开始集成。

  下面通过分析内核代码来了解TFO的运行机制。开启TFO功能后,server端进程在调用listen系统调用时会初始化TFO队列:

 195 int inet_listen(struct socket *sock, int backlog)
 196 {
 197     struct sock *sk = sock->sk;    
 198     unsigned char old_state;
 199     int err;             
...
 214     if (old_state != TCP_LISTEN) { 
...
 222         if ((sysctl_tcp_fastopen & TFO_SERVER_ENABLE) != 0 &&
 223             inet_csk(sk)->icsk_accept_queue.fastopenq == NULL) {
 224             if ((sysctl_tcp_fastopen & TFO_SERVER_WO_SOCKOPT1) != 0)
 225                 err = fastopen_init_queue(sk, backlog);
 226             else if ((sysctl_tcp_fastopen &
 227                   TFO_SERVER_WO_SOCKOPT2) != 0)
 228                 err = fastopen_init_queue(sk,
 229                     ((uint)sysctl_tcp_fastopen) >> 16);
 230             else
 231                 err = 0;
 232             if (err)
 233                 goto out;
 234         }
 235         err = inet_csk_listen_start(sk, backlog);
...
  fastopen_init_queue函数:

373 static inline int fastopen_init_queue(struct sock *sk, int backlog)
374 {
375     struct request_sock_queue *queue =
376         &inet_csk(sk)->icsk_accept_queue;
377 
378     if (queue->fastopenq == NULL) {
379         queue->fastopenq = kzalloc(
380             sizeof(struct fa
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值