频道栏目
首页 > 程序开发 > 综合编程 > 其他综合 > 正文
Linux TCP_FASTOPEN的作用
2017-01-10 10:05:00         来源:for_tech的博客  
收藏   我要投稿

1. 原理1.

1. 客户端发送一个SYN包到服务器,这个包中携带了Fast Open Cookie Request;

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

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

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

1. 客户端发送一个SYN包,这个包比较特殊,因为它携带应用数据和cookie;

2. 服务器验证这个cookie,如果合法,服务器发送一个SYN+ACK,这个ACK同时确认SYN和数据。然后数据被传递到应用进程;

如果不合法,服务器丢弃数据,发送一个SYN+ACK,这个ACK只确认SYN,接下来走三次握手的普通流程;

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

4. 如果验证不合法(客户端在SYN中带的数据没被确认),客户端发送ACK确认服务器的SYN;并且,数据会在ACK包中重传;

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

\

2. 测试环境1. 测试环境

CentOS7虚拟机

3. 启用TCP_FASTOPEN

# echo 3 > /proc/sys/net/ipv4/tcp_fastopen 

4. 测试代码

服务端

#include <unistd.h>  
#include <string.h>  
  
#include <sys/types.h>  
#include <sys/socket.h>  
#include <netinet/in.h>  
#include <netinet/tcp.h>  
  
int main()  
{  
  int serverSock;  
  struct sockaddr_in addr;  
  
  int clientSock;  
  struct sockaddr_in clientAddr;  
  int addrLen;  
  
  char buf[1024];  
  int read;  
  
  serverSock = socket(AF_INET, SOCK_STREAM, 0);  
  
  if(serverSock == -1)  
  {  
    write(STDERR_FILENO, "failed!\n",8);  
    return 1;  
  }  
  
#ifdef DEFER_ACCEPT  
  int soValue = 1;  
  if(setsockopt(serverSock, IPPROTO_TCP, TCP_DEFER_ACCEPT, &soValue, sizeof(soValue))<0)  
  {  
    write(STDERR_FILENO, "failed!\n",8);  
    return 10;  
  }  
#endif  
  
  memset(&addr, 0, sizeof(addr));  
  addr.sin_family = AF_INET;  
  addr.sin_port = htons(7890);  
  addr.sin_addr.s_addr=inet_addr("127.0.0.1");  
  
  if(bind(serverSock, (struct sockaddr*)&addr, sizeof(addr))<0)  
  {  
    write(STDERR_FILENO, "failed!\n",8);  
    return 2;  
  }  
  
#ifdef FAST_OPEN  
  int qlen=5;  
  setsockopt(serverSock, SOL_TCP, TCP_FASTOPEN, &qlen, sizeof(qlen));  
#endif  
  
  if(listen(serverSock,511)<0)  
  {  
    write(STDERR_FILENO, "failed!\n",8);  
    return 3;  
  }  
  
  while(1)  
  {  
    addrLen = sizeof(clientAddr);  
    clientSock = accept(serverSock, (struct sockaddr*)&clientAddr, &addrLen);  
    if(clientSock<0)  
    {  
      write(STDERR_FILENO, "failed!\n",8);  
      return 4;  
    }  
  
    read = recv(clientSock, buf, 1024, 0);  
    write(STDOUT_FILENO, buf, read);  
    close(clientSock);  
  }  
  
  return 0;  
}

客户端

#include <unistd.h>  
#include <string.h>  
  
#include <sys/types.h>  
#include <sys/socket.h>  
#include <netinet/in.h>  
  
#include <netinet/tcp.h>  
  
#ifndef MSG_FASTOPEN  
#define MSG_FASTOPEN   0x20000000  
#endif  
  
