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收集这些少量的小分组,并在确认到来时以一个分组的方式发出去。
代码语言:javascript复制//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的例子,一个小文件分两次发送
代码语言:javascript复制//客户端
#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 %sn",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);
}
代码语言:javascript复制//服务端
#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] = '