引言

三次握手的过程中,当用户首次访问server时,发送syn包,server根据用户IP生成cookie,并与syn+ack一同发回client;client再次访问server时,在syn包携带TCP cookie;如果server校验合法,则在用户回复ack前就可以直接发送数据;否则按照正常三次握手进行。

TFO提高性能的关键是省去了热请求的三次握手,这在充斥着小对象的移动应用场景中能够极大提升性能。

Google研究发现TCP 二次握手是页面延迟时间的重要部分,所以提出TFO

TFO的fast open标志体现在TCP报文的头部的OPTION字段

TCP Fast Open的标准文档是rfc7413

TFO与2.6.34内核合并到主线,lwn通告地址

TFO的使用目前还是有些复杂的,从linux的network文档来看

TFO的配置说明:

tcp_fastopen - INTEGER
Enable TCP Fast Open feature (draft-ietf-tcpm-fastopen) to send data
in the opening SYN packet. To use this feature, the client application
must use sendmsg() or sendto() with MSG_FASTOPEN flag rather than
connect() to perform a TCP handshake automatically. The values (bitmap) are
1: Enables sending data in the opening SYN on the client w/ MSG_FASTOPEN.
2: Enables TCP Fast Open on the server side, i.e., allowing data in
a SYN packet to be accepted and passed to the application before
3-way hand shake finishes.
4: Send data in the opening SYN regardless of cookie availability and
without a cookie option.
0x100: Accept SYN data w/o validating the cookie.
0x200: Accept data-in-SYN w/o any cookie option present.
0x400/0x800: Enable Fast Open on all listeners regardless of the
TCP_FASTOPEN socket option. The two different flags designate two
different ways of setting max_qlen without the TCP_FASTOPEN socket
option. Default: 1 Note that the client & server side Fast Open flags (1 and 2
respectively) must be also enabled before the rest of flags can take
effect. See include/net/tcp.h and the code for more details.

为了启用 tcp fast open功能

- client需要使用sendmsg或者sento系统调用,加上MSG_FASTOPEN flag,来连接server端,代替connect系统调用。
- 对server端不做要求。

linux系统(高版本内核)默认tcp_fastopen为1:

$ sysctl -a | grep fastopen

net.ipv4.tcp_fastopen = 1

测试代码: server.c

// reference: http://blog.csdn.net/hanhuili/article/details/8540227

#include <string.h>
#include <sys/types.h> /* See NOTES */
#include <sys/socket.h>
#include <netinet/in.h>
int main(){
int portno = 6666;
socklen_t clilen;
char buffer[256];
struct sockaddr_in serv_addr, cli_addr;
int cfd;
int sfd = socket(AF_INET, SOCK_STREAM, 0); // Create socket bzero((char *) &serv_addr, sizeof(serv_addr));
serv_addr.sin_family = AF_INET;
serv_addr.sin_addr.s_addr = INADDR_ANY;
serv_addr.sin_port = htons(portno);
bind(sfd, &serv_addr,sizeof(serv_addr)); // Bind to well known address int qlen = 5; // Value to be chosen by application
int err = setsockopt(sfd, IPPROTO_TCP/*SOL_TCP*/, 23/*TCP_FASTOPEN*/, &qlen, sizeof(qlen)); listen(sfd,1); // Mark socket to receive connections while(1){
cfd = accept(sfd, NULL, 0); // Accept connection on new socket while(1){
int len = read(cfd,buffer,256);
if(len)
printf("tcp fast open: %s\n",buffer);
else
break;
// read and write data on connected socket cfd
} memset(buffer, 0, 256);
close(cfd);
} }

测试代码:client.c


