Linux TCP_DEFER_ACCEPT的作用
来源:互联网 发布:淘宝手机助手好用吗 编辑:程序博客网 时间:2024/06/05 03:37
1. TCP服务端
int serverSocket = socket(AF_INET, SOCK_STREAM, 0); ...... bind(serverSocket, (struct sockaddr *)&server_addr, sizeof(server_addr));listen(serverSocket, 5);while(1){int client = accept(serverSocket, (struct sockaddr*)&clientAddr, (socklen_t*)&addr_len); while(not_finished){iDataNum = recv(client, buffer, 1024, 0);//process the data received...}}
2. 连接的建立
client server
--------------SYNC--------------->>
<<----------SYNC/ACK---------------
---------------ACK--------------->> accept() returns, but blocks on recv() immediately;
3. 对于HTTP
其实1中的TCP服务端的例子和HTTP服务端很像:建立连接后,立即读数据(读取HTTP请求)。在这种情况下,client发出ACK之后,server被唤醒(accept返回),并立即试图读数据,由于client还没有发数据,server又再度阻塞。这对于调度是一种浪费。另外,client发的那个ACK也没有实际作用,是不必要的。
假如server端内核忽略client发的ACK,而直接等待数据,数据收到之后再唤醒serve(accept返回),server醒来后就可以直接得到数据并处理。这就是TCP_DEFER_ACCEPT的作用。
server端的socket fd是处于listen状态的,设置TCP_DEFER_ACCEPT之后,内核就会忽略ACK,接收到数据之后再唤醒server(accept返回)。
另外,试想,既然server端忽略ACK,那么客户端可不可以不发送ACK,而直接发送数据呢,这样就节省了一次传输?答案是可以的:TCP_DEFER_ACCEPT设置在client端的socket fd上,就能达到这个效果。
问题:客户端如何知道服务端设置了TCP_DEFER_ACCEPT呢?假如服务端没有设置,而自己设置了,会不会出错?我猜应该不会出错,client本该使用序列号X发送ACK,现在使用序列号X直接发送数据,也符合TCP协议,验证一下。首先,写个简单的TCP服务端客户端(可能不够严谨,不过作为我们这个验证是够了)
服务端:
#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; } 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>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"); 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; } close(clientSock); return 0;}
3.1 client和server都不设置TCP_DEFER_ACCEPT
# gcc server.c -o server# gcc client.c -o client
运行server和client,并通过
# tcpdump -i 8 -x tcp and host 127.0.0.1 and port 7890
来抓包。我们分析6个标志位:URG, ACK,PSH, RST, SYN, FIN。
其中,对于我们日常的分析有用的就是其中五个,它们的含义是:
ACK表示响应;
PSH表示有 DATA数据传输;
RST表示连接重置;
SYN表示建立连接;
FIN表示关闭连接;
前四个包是:
# tcpdump -i 8 -x tcp and host 127.0.0.1 and port 7890tcpdump: verbose output suppressed, use -v or -vv for full protocol decodelistening on lo, link-type EN10MB (Ethernet), capture size 65535 bytes19:45:03.166902 IP localhost.35151 > localhost.7890: Flags [S], seq 587644530, win 43690, options [mss 65495,sackOK,TS val 7427280 ecr 0,nop,wscale 7], length 00x0000: 4500 003c eaa2 4000 4006 5217 7f00 00010x0010: 7f00 0001 894f 1ed2 2306 be72 0000 00000x0020: a002 aaaa fe30 0000 0204 ffd7 0402 080a <-- a002最后6bit是000010,SYNC位被设置0x0030: 0071 54d0 0000 0000 0103 030713:05:28.132948 IP localhost.7890 > localhost.35151: Flags [S.], seq 3213519726, ack 587644531, win 43690, options [mss 65495,sackOK,TS val 7427280 ecr 7427280,nop,wscale 7], length 00x0000: 4500 003c 0000 4000 4006 3cba 7f00 00010x0010: 7f00 0001 1ed2 894f bf8a 6b6e 2306 be730x0020: a012 aaaa fe30 0000 0204 ffd7 0402 080a <-- a012最后6bit是010010,ACK和SYNC都被设置0x0030: 0071 54d0 0071 54d0 0103 030719:45:03.166941 IP localhost.35151 > localhost.7890: Flags [.], ack 1, win 342, options [nop,nop,TS val 7427280 ecr 7427280], length 00x0000: 4500 0034 eaa3 4000 4006 521e 7f00 00010x0010: 7f00 0001 894f 1ed2 2306 be73 bf8a 6b6f0x0020: 8010 0156 fe28 0000 0101 080a 0071 54d0 <-- 8010最后6bit是010000,ACK被设置0x0030: 0071 54d019:45:03.167723 IP localhost.35151 > localhost.7890: Flags [P.], seq 1:7, ack 1, win 342, options [nop,nop,TS val 7427280 ecr 7427280], length 60x0000: 4500 003a eaa4 4000 4006 5217 7f00 00010x0010: 7f00 0001 894f 1ed2 2306 be73 bf8a 6b6f0x0020: 8018 0156 fe2e 0000 0101 080a 0071 54d0 <-- 8018最后6bit是011000,ACK和PSH被设置,PSH表示包含负载数据0x0030: 0071 54d0 4865 6c6c 6f0a <-- Hello\n
3.2 server设置TCP_DEFER_ACCEPT而client不设置
# gcc -DDEFER_ACCEPT server.c -o server# gcc client.c -o client前四个包是:
19:57:33.551706 IP localhost.35153 > localhost.7890: Flags [S], seq 899612856, win 43690, options [mss 65495,sackOK,TS val 8177664 ecr 0,nop,wscale 7], length 00x0000: 4500 003c c8b8 4000 4006 7401 7f00 00010x0010: 7f00 0001 8951 1ed2 359f 00b8 0000 00000x0020: a002 aaaa fe30 0000 0204 ffd7 0402 080a <-- a002最后6bit是000010,SYNC位被设置0x0030: 007c c800 0000 0000 0103 030720:20:03.622935 IP localhost.7890 > localhost.35153: Flags [S.], seq 2767718812, ack 899612857, win 43690, options [mss 65495,sackOK,TS val 8177664 ecr 8177664,nop,wscale 7], length 00x0000: 4500 003c 0000 4000 4006 3cba 7f00 00010x0010: 7f00 0001 1ed2 8951 a4f8 099c 359f 00b90x0020: a012 aaaa fe30 0000 0204 ffd7 0402 080a <-- a012最后6bit是010010,ACK和SYNC都被设置0x0030: 007c c800 007c c800 0103 030719:57:33.551798 IP localhost.35153 > localhost.7890: Flags [.], ack 1, win 342, options [nop,nop,TS val 8177665 ecr 8177664], length 00x0000: 4500 0034 c8b9 4000 4006 7408 7f00 00010x0010: 7f00 0001 8951 1ed2 359f 00b9 a4f8 099d0x0020: 8010 0156 fe28 0000 0101 080a 007c c801 <-- 8010最后6bit是010000,ACK被设置0x0030: 007c c80019:57:33.552000 IP localhost.35153 > localhost.7890: Flags [P.], seq 1:7, ack 1, win 342, options [nop,nop,TS val 8177665 ecr 8177664], length 60x0000: 4500 003a c8ba 4000 4006 7401 7f00 00010x0010: 7f00 0001 8951 1ed2 359f 00b9 a4f8 099d0x0020: 8018 0156 fe2e 0000 0101 080a 007c c801 <-- 8018最后6bit是011000,ACK和PSH被设置0x0030: 007c c800 4865 6c6c 6f0a <-- Hello\n
从抓包上,并看不出什么不同;但是,accept在接到第三个包时并不返回,而直到接到第四个包才返回。返回后,recv立即就能接收到数据,不需要再度阻塞。
3.3 client和server都设置TCP_DEFER_ACCEPT
# gcc -DDEFER_ACCEPT server.c -o server# gcc -DDEFER_ACCEPT client.c -o client
前三个包是:
20:05:33.235173 IP localhost.35154 > localhost.7890: Flags [S], seq 3639246407, win 43690, options [mss 65495,sackOK,TS val 8657348 ecr 0,nop,wscale 7], length 00x0000: 4500 003c 6783 4000 4006 d536 7f00 00010x0010: 7f00 0001 8952 1ed2 d8ea 7e47 0000 00000x0020: a002 aaaa fe30 0000 0204 ffd7 0402 080a <-- a002最后6bit是000010,SYNC被设置0x0030: 0084 19c4 0000 0000 0103 030716:37:06.900176 IP localhost.7890 > localhost.35154: Flags [S.], seq 4073929758, ack 3639246408, win 43690, options [mss 65495,sackOK,TS val 8657348 ecr 8657348,nop,wscale 7], length 00x0000: 4500 003c 0000 4000 4006 3cba 7f00 00010x0010: 7f00 0001 1ed2 8952 f2d3 3c1e d8ea 7e480x0020: a012 aaaa fe30 0000 0204 ffd7 0402 080a <-- a012最后6bit是010010,ACK和SYNC都被设置0x0030: 0084 19c4 0084 19c4 0103 030720:05:33.235891 IP localhost.35154 > localhost.7890: Flags [P.], seq 1:7, ack 1, win 342, options [nop,nop,TS val 8657349 ecr 8657348], length 60x0000: 4500 003a 6784 4000 4006 d537 7f00 00010x0010: 7f00 0001 8952 1ed2 d8ea 7e48 f2d3 3c1f0x0020: 8018 0156 fe2e 0000 0101 080a 0084 19c5 <-- 8018最后6bit是011000,ACK和PSH被设置0x0030: 0084 19c4 4865 6c6c 6f0a <--Hello\n
可见,节省了一个包。
3.4 client设置TCP_DEFER_ACCEPT而server不设置
# gcc server.c -o server# gcc -DDEFER_ACCEPT client.c -o client前三个包是:
20:13:21.659260 IP localhost.35155 > localhost.7890: Flags [S], seq 1415645579, win 43690, options [mss 65495,sackOK,TS val 9125772 ecr 0,nop,wscale 7], length 00x0000: 4500 003c 3607 4000 4006 06b3 7f00 00010x0010: 7f00 0001 8953 1ed2 5461 098b 0000 00000x0020: a002 aaaa fe30 0000 0204 ffd7 0402 080a <-- a002最后6bit是000010,SYNC被设置0x0030: 008b 3f8c 0000 0000 0103 030723:28:13.129345 IP localhost.7890 > localhost.35155: Flags [S.], seq 1092521209, ack 1415645580, win 43690, options [mss 65495,sackOK,TS val 9125772 ecr 9125772,nop,wscale 7], length 00x0000: 4500 003c 0000 4000 4006 3cba 7f00 00010x0010: 7f00 0001 1ed2 8953 411e 8cf9 5461 098c0x0020: a012 aaaa fe30 0000 0204 ffd7 0402 080a <-- a012最后6bit是010010,ACK和SYNC都被设置0x0030: 008b 3f8c 008b 3f8c 0103 030720:13:21.659471 IP localhost.35155 > localhost.7890: Flags [P.], seq 1:7, ack 1, win 342, options [nop,nop,TS val 9125772 ecr 9125772], length 60x0000: 4500 003a 3608 4000 4006 06b4 7f00 00010x0010: 7f00 0001 8953 1ed2 5461 098c 411e 8cfa0x0020: 8018 0156 fe2e 0000 0101 080a 008b 3f8c <-- 8018最后6bit是011000,ACK和PSH都被设置0x0030: 008b 3f8c 4865 6c6c 6f0a <-- Hello\n可见,即使server端不设置TCP_DEFER_ACCEPT,客户端省掉一个ACK也不出错。其实,这个ACK没有被省掉,而是和数据包合在一起了。所以,只要连接建立后,第一个包由客户端发出,客户端就可以设置这个标记,从而省掉一次传输。HTTP满足这个特性,但FTP不满足。见第4节。
4. 对于FTP
这个优化不能适用于FTP,原因是:连接建立之后,服务端应该立即发送数据给客户端,而不是立即等待接收数据。服务端发的数据就是提示符prompt,客户端收到提示符之后,才能上传下载文件。别的系统也可能有类似的东西,但名字可能不同,例如FreeBSD上叫SO_ACCEPTFILTER.
- Linux TCP_DEFER_ACCEPT的作用
- linux网络编程TCP_DEFER_ACCEPT
- linux网络编程TCP_DEFER_ACCEPT
- linux网络编程TCP_DEFER_ACCEPT
- TCP_DEFER_ACCEPT
- TCP_DEFER_ACCEPT
- TCP_DEFER_ACCEPT
- TCP_DEFER_ACCEPT
- linux下tcp选项TCP_DEFER_ACCEPT详解
- 一个很特别的TCP选项TCP_DEFER_ACCEPT
- 一个很特别的TCP选项TCP_DEFER_ACCEPT
- 一个很特别的TCP选项TCP_DEFER_ACCEPT .
- tcp的半连接攻击和全连接攻击--TCP_DEFER_ACCEPT
- tcp的半连接攻击和全连接攻击--TCP_DEFER_ACCEPT
- tcp的半连接攻击和全连接攻击--TCP_DEFER_ACCEPT
- tcp的半连接攻击和全连接攻击--TCP_DEFER_ACCEPT
- tcp的半连接攻击和全连接攻击--TCP_DEFER_ACCEPT
- TCP_DEFER_ACCEPT设置
- Android异常—捕获并保存到SD卡中
- Excel VBA Table Tools / DB facilities
- 常见机器学习算法比较
- 调用scanf函数的一个陷阱
- jQuery实现标签子元素的添加和赋值
- Linux TCP_DEFER_ACCEPT的作用
- C++ algorithm
- struts2 validation.xml 验证
- 视图容器ViewPager简单操作
- 安卓仿微信录音功能实现
- halcon与VS2013的配置
- android效果TapBarMenu绘制底部导航栏的使用方式
- 怎样注册破解MyEclipse(绝对有效)?
- C++ Primer课后练习11.3,11.4,11.7,11.8,11.11,11.12,11.13,11.14