int main()  
{  
  int clientSock;  
  struct sockaddr_in addr;  
  
  clientSock = socket(AF_INET, SOCK_STREAM, 0);  
  
  if(clientSock == -1)  
  {  
    write(STDERR_FILENO, "failed!\n",8);  
    return 1;  
  }  
  
#ifdef DEFER_ACCEPT  
  int soValue = 1;  
  if(setsockopt(clientSock, IPPROTO_TCP, TCP_DEFER_ACCEPT, &soValue, sizeof(soValue))<0)  
  {  
    write(STDERR_FILENO, "failed!\n",8);  
    return 10;  
  }  
#endif  
  
  memset(&addr, 0, sizeof(addr));  
  addr.sin_family = AF_INET;  
  addr.sin_port = htons(7890);  
  addr.sin_addr.s_addr=inet_addr("127.0.0.1");  
  
#ifdef FAST_OPEN  
  
int ret = sendto(clientSock, "Hello\n", 6, MSG_FASTOPEN,  
                   (struct sockaddr *)&addr, sizeof(addr));  
  
if(ret<0)  
{  
    write(STDERR_FILENO, "failed!\n",8);  
    return 11;  
}  
  
#else  
  if(connect(clientSock, (struct sockaddr*)&addr, sizeof(addr))<0)  
  {  
    write(STDERR_FILENO, "failed!\n",8);  
    return 2;  
  }  
  
  if(send(clientSock, "Hello\n", 6, 0)<0)  
  {  
    write(STDERR_FILENO, "failed!\n",8);  
    return 3;  
  }  
#endif  
  
  close(clientSock);  
  
  return 0;  
}

5. 抓包与分析

# tcpdump -i 8 -x tcp and host 127.0.0.1 and port 7890
# gcc -DFAST_OPEN  server.c -o server
# gcc -DFAST_OPEN  client.c -o client

启动server。第一次运行客户端:
23:10:36.642141 IP localhost.46214 > localhost.7890: Flags [S], seq 3807185046, win 43690, options [mss 65495,sackOK,TS val 1620536 ecr 0,nop,wscale 7,unknown-34,nop,nop], length 0
	0x0000:  4500 0040 b2ae 4000 4006 8a07 7f00 0001
	0x0010:  7f00 0001 b486 1ed2 e2ed 0896 0000 0000
	0x0020:  b002 aaaa fe34 0000 0204 ffd7 0402 080a    <-- b002最后6bit是000010,SYN被设置
	0x0030:  0018 ba38 0000 0000 0103 0307 2202 0101    <-- 2202 0101 就是Fast Open Cookie Request,别的系统可能不同
21:22:31.043164 IP localhost.7890 > localhost.46214: Flags [S.], seq 739592308, ack 3807185047, win 43690, options [mss 65495,sackOK,TS val 1620536 ecr 1620536,nop,wscale 7,unknown-34 0xbba2f23225c4afcc,nop,nop], length 0
	0x0000:  4500 0048 0000 4000 4006 3cae 7f00 0001
	0x0010:  7f00 0001 1ed2 b486 2c15 4874 e2ed 0897
	0x0020:  d012 aaaa fe3c 0000 0204 ffd7 0402 080a    <-- d012最后6bit是010010,ACK和SYN被设置
	0x0030:  0018 ba38 0018 ba38 0103 0307 220a bba2
	0x0040:  f232 25c4 afcc 0101                        <-- f232 25c4 afcc 0101 就是cookie 
23:10:36.643626 IP localhost.46214 > localhost.7890: Flags [P.], seq 1:7, ack 1, win 342, options [nop,nop,TS val 1620538 ecr 1620536], length 6
	0x0000:  4500 003a b2af 4000 4006 8a0c 7f00 0001
	0x0010:  7f00 0001 b486 1ed2 e2ed 0897 2c15 4875
	0x0020:  8018 0156 fe2e 0000 0101 080a 0018 ba3a    <-- 8018最后6bit是011000,ACK和PSH被设置,结束建连,并发送数据
	0x0030:  0018 ba38 4865 6c6c 6f0a                   <-- Hello\n
23:10:36.643648 IP localhost.7890 > localhost.46214: Flags [.], ack 7, win 342, options [nop,nop,TS val 1620538 ecr 1620538], length 0
	0x0000:  4500 0034 f24f 4000 4006 4a72 7f00 0001
	0x0010:  7f00 0001 1ed2 b486 2c15 4875 e2ed 089d
	0x0020:  8010 0156 fe28 0000 0101 080a 0018 ba3a
	0x0030:  0018 ba3a