#include <sys/socket.h>
#include <netinet/in.h>
#include <netdb.h>
#include <errno.h>
int main(){
struct sockaddr_in serv_addr;
struct hostent *server; char *data = "Hello, tcp fast open";
int data_len = strlen(data); int sfd = socket(AF_INET, SOCK_STREAM, 0);
server = gethostbyname("localhost"); bzero((char *) &serv_addr, sizeof(serv_addr));
serv_addr.sin_family = AF_INET;
bcopy((char *)server->h_addr,
(char *)&serv_addr.sin_addr.s_addr,
server->h_length);
serv_addr.sin_port = htons(6666); // /usr/src/linux-headers-4.4.0-22/include/linux/socket.h:#define MSG_FASTOPEN 0x20000000 /* Send data in TCP SYN */ // int len = sendto(sfd, data, data_len, 0x20000000/*MSG_FASTOPEN*/, int len = sendto(sfd, data, data_len, MSG_FASTOPEN/*MSG_FASTOPEN*/,
(struct sockaddr *) &serv_addr, sizeof(serv_addr));
if(errno != 0){
printf("error: %s\n", strerror(errno));
}
close(sfd);
}

通信过程:tcpdump


$ sudo tcpdump -i any port 6666 -X # 第一次 ./client.o 00:29:34.820187 IP localhost.51388 > localhost.6666: Flags [S], seq 755101042, win 43690, options [mss 65495,sackOK,TS val 17053 ecr 0,nop,wscale 7,unknown-34,nop,nop], length 0
0x0000: 4500 0040 afef 4000 4006 8cc6 7f00 0001 E..@..@.@.......
0x0010: 7f00 0001 c8bc 1a0a 2d01 ed72 0000 0000 ........-..r....
0x0020: b002 aaaa fe34 0000 0204 ffd7 0402 080a .....4..........
0x0030: 0000 429d 0000 0000 0103 0307 2202 0101 ..B........."...
00:29:34.820284 IP localhost.6666 > localhost.51388: Flags [S.], seq 3725111481, ack 755101043, win 43690, options [mss 65495,sackOK,TS val 17053 ecr 17053,nop,wscale 7], length 0
0x0000: 4500 003c 0000 4000 4006 3cba 7f00 0001 E..<..@.@.<.....
0x0010: 7f00 0001 1a0a c8bc de08 b0b9 2d01 ed73 ............-..s
0x0020: a012 aaaa fe30 0000 0204 ffd7 0402 080a .....0..........
0x0030: 0000 429d 0000 429d 0103 0307 ..B...B.....
00:29:34.820372 IP localhost.51388 > localhost.6666: Flags [P.], seq 1:21, ack 1, win 342, options [nop,nop,TS val 17053 ecr 17053], length 20
0x0000: 4500 0048 aff0 4000 4006 8cbd 7f00 0001 E..H..@.@.......
0x0010: 7f00 0001 c8bc 1a0a 2d01 ed73 de08 b0ba ........-..s....
0x0020: 8018 0156 fe3c 0000 0101 080a 0000 429d ...V.<........B.
0x0030: 0000 429d 4865 6c6c 6f2c 2074 6370 2066 ..B.Hello,.tcp.f
0x0040: 6173 7420 6f70 656e ast.open
00:29:34.820433 IP localhost.6666 > localhost.51388: Flags [.], ack 21, win 342, options [nop,nop,TS val 17053 ecr 17053], length 0
0x0000: 4500 0034 f227 4000 4006 4a9a 7f00 0001 E..4.'@.@.J.....
0x0010: 7f00 0001 1a0a c8bc de08 b0ba 2d01 ed87 ............-...
0x0020: 8010 0156 fe28 0000 0101 080a 0000 429d ...V.(........B.
0x0030: 0000 429d ..B.
00:29:34.859246 IP localhost.6666 > localhost.51388: Flags [.], ack 22, win 342, options [nop,nop,TS val 17063 ecr 17053], length 0
0x0000: 4500 0034 f228 4000 4006 4a99 7f00 0001 E..4.(@.@.J.....
0x0010: 7f00 0001 1a0a c8bc de08 b0ba 2d01 ed88 ............-...
0x0020: 8010 0156 fe28 0000 0101 080a 0000 42a7 ...V.(........B.
0x0030: 0000 429d ..B. # 第二次 ./client.o 00:29:39.271936 IP localhost.51398 > localhost.6666: Flags [S], seq 2362540136, win 43690, options [mss 65495,sackOK,TS val 18166 ecr 0,nop,wscale 7,exp-tfo cookiereq], length 0
0x0000: 4500 0040 c69e 4000 4006 7617 7f00 0001 E..@..@.@.v.....
0x0010: 7f00 0001 c8c6 1a0a 8cd1 8068 0000 0000 ...........h....
0x0020: b002 aaaa fe34 0000 0204 ffd7 0402 080a .....4..........
0x0030: 0000 46f6 0000 0000 0103 0307 fe04 f989 ..F.............
00:29:39.271986 IP localhost.6666 > localhost.51398: Flags [S.], seq 3703577773, ack 2362540137, win 43690, options [mss 65495,sackOK,TS val 18166 ecr 18166,nop,wscale 7], length 0
0x0000: 4500 003c 0000 4000 4006 3cba 7f00 0001 E..<..@.@.<.....
0x0010: 7f00 0001 1a0a c8c6 dcc0 1cad 8cd1 8069 ...............i
0x0020: a012 aaaa fe30 0000 0204 ffd7 0402 080a .....0..........
0x0030: 0000 46f6 0000 46f6 0103 0307 ..F...F.....
00:29:39.272038 IP localhost.51398 > localhost.6666: Flags [P.], seq 1:21, ack 1, win 342, options [nop,nop,TS val 18166 ecr 18166], length 20
0x0000: 4500 0048 c69f 4000 4006 760e 7f00 0001 E..H..@.@.v.....
0x0010: 7f00 0001 c8c6 1a0a 8cd1 8069 dcc0 1cae ...........i....
0x0020: 8018 0156 fe3c 0000 0101 080a 0000 46f6 ...V.<........F.
0x0030: 0000 46f6 4865 6c6c 6f2c 2074 6370 2066 ..F.Hello,.tcp.f
0x0040: 6173 7420 6f70 656e ast.open
00:29:39.272072 IP localhost.6666 > localhost.51398: Flags [.], ack 21, win 342, options [nop,nop,TS val 18166 ecr 18166], length 0
0x0000: 4500 0034 5a58 4000 4006 e269 7f00 0001 E..4ZX@.@..i....
0x0010: 7f00 0001 1a0a c8c6 dcc0 1cae 8cd1 807d ...............}
0x0020: 8010 0156 fe28 0000 0101 080a 0000 46f6 ...V.(........F.
0x0030: 0000 46f6 ..F.
00:29:39.311280 IP localhost.6666 > localhost.51398: Flags [.], ack 22, win 342, options [nop,nop,TS val 18176 ecr 18166], length 0
0x0000: 4500 0034 5a59 4000 4006 e268 7f00 0001 E..4ZY@.@..h....
0x0010: 7f00 0001 1a0a c8c6 dcc0 1cae 8cd1 807e ...............~
0x0020: 8010 0156 fe28 0000 0101 080a 0000 4700 ...V.(........G.
0x0030: 0000 46f6 ..F.

奇怪的是,在代码中启用tcp_fastopen的结果和不启用,并没有区别。那这是什么原因呢?

通过搜索,发现在介绍tcp fast open优化shadowsocks时,设置net.ipv4.tcp_fastopen为3,虽然奇怪,但是可以试试:

$ sysctl -a | grep fastopen

net.ipv4.tcp_fastopen = 3

# 第一次,server返回cookie unknown-34 0x38af51c10bf41ca4 00:36:36.667932 IP localhost.52220 > localhost.6666: Flags [S], seq 3662514892, win 43690, options [mss 65495,sackOK,TS val 122515 ecr 0,nop,wscale 7,unknown-34,nop,nop], length 0
0x0000: 4500 0040 545f 4000 4006 e856 7f00 0001 E..@T_@.@..V....
0x0010: 7f00 0001 cbfc 1a0a da4d 8acc 0000 0000 .........M......
0x0020: b002 aaaa fe34 0000 0204 ffd7 0402 080a .....4..........
0x0030: 0001 de93 0000 0000 0103 0307 2202 0101 ............"...
00:36:36.667990 IP localhost.6666 > localhost.52220: Flags [S.], seq 3186866007, ack 3662514893, win 43690, options [mss 65495,sackOK,TS val 122515 ecr 122515,nop,wscale 7,unknown-34 0x38af51c10bf41ca4,nop,nop], length 0
0x0000: 4500 0048 0000 4000 4006 3cae 7f00 0001 E..H..@.@.<.....
0x0010: 7f00 0001 1a0a cbfc bdf3 b757 da4d 8acd ...........W.M..
0x0020: d012 aaaa fe3c 0000 0204 ffd7 0402 080a .....<..........
0x0030: 0001 de93 0001 de93 0103 0307 220a 38af ............".8.
0x0040: 51c1 0bf4 1ca4 0101 Q.......
00:36:36.668050 IP localhost.52220 > localhost.6666: Flags [P.], seq 1:21, ack 1, win 342, options [nop,nop,TS val 122515 ecr 122515], length 20
0x0000: 4500 0048 5460 4000 4006 e84d 7f00 0001 E..HT`@.@..M....
0x0010: 7f00 0001 cbfc 1a0a da4d 8acd bdf3 b758 .........M.....X
0x0020: 8018 0156 fe3c 0000 0101 080a 0001 de93 ...V.<..........
0x0030: 0001 de93 4865 6c6c 6f2c 2074 6370 2066 ....Hello,.tcp.f
0x0040: 6173 7420 6f70 656e ast.open
00:36:36.668109 IP localhost.6666 > localhost.52220: Flags [.], ack 21, win 342, options [nop,nop,TS val 122515 ecr 122515], length 0
0x0000: 4500 0034 69cb 4000 4006 d2f6 7f00 0001 E..4i.@.@.......
0x0010: 7f00 0001 1a0a cbfc bdf3 b758 da4d 8ae1 ...........X.M..
0x0020: 8010 0156 fe28 0000 0101 080a 0001 de93 ...V.(..........
0x0030: 0001 de93 ....
00:36:36.707264 IP localhost.6666 > localhost.52220: Flags [.], ack 22, win 342, options [nop,nop,TS val 122525 ecr 122515], length 0
0x0000: 4500 0034 69cc 4000 4006 d2f5 7f00 0001 E..4i.@.@.......
0x0010: 7f00 0001 1a0a cbfc bdf3 b758 da4d 8ae2 ...........X.M..
0x0020: 8010 0156 fe28 0000 0101 080a 0001 de9d ...V.(..........
0x0030: 0001 de93 .... # 第二次,client发送请求时,将cookie写在syn包中,同时带上发送的数据;server端校验后(kernel和tcp/ip协议栈做校验)后返回成功,如此在3次握手中节省了一次rtt时间 00:36:38.744954 IP localhost.52226 > localhost.6666: Flags [S], seq 1820632025:1820632045, win 43690, options [mss 65495,sackOK,TS val 123034 ecr 0,nop,wscale 7,unknown-34 0x38af51c10bf41ca4,nop,nop], length 20
0x0000: 4500 005c 4343 4000 4006 f956 7f00 0001 E..\CC@.@..V....
0x0010: 7f00 0001 cc02 1a0a 6c84 a3d9 0000 0000 ........l.......
0x0020: d002 aaaa fe50 0000 0204 ffd7 0402 080a .....P..........
0x0030: 0001 e09a 0000 0000 0103 0307 220a 38af ............".8.
0x0040: 51c1 0bf4 1ca4 0101 4865 6c6c 6f2c 2074 Q.......Hello,.t
0x0050: 6370 2066 6173 7420 6f70 656e cp.fast.open
00:36:38.745022 IP localhost.6666 > localhost.52226: Flags [S.], seq 3848342665, ack 1820632046, win 43690, options [mss 65495,sackOK,TS val 123034 ecr 123034,nop,wscale 7], length 0
0x0000: 4500 003c 0000 4000 4006 3cba 7f00 0001 E..<..@.@.<.....
0x0010: 7f00 0001 1a0a cc02 e561 0c89 6c84 a3ee .........a..l...
0x0020: a012 aaaa fe30 0000 0204 ffd7 0402 080a .....0..........
0x0030: 0001 e09a 0001 e09a 0103 0307 ............
00:36:38.745072 IP localhost.52226 > localhost.6666: Flags [.], ack 1, win 342, options [nop,nop,TS val 123034 ecr 123034], length 0
0x0000: 4500 0034 4344 4000 4006 f97d 7f00 0001 E..4CD@.@..}....
0x0010: 7f00 0001 cc02 1a0a 6c84 a3ee e561 0c8a ........l....a..
0x0020: 8010 0156 fe28 0000 0101 080a 0001 e09a ...V.(..........
0x0030: 0001 e09a ....
00:36:38.745127 IP localhost.52226 > localhost.6666: Flags [F.], seq 1, ack 1, win 342, options [nop,nop,TS val 123034 ecr 123034], length 0
0x0000: 4500 0034 4345 4000 4006 f97c 7f00 0001 E..4CE@.@..|....
0x0010: 7f00 0001 cc02 1a0a 6c84 a3ee e561 0c8a ........l....a..
0x0020: 8011 0156 fe28 0000 0101 080a 0001 e09a ...V.(..........
0x0030: 0001 e09a ....
00:36:38.747232 IP localhost.6666 > localhost.52226: Flags [.], ack 2, win 342, options [nop,nop,TS val 123035 ecr 123034], length 0
0x0000: 4500 0034 ec10 4000 4006 50b1 7f00 0001 E..4..@.@.P.....
0x0010: 7f00 0001 1a0a cc02 e561 0c8a 6c84 a3ef .........a..l...
0x0020: 8010 0156 fe28 0000 0101 080a 0001 e09b ...V.(..........
0x0030: 0001 e09a ....
  • 上述通信过程中

    • 第一次,server返回cookie unknown-34 0x38af51c10bf41ca4
    • 第二次,client发送请求时,将cookie写在syn包中,同时带上发送的数据;server端校验后(kernel和tcp/ip协议栈做校验)后返回成功,如此在3次握手中节省了一次rtt时间
  • 也就是说,在net.ipv4.tcp_fastopen设置为3时,tcp fastopen特性使能

关于如何使能TFO,在前文中的TFO的配置说明中,我们可以看到,

	The values (bitmap) are
1: Enables sending data in the opening SYN on the client w/ MSG_FASTOPEN.
使能client端的TFO特性
2: Enables TCP Fast Open on the server side, i.e., allowing data in
a SYN packet to be accepted and passed to the application before
3-way hand shake finishes.
使能server端的TFO特性
4: Send data in the opening SYN regardless of cookie availability and
without a cookie option.

并且这个标志是位操作,如果我在本机做实验,将本机作为sever端和client端的话,需要两个位都使能,所以应该将该值设置为3.

同时我们可以看到,tcp fast open是非常向后兼容的,升级成本不高,需要高于3.7+版本内核,但总体来说值得采用。

nginx 1.5.18(2013年)开始支持tcp fast open

TODO LIST

  • TFO在移动端场景中的性能体现:android+nginx
  • tcp fast open 在内核中的实现

参考链接

tcp_fast_open的概念 作用以及实现(转)的更多相关文章

  1. Java 之 Serializable 序列化和反序列化的概念,作用的通俗易懂的解释

    遇到这个 Java Serializable 序列化这个接口,我们可能会有如下的问题a,什么叫序列化和反序列化b,作用.为啥要实现这个 Serializable 接口,也就是为啥要序列化c,seria ...

  2. 全面解读Oracle同义词的概念作用、创建删除查看及Oracle的db link

    Oracle的同义词(synonyms)从字面上理解就是别名的意思,和视图的功能类似,就是一种映射关系. 在Oracle中对用户的管理是使用权限的方式来管理的,也就是说,如果我们想使用数据库,我们就必 ...

  3. [转]Java 之 Serializable 序列化和反序列化的概念,作用的通俗易懂的解释

    原文地址:https://blog.csdn.net/qq_27093465/article/details/78544505 遇到这个 Java Serializable 序列化这个接口,我们可能会 ...

  4. HTTP协议的概念作用和特点

    Web交互的基本流程 请求:客户端根据服务器地址把数据发送给服务器的过程叫请求. 相应:服务器将请求的处理结果发送给浏览器的过程叫响应. 什么是HTTP? 概念:超文本传输协议. HTTP有什么作用? ...

  5. Maven是什么? Maven的概念+作用+仓库的介绍+常用命令

    Maven系列1 1.什么是Maven? Maven是一个项目管理工具,它包含了一个对象模型.一组标准集合,一个依赖管理系统.和用来运行定义在生命周期阶段中插件目标和逻辑. 核心功能 Maven的核心 ...

  6. CSS 概念 & 作用

    http://www.cnblogs.com/moveofgod/archive/2012/09/18/2691101.html 式样定义   如何显示 HTML内容 通常存储在式样表中 作用 : 解 ...

  7. C#反射技术概念作用和要点

    反射(Reflection)是.NET中的重要机制,通过放射,可以在运行时获得.NET中每一个类型(包括类.结构.委托.接口和枚举等)的成员,包括方法.属性.事件,以及构造函数等.还可以获得每个成员的 ...

  8. CDN概念+作用+特点+原理

    CDN的全称是Content Delivery Network,即内容分发网络.其目的是通过在现有的Internet中增加一层新的网络架构,将网站的内容发布到最接近用户的网络"边缘" ...

  9. C++构造函数概念作用

    作用: 对对象进行初始化,如给成员变量赋初值,而不用专门再写初始化函数. 防止有些对象没被初始化就使用,导致程序出错. 要求: 名字与类名相同,可以有参数,但不能有返回值(void也不行) 编译时: ...

随机推荐

  1. 开源监控系统Prometheus介绍

    前言 Prometheus是CNCF的一个开源项目,Google BorgMon监控系统的开源版本,是一个系统和服务的监控系统.周期性采集metrics指标,匹配规则和展示结果,以及触发某些条件的告警 ...

  2. Linq的优缺点

    优点: 1.Linq提供了不同数据源的抽象层,所以可以使用相同的语法访问不同的数据源(只要该数据源有提供程序即可) 2.Linq为底层的数据存储提供了一个强类型化的界面,可以把底层的数据作为对象来访问 ...

  3. ArcGIS api for js OverviewMap(鹰眼/概览图)

    说明.本篇博客中主要介绍 地图显示在某个div情况 1.运行效果 2.HTML <!DOCTYPE html> <html> <head> <meta htt ...

  4. Mac版 Intellij IDEA 激活

    第一步:修改这两个文件夹 进入跳转路径,输入 /private/etc/ 点击[前往] 同时加上: # Intellij IDEA0.0.0.0 account.jetbrains.com 第二部:在 ...

  5. x86 寻址学习

    x86 寻址方式众多,什么直接寻址.间接寻址.基址寻址.基址变址寻址等等让人眼花缭乱,而 AT&T 语法对内存寻址方式做了一个很好的统一,其格式为 section:displacement(b ...

  6. 【docker】开启remote api访问,并使用TLS加密

    背景: docker默认是能使用本地的socket进行管理,这个在集群中使用的时候很不方便,因为很多功能还是需要链接docker服务进行操作,docker默认也可以开启tcp访问,但是这就相当于把整个 ...

  7. 友盟分享到微信的几点备忘(IOS)

    1.下载最新的友盟分享版本,参考友盟官方的demo 2.注册微信开放平台用户,不是公众平台,注册应用 3.参考文档和demo,加入sdk包和相应的lib 4.在plist加入URL types.URL ...

  8. Spark与缓存

    预期成果 1.1   当前问题 当前以图搜图应用存在的问题: 当前使用spark RDD方案无法达到数据实时加载(每10分钟加载一次,虽然可配,但太短可能会有问题) Spark RDD内存会被分为两部 ...

  9. Data Structure Binary Tree: Level order traversal in spiral form

    http://www.geeksforgeeks.org/level-order-traversal-in-spiral-form/ #include <iostream> #includ ...

  10. JS中继承方式总结

    说在前面:为了使代码更为简洁方便理解, 本文中的代码均将"非核心实现"部分的代码移出. 一.原型链方式关于原型链,可点击<深入浅出,JS原型链的工作原理>,本文不再重复 ...