https://www.2cto.com/kf/201701/586043.html
http://blog.csdn.net/u011130578/article/details/44515165
http://blog.sina.com.cn/s/blog_583f42f101011veh.html
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
|
测试
开启
1 2 3 4 |
|
client
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 |
|
server
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 |
|
原理
1.客户端发送一个SYN包到服务器,这个包中携带了Fast Open Cookie Request;
2.服务器生成一个cookie,这个cookie是加密客户端的IP地址生成的。服务器给客户端发送SYN+ACK响应,在响应包的选项中包含了这个cookie;
3.客户端存储这个cookie以便将来再次与这个服务器的IP建立TFO连接时使用;
也就是说,第一次TCP连接只是交换cookie信息,无法在SYN包中携带数据。在第一次交换之后,接下来的TCP连接就可以在SYN中携带数据了。流程如下:
4.客户端发送一个SYN包,这个包比较特殊,因为它携带应用数据和cookie;
5.服务器验证这个cookie,如果合法,服务器发送一个SYN+ACK,这个ACK同时确认SYN和数据。然后数据被传递到应用进程;
如果不合法,服务器丢弃数据,发送一个SYN+ACK,这个ACK只确认SYN,接下来走三次握手的普通流程;
6.如果验证合法(接收了SYN包中的数据),服务器在接收到客户端的第一个ACK前可以发送其它响应数据;
7.如果验证不合法(客户端在SYN中带的数据没被确认),客户端发送ACK确认服务器的SYN;并且,数据会在ACK包中重传;
8.下面的流程与普通的TCP交互流程无异。
源码分析
TFO功能在Linux 2.6.34内核中开始集成。
下面通过分析内核代码来了解TFO的运行机制。开启TFO功能后,server端进程在调用listen系统调用时会初始化TFO队列:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 |
|
fastopen_init_queue函数
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
|
如果net.ipv4.tcp_fastopen && (TFO_SERVER_WO_SOCKOPT1|TFO_SERVER_WO_SOCKOPT2)为假,则TFO队列不会被初始化。但setsockopt函数也可以初始化TFO队列:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
|
如果inet_csk(sk)->icsk_accept_queue.fastopenq为NULL的话意味着TFO功能未开启。
轮到client端出场了!client端的sendto系统调用在内核中对应的TCP函数是tcp_sendmsg:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 |
|
tcp_sendmsg_fastopen函数用于发送带TFO请求的SYN或携带数据的SYN:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 |
|
__inet_stream_connect
函数会调用tcp_connect函数发送SYN:
1 2 3 4 5 6 7 8 9 |
|
tcp_send_syn_data函数:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 |
|
如果client是发送TFO请求,则tcp_send_syn_data函数会发送一个不带数据的SYN包,数据部分则会由tcp_sendmsg函数放入发送队列中,等待三次握手完成后再发送。
tcp_transmit_skb函数会调用tcp_syn_options函数构建选项信息,tcp_options_write函数负责将选项写入TCP报头中:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
|
client端在每次使用TFO功能时都会在TCP的选项中添加一个TFO选项,与server端进行第一次TFO交互时TFO选项只有4字节长,其值是一个“MAGIC”,这种TFO被称为“TFO请求”;后续的TFO选项长度会增加一个从服务器端获得的TFO cookie的长度值,并且在这个SYN中会携带数据。
server收到SYN后,会在tcp_v4_conn_request中进行处理:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 |
|
tcp_fastopen_check函数用于检查SYN中TFO请求的合法性以及生成TFO cookie:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 |
|
1327:rskq_rst_head为NULL的场景为有很多带TFO的SYN到来但SYN|ACK发送后并没有收到RST包,这意味着之前收到的那些带数据的TFO SYN可能是合法的;如果不为NULL但对立中最老的一个仍然没有超时的话,也不能将其替换
1344-1351:如果clienet端的TFO不是请求,而是cookie,则不设置valid_foc;另外如果server端被设置为不检查cookie的合法性,则生成一个cookie再检查SYN中的TFO cookie的合法性,如果不合法则不使用TFO功能。
tcp_make_synack函数会将tcp_fastopen_check中生成的TFO cookie写入TCP首部中,tcp_synack_options函数用来构建SYN|ACK报文的选项信息:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
|
将选项信息写入SYN|ACK的方法与client发送SYN时一样,都是调用tcp_options_write函数。可以看出,TCP server端会返回给发送TFO请求的client端一个TFO cookie。client发送的下一个带数据的SYN必须携带这个cookie,而TCP server对这样的SYN回复的SYN|ACK中不会携带TFO选项。
在SYN携带TFO cookie的情况下TCP server会在收到SYN时就创建sock,这个功能由cp_v4_conn_req_fastopen函数完成:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 |
|
应用进程收到listening socket的可读通告后,使用accept系统调用建立socket,就可以立即从这个新的socket中读到数据,并开始与客户端进行数据交互。
如果client的TFO是cookie,则SYN|ACK的处理过程与不使用TFO的情况是一样的;如果client发送的TFO是请求,则在收到SYN|ACK时需要将包中的TFO cookie保存下来:
1 2 3 4 5 6 7 8 9 10 11 12 13 |
|
tcp_rcv_fastopen_synack函数检查并保存server端发送的TFO cookie:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 |
|
在保存了TFO cookie后,client在向相同IP地址的server发送SYN时都可以携带数据(这时必须发送TFO cookie)。client在收到SYN|ACK后需要回复ACK报文,服务器端在接收ACK时对TFO的处理如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 |
|
综上,TFO在收到SYN的时候就创建socket并将数据提交给应用进程,这样就比普通模式节省了SYN|ACK与ACK的交互时间,减小了通信延迟。