保持server继续运行,第二次运行客户端:
23:15:11.968769 IP localhost.46215 > localhost.7890: Flags [S], seq 445095168:445095174, win 43690, options [mss 65495,sackOK,TS val 0 ecr 0,nop,wscale 7,unknown-34 0xbba2f23225c4afcc,nop,nop], length 6
	0x0000:  4500 004e 70b7 4000 4006 cbf0 7f00 0001
	0x0010:  7f00 0001 b487 1ed2 1a87 9d00 0000 0000
	0x0020:  d002 aaaa fe42 0000 0204 ffd7 0402 080a  <-- d002最后6bit是000010,SYN被设置,建连
	0x0030:  0000 0000 0000 0000 0103 0307 220a bba2  
	0x0040:  f232 25c4 afcc 0101 4865 6c6c 6f0a       <-- 同时带有cookie和用户数据Hello\n
13:51:11.779261 IP localhost.7890 > localhost.46215: Flags [S.], seq 586801399, ack 445095175, win 43690, options [mss 65495,sackOK,TS val 1895863 ecr 0,nop,wscale 7], length 0
	0x0000:  4500 003c 0000 4000 4006 3cba 7f00 0001
	0x0010:  7f00 0001 1ed2 b487 22f9 e0f7 1a87 9d07
	0x0020:  a012 aaaa fe30 0000 0204 ffd7 0402 080a  <-- a012最后6bit是010010,ACK和SYN被设置
	0x0030:  001c edb7 0000 0000 0103 0307
23:15:11.968877 IP localhost.46215 > localhost.7890: Flags [.], ack 1, win 342, options [nop,nop,TS val 1895863 ecr 1895863], length 0
	0x0000:  4500 0034 70b8 4000 4006 cc09 7f00 0001
	0x0010:  7f00 0001 b487 1ed2 1a87 9d07 22f9 e0f8
	0x0020:  8010 0156 fe28 0000 0101 080a 001c edb7  <-- 8010最后6bit是010000,ACK被设置,没有PSH,即没有用户数据
	0x0030:  001c edb7
23:15:11.968932 IP localhost.7890 > localhost.46215: Flags [F.], seq 1, ack 1, win 342, options [nop,nop,TS val 1895863 ecr 0], length 0
	0x0000:  4500 0034 0d4f 4000 4006 2f73 7f00 0001
	0x0010:  7f00 0001 1ed2 b487 22f9 e0f8 1a87 9d07
	0x0020:  8011 0156 fe28 0000 0101 080a 001c edb7  <-- 8011最后6bit是,010001,ACK和FIN被设置
	0x0030:  0000 0000
23:15:11.969022 IP localhost.46215 > localhost.7890: Flags [F.], seq 1, ack 2, win 342, options [nop,nop,TS val 1895863 ecr 1895863], length 0
	0x0000:  4500 0034 70b9 4000 4006 cc08 7f00 0001
	0x0010:  7f00 0001 b487 1ed2 1a87 9d07 22f9 e0f9
	0x0020:  8011 0156 fe28 0000 0101 080a 001c edb7
	0x0030:  001c edb7

可见,启用TCP_FASTOPEN,数据能尽快传给server。另外,即使server端没有启用TCP_FASTOPEN而client端启用了,也不会出错,这时Fast Open Cookie Request不被处理,走正常三次握手。

点击复制链接 与好友分享!回本站首页
上一篇:BDPSNR的MATLAB实现
下一篇:thinkphp nginx下面完美配置解决404 file not found
相关文章
图文推荐
文章
推荐
点击排行

关于我们 | 联系我们 | 广告服务 | 投资合作 | 版权申明 | 在线帮助 | 网站地图 | 作品发布 | Vip技术培训 | 举报中心

版权所有: 红黑联盟--致力于做实用的IT技术学习网站