Chinaunix首页 | 论坛 | 博客
  • 博客访问: 8770
  • 博文数量: 5
  • 博客积分: 10
  • 博客等级: 民兵
  • 技术积分: 60
  • 用 户 组: 普通用户
  • 注册时间: 2012-03-28 20:12
个人简介

在 CU 的博客

文章分类

全部博文(5)

文章存档

2015年(5)

我的朋友

分类: LINUX

2015-06-30 21:17:37

参考资料:
1. LWN 的 TFO 介绍: style="color:#666666;font-family:宋体, Arial;line-height:26px;white-space:normal;background-color:#FFFFFF;" /> 2. Google 研究人员关于 TFO 实现的论文: style="color:#666666;font-family:宋体, Arial;line-height:26px;white-space:normal;background-color:#FFFFFF;" /> 3. RFC7413: style="color:#666666;font-family:宋体, Arial;line-height:26px;white-space:normal;background-color:#FFFFFF;" />
题记:最近导师组的活和 Linux 内核 TCP patch 有关,在这里我把相关资料记录一下。

1. TFO 的背景

TCP 数据流的速度决定于两个因素:传输时延(transmission delay)和传播时延(propagation delay)。传输时延决定于网络的带宽,而自网络诞生以来带宽一直稳步增加,所以传输时延正变得越来越小;而传播时延决定于路由器的延时和信号速度,前者没有带宽那么大的改善,后者是一个常量。随着传输时延的进一步降低,传播实验所占的比重正变得越来越大,尤其是在请求网页时,浏览器常常打开许多连接请求网页上的各个小的对象。

减少 TCP 连接所需的信号往返次数也因此成为了互联网服务商热衷的兴趣点。比如 Google 就成为了 Linux 内核网络栈 TFO 实现的发起者。 TCP 快速打开(TCP Fast Open)是对 TCP 连接的一种简化了握手手续的扩展,可用于提升两终点间连续连接的打开速度。它通过握手开始时发送的 SYN 包中的 TFO cookie 来验证一个之前连接过的客户端。如果验证成功,服务器端能在3次握手最终的 ACK包收到之前就开始发送数据,这样就能更快地开始数据的传输。这个加密的 cookie 存储在客户端,在一开始的连接时被设定好,每当客户端连接时,这个 cookie 都被重复返回。 IPv4的TFO 客户端支持在3.6版本进入内核,服务器端在3.7版本进入内核。从3.13版本开始默认打开,IPv6的TFO 支持被合并入3.16版本。 TFO 能减少某些 TCP 连接中一个 RTT 的时间,Google 研究人员的论文显示 TFO 能减少平均 15% 的传播时延,并加快超过 10% 的页面加载速度。

2. TFO 原理概述

先来看一下 TCP 3次握手的过程:


如上图所示,在第三次握手时客户端才能向服务器发送数据,那么之前两次握手的一个 RTT 时间都是没有数据发送的。已有的一个解决方案是 HTTP 持久连接(HTTP persistent connections),即浏览器会保持和服务器的连接,以便在之后的 HTTP 请求中重用。但服务器常常会因为资源占用的原因终止这些空闲的连接(idle connections),这样客户端又要重新开始 TCP 的3次握手,效率还是不高。

TFO 连接的建立如上图所示,多出来的部分是客户端首先发送的 SYN 包中含有 cookie 请求,第二次握手时服务器端传回的包中含有生成的 cookie,客户端会保存这个 cookie。TFO cookie 由服务器端生成,发给客户端保存,用于之后连接时使用;其内容是对客户端 IP 地址的加密文本,因此可以作为客户端和服务器建立过连接的凭据。对 TFO cookie 的请求,生成和交换对于应用层都是透明的。


有了这个 cookie,之后的连接过程如上图。在第一次握手时客户端发送的包中添加了 cookie,并在这里就直接向服务器端发送数据。服务器验证 cookie 后返回第二次握手的包用于确认,并直接发送数据。这就避免了第二次和第三次握手的传播时延,减少了一个 RTT 的等待。如果服务器端发现 cookie 不对,服务器端会传回通常的第二次握手包,之后的过程和通常 TCP 握手的二,三次握手相同。

还有一些其他的细节,比如生成 cookie 的加密算法,这个算法需要对 CPU 负担小,并且服务器需要周期性地更换算法的密钥,防止被攻击者通过收集 cookie 猜测出来。服务器也应设立允许 TFO 连接的最大值,防止被 SYN 洪范耗尽资源。

实现 TFO 后,在服务器端连接中这么用:
  1. sfd = socket(AF_INET, SOCK_STREAM, 0); // Create socket

  2. bind(sfd, ...); // Bind to well known address
  3.   
  4. int qlen = 5; // Value to be chosen by application
  5. setsockopt(sfd, SOL_TCP, TCP_FASTOPEN, &qlen, sizeof(qlen));
  6.   
  7. listen(sfd, ...); // Mark socket to receive connections

  8. cfd = accept(sfd, NULL, 0); // Accept connection on new socket

  9. // read and write data on connected socket cfd

  10. close(cfd);
其中 qlen 是服务器 TFO 请求队列的大小。

客户端代码是这样:
  1. sfd = socket(AF_INET, SOCK_STREAM, 0);
  2.   
  3. sendto(sfd, data, data_len, MSG_FASTOPEN, 
  4.                (struct sockaddr *) &server_addr, addr_len);
  5.        // Replaces connect() + send()/write()
  6.   
  7.    // read and write further data on connected socket sfd

  8. close(sfd);
直接就用 sendto() 了,省去了之前 connect() 的调用。
阅读(1642) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~