网络编程学习笔记7-TCP使用的注意事项(附代码)
Nagle和NODELAY,视频p18
本笔记参考的视频链接:https://www.bilibili.com/video/BV1Ht411p7wx?p=18 库链接:https://github.com/chenshuo/muduo
SIGPIPE
当服务器close一个连接时,若client端接着发数据
根据TCP 协议的规定,会收到一个RST响应,client再往这个服务器发送数据时,系统会发出一个SIGPIPE信号给进程,告诉进程这个连接已经断开了,不要再写了
根据信号的默认处理规则SIGPIPE信号的默认执行动作是terminate(终止、退出),所以client会退出,server也会退出
为了不影响服务端和其他客户端,可以忽略SIGPIPE
Nagle
Nagle算法主要是避免发送小的数据包,要求TCP连接上最多只能有一个未被确认的小分组,在该分组的确认到达之前不能发送其他的小分组。相反,TCP收集这些少量的小分组,并在确认到来时以一个分组的方式发出去。
//MSS最大段大小
if there is new data to send //如果还有数据发送
if the window size >= MSS and available data is >= MSS //如果发送大小大于MSS,则发送MSS大小的数据,多出MSS的数据下一个RTT发送
send complete MSS segment now
else
if there is unconfirmed data still in the pipe //如果还有未收到前一个数据ACK的数据,等待ACK
enqueue data in the buffer until an acknowledge is received
else
send data immediately //小于MSS而且都收到了前一个数据的ACK,则直接发送
end if
end if
end if
Nagle算法只允许一个未被ACK的包存在,它并不管包的大小,所以只要有一个没收到ACK,则会一直等下去
因为Nagle要等待确认后才能发送,所以会有一定延迟
而且这里面有着ACK延迟机制,当Server端收到数据之后,它并不会马上向client端发送ACK,而是会将ACK的发送延迟一段时间(假设为t),它希望在t时间内server端会向client端发送应答数据,这样ACK就能够和应答数据一起发送,就像是应答数据捎带着ACK过去
所以一般发送延迟出现40ms以上的原因是Nagle算法在捣乱
TCP_NODELAY
TCP_NODELAY就是禁用Nagle算法
验证Nagle和TCP_NODELAY的延迟
因为找不到muduo对应的代码,但是我又想自己看看两者的延迟,所以我自己写了一个验证 这里参考muduo的例子,一个小文件分两次发送
//客户端
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<errno.h>
#include<unistd.h>
#include <arpa/inet.h>
#include<sys/types.h>
#include<sys/socket.h>
#include<netinet/in.h>
#include<netinet/tcp.h>
#define MAXLINE 20
int main(int argc, char** argv)
{
int sockfd, n;
char recvline[4096], sendhead[20], senddata[20];
struct sockaddr_in servaddr;
if( argc < 2){
printf("usage: ./client <ipaddress>\n");
exit(0);
}
if( (sockfd = socket(AF_INET, SOCK_STREAM, 0)) < 0){
printf("create socket error: %s(errno: %d)\n", strerror(errno),errno);
exit(0);
}
memset(&servaddr, 0, sizeof(servaddr));
servaddr.sin_family = AF_INET;
servaddr.sin_port = htons(6666);
if( inet_pton(AF_INET, argv[1], &servaddr.sin_addr) <= 0){
printf("inet_pton error for %s\n",argv[1]);
exit(0);
}
//设置tcp_nodelay
if(argc==3 && argv[2]=="nodelay")
{
int on = 1;
setsockopt(sockfd, SOL_SOCKET, SO_KEEPALIVE, (void *)&on, sizeof(on));
setsockopt( sockfd, IPPROTO_TCP, TCP_NODELAY, (void *)&on, sizeof(on));
}
if( connect(sockfd, (struct sockaddr*)&servaddr, sizeof(servaddr)) < 0){
printf("connect error: %s(errno: %d)\n",strerror(errno),errno);
exit(0);
}
printf("send msg to server: \n");
//fgets(sendline, 4096, stdin);
for(int i=0;i<MAXLINE;i++)
{
sendhead[i]='s';
senddata[i]='o';
}
//发送两次
if( send(sockfd, sendhead, strlen(sendhead), 0) < 0)
{
printf("send msg error: %s(errno: %d)\n", strerror(errno), errno);
exit(0);
}
if( send(sockfd, senddata, strlen(senddata), 0) < 0)
{
printf("send msg error: %s(errno: %d)\n", strerror(errno), errno);
exit(0);
}
close(sockfd);
exit(0);
}
//服务端
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<errno.h>
#include<unistd.h>
#include<sys/types.h>
#include<sys/socket.h>
#include<netinet/in.h>
#define MAXLINE 4096
int main(int argc, char** argv)
{
int listenfd, connfd;
struct sockaddr_in servaddr;
char buff[4096];
int n;
if( (listenfd = socket(AF_INET, SOCK_STREAM, 0)) == -1 ){
printf("create socket error: %s(errno: %d)\n",strerror(errno),errno);
exit(0);
}
memset(&servaddr, 0, sizeof(servaddr));
servaddr.sin_family = AF_INET;
servaddr.sin_addr.s_addr = htonl(INADDR_ANY);
servaddr.sin_port = htons(6666);
if( bind(listenfd, (struct sockaddr*)&servaddr, sizeof(servaddr)) == -1){
printf("bind socket error: %s(errno: %d)\n",strerror(errno),errno);
exit(0);
}
if( listen(listenfd, 10) == -1){
printf("listen socket error: %s(errno: %d)\n",strerror(errno),errno);
exit(0);
}
printf("======waiting for client's request======\n");
while(1){
if( (connfd = accept(listenfd, (struct sockaddr*)NULL, NULL)) == -1){
printf("accept socket error: %s(errno: %d)",strerror(errno),errno);
continue;
}
n = recv(connfd, buff, MAXLINE, 0);
buff[n] = '\0';
printf("recv msg from client: %s\n", buff);
close(connfd);
}
close(listenfd);
}
验证结果
在服务端tcpdump
sudo tcpdump -i ens33 -n tcp port 6666 -ttt
下面是结果
//没加nodelay
00:00:00.000000 IP 192.168.200.132.46442 > 192.168.200.134.ircu-2: Flags [S], seq 922712728, win 29200, options [mss 1460,sackOK,TS val 41496494 ecr 0,nop,wscale 7], length 0
00:00:00.000051 IP 192.168.200.134.ircu-2 > 192.168.200.132.46442: Flags [S.], seq 536372574, ack 922712729, win 28960, options [mss 1460,sackOK,TS val 41501953 ecr 41496494,nop,wscale 7], length 0
00:00:00.000296 IP 192.168.200.132.46442 > 192.168.200.134.ircu-2: Flags [.], ack 1, win 229, options [nop,nop,TS val 41496494 ecr 41501953], length 0
* 00:00:00.000247 IP 192.168.200.132.46442 > 192.168.200.134.ircu-2: Flags [P.], seq 1:21, ack 1, win 229, options [nop,nop,TS val 41496494 ecr 41501953], length 20
00:00:00.000031 IP 192.168.200.134.ircu-2 > 192.168.200.132.46442: Flags [.], ack 21, win 227, options [nop,nop,TS val 41501953 ecr 41496494], length 0
* 00:00:00.000068 IP 192.168.200.132.46442 > 192.168.200.134.ircu-2: Flags [FP.], seq 21:41, ack 1, win 229, options [nop,nop,TS val 41496494 ecr 41501953], length 20
00:00:00.000253 IP 192.168.200.134.ircu-2 > 192.168.200.132.46442: Flags [F.], seq 1, ack 42, win 227, options [nop,nop,TS val 41501953 ecr 41496494], length 0
00:00:00.000303 IP 192.168.200.132.46442 > 192.168.200.134.ircu-2: Flags [.], ack 2, win 229, options [nop,nop,TS val 41496495 ecr 41501953], length 0
//加了nodelay
00:00:28.461646 IP 192.168.200.132.46444 > 192.168.200.134.ircu-2: Flags [S], seq 2190537608, win 29200, options [mss 1460,sackOK,TS val 41524957 ecr 0,nop,wscale 7], length 0
00:00:00.000200 IP 192.168.200.134.ircu-2 > 192.168.200.132.46444: Flags [S.], seq 2615353636, ack 2190537609, win 28960, options [mss 1460,sackOK,TS val 41530416 ecr 41524957,nop,wscale 7], length 0
00:00:00.000249 IP 192.168.200.132.46444 > 192.168.200.134.ircu-2: Flags [.], ack 1, win 229, options [nop,nop,TS val 41524957 ecr 41530416], length 0
* 00:00:00.000153 IP 192.168.200.132.46444 > 192.168.200.134.ircu-2: Flags [P.], seq 1:21, ack 1, win 229, options [nop,nop,TS val 41524957 ecr 41530416], length 20
00:00:00.000084 IP 192.168.200.134.ircu-2 > 192.168.200.132.46444: Flags [.], ack 21, win 227, options [nop,nop,TS val 41530416 ecr 41524957], length 0
* 00:00:00.000031 IP 192.168.200.132.46444 > 192.168.200.134.ircu-2: Flags [FP.], seq 21:41, ack 1, win 229, options [nop,nop,TS val 41524957 ecr 41530416], length 20
00:00:00.000543 IP 192.168.200.134.ircu-2 > 192.168.200.132.46444: Flags [F.], seq 1, ack 42, win 227, options [nop,nop,TS val 41530417 ecr 41524957], length 0
00:00:00.000135 IP 192.168.200.132.46444 > 192.168.200.134.ircu-2: Flags [.], ack 2, win 229, options [nop,nop,TS val 41524958 ecr 41530417], length 0
可以看到,打开tcp_nodelay之后,延迟时间差不多为不打开的一半,说明tcp_nodelay生效了
参考链接:
https://www.cnblogs.com/skynet/archive/2010/12/12/1903949.html
https://www.cnblogs.com/chenpingzhao/p/9108570.html
https://blog.xuite.net/jyoutw/xtech/23669726-tcpdump+%E7%9A%84%E7%94%A8%E6%B3%95
相关文章
- 大型项目研发经验分享:以3A游戏为例
- 腾讯数据中心冷源系统AI调优的应用与实践
- 直播报名|腾讯游戏专家分享云原生在高并发游戏推荐系统中的实践
- 数字周报88期|“数字敦煌·开放素材库”正式上线;腾讯宣布将用AI破译甲骨文
- 腾讯自研XR虚实融合技术,助力文旅行业新体验新发展
- 自然语言处理的基本问题——分词问题
- 爆火的OpenAI ChatGPT注册试用全攻略
- chatbot聊天机器人技术路线
- CVPR 2022|达摩院开源低成本大规模分类框架FFC
- 小米「铁大」机器人学会打架子鼓,铁大2.0已投入研发
- 从单点智能到全流程AI,腾讯云智能AI能力「成团出道」
- 模型越大,表现越差?谷歌收集了让大模型折戟的任务,还打造了一个新基准
- ChatGPT讲故事,DALLE-2负责画出来,两AI合作出绘本
- 上线3天,下载4万,ChatGPT中文版VSCode插件来了
- 告诉Stable Diffusion 2.0你不想要什么,生成效果更好:Negative Prompt显奇效
- 上海数字大脑研究院发布国内首个多模态决策大模型DB1,可实现超复杂问题快速决策
- ICLR盲审阶段就被评审赞不绝口的论文:会是Transformer架构的一大创新吗?
- 【拆机测评】英特尔基于FPGA的 IPU表现如何?
- 突破CPU/GPU/FPGA等高端芯片可奖励1000万!反击美方垄断的突围战在深圳打响!
- 如何实现网络切片的端到端隔离?