unix网络编程中这样解释
曾经指未完成队列和已完成队列之和
man listen
man 手册上这样解释:
The behavior of the backlog argument on TCP sockets changed with Linux 2.2. Now it specifies the queue length for completely established sockets waiting to be accepted, instead of the number of incomplet connection requests.
TCP套接字上backlog参数的行为在Linux 2.2中发生了更改。现在,它指定等待接受的完全建立的套接字的队列长度,而不是不完整连接请求的数量。
就是说,backlog是已经完成三次握手,即ESTABLISHED状态(且未accept)的连接队列长度
客户端SYN到来时,加入TCP未完成连接队列,
服务器响应自己的SYN并携带对客户端的ack,
当客户端回复ACK时,从未完成连接队列取这个结点,加入TCP已完成队列的队尾,即完成三次握手。
accpet函数实际不参与三路握手,换句话说,三路握手完成之后,进程再accept,当调用accept函数时,从TCP已完成队列取队头结点,返回给进程,若已完成队列为空,则accpet阻塞。
下面通过改变listen参数的不同值测试
server.c
#include<stdio.h>
#include<stdlib.h>
#include<sys/socket.h>
#include<arpa/inet.h>
#include<unistd.h>
#include<string.h>
#define SIZE 300
int main(int argc,char *argv[])
{
int server_sock,client_sock,r;
struct sockaddr_in server_addr,client_addr;
socklen_t len;
char buf[SIZE];
if(argc!=3)
{
printf("need port + backlog\n");
return 1;
}
server_sock = socket(AF_INET,SOCK_STREAM,0);
int on=1;
if(setsockopt(server_sock, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on))){
printf("setsockopt error\n");
return 1;
}
memset(&server_addr,0,sizeof server_addr);
server_addr.sin_family=AF_INET;
server_addr.sin_addr.s_addr=htonl(INADDR_ANY);
server_addr.sin_port=htons(atoi(argv[1]));
if(-1 == bind(server_sock, (struct sockaddr*)&server_addr,sizeof(server_addr)))
{
printf(" bind error\n");
return -1;
}
if(-1 == listen(server_sock, atoi(argv[2])))
{
printf(" listen error\n");
return -1;
}
//阻塞
while(1);
close(server_sock);
return 0;
}
多线程client.c
#include<pthread.h>
#include<stdio.h>
#include<stdlib.h>
#include<sys/socket.h>
#include<arpa/inet.h>
#include<unistd.h>
#include<string.h>
pthread_mutex_t mutex;
int count=0;
void* run(void*arg){
int *p=(int*)arg;
int port=*p;
int sock;
struct sockaddr_in addr;
bzero(&addr,sizeof(addr));
socklen_t len;
sock=socket(AF_INET,SOCK_STREAM,0);
addr.sin_family=AF_INET;
addr.sin_addr.s_addr=inet_addr("127.0.0.1");
addr.sin_port=htons(port);
len=sizeof addr;
int r=connect(sock,(struct sockaddr*)&addr,len);
if(r==-1)
{
printf("connect error\n");
pthread_exit(NULL);
//return 0;
}
pthread_mutex_lock(&mutex);
count++;
pthread_mutex_unlock(&mutex);
printf("pthread_self %lu connect ok count=%d\n",pthread_self(),count);
while(1);
//pthread_exit(NULL);
return NULL;
}
int main(int argc,char*argv[])
{
if(argc!=3)
{
printf("port + num\n");
return 1;
}
pthread_mutex_init(&mutex,NULL);
int num = atoi(argv[2]);
int port = atoi(argv[1]);
pthread_t *tid=(pthread_t*)malloc((sizeof(pthread_t)*num));
if (!tid)
{
return 1;
}
int i;
for ( i = 0; i < num; ++i)
{
int r=pthread_create(&tid[i],NULL,run,(void*)&port);
if (r)
{
printf("pthread_create error\n");
return 1;
}
}
for (int j = 0; j < num; ++j)
pthread_join(tid[j],NULL);
return 0;
}
百度一下:
已完成队列满后
通常未完成队列的长度大于已完成队列.
已完成队列满后, 当服务器收到来自客户端的ACK包时
如果 /proc/sys/net/ipv4/tcp_abort_on_overflow 设为 1, 直接回RST包,结束连接.
否则忽视ACK包.
内核有定时器管理未完成队列,对于由于网络原因没收到ACK包或是收到ACK包后被忽视的SYN_RCVD连接重发SYN+ACK包, 最多重发次数由/proc/sys/net/ipv4/tcp_synack_retries 设定.
backlog 即上述已完成队列的大小, 这个设置是个参考值,不是精确值. 内核会做些调整, 大于/proc/sys/net/core/somaxconn, 则取somaxconn的值
未完成队列满后
如果启用syncookies (net.ipv4.tcp_syncookies = 1),新的连接不进入未完成队列,不受影响.
否则,服务器不在接受新的连接.
作者:黄洪清
链接:https://www.jianshu.com/p/fe2228a77429
来源:简书
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
server设置backlog为3
client设置5个线程连接
前四个ok,三次握手正常数大于backlog设置的3,原因未知。
最后一个连接失败,因为队列已经满了。
gdut17@ubuntu:~/code/1227$ ./server 9999 3
gdut17@ubuntu:~/code/1227$ ./client 9999 5
pthread_self 140434824558336 connect ok count=1
pthread_self 140434832951040 connect ok count=2
pthread_self 140434841343744 connect ok count=3
pthread_self 140434849736448 connect ok count=4
connect error
gdut17@ubuntu:~$ cat /proc/sys/net/ipv4/tcp_abort_on_overflow
0
是0说明队列满后,服务端对新来的SYN不予理会
tcpdump抓包片段
sudo tcpdump port 9999 -i any -v -n
127.0.0.1.48082 > 127.0.0.1.9999: Flags [S], cksum 0xfe30 (incorrect -> 0x2f45), seq 2993281626, win 65495, options [mss 65495,sackOK,TS val 3513442219 ecr 0,nop,wscale 7], length 0
20:53:53.130377 IP (tos 0x0, ttl 64, id 16645, offset 0, flags [DF], proto TCP (6), length 60)
127.0.0.1.48082 > 127.0.0.1.9999: Flags [S], cksum 0xfe30 (incorrect -> 0x2b3d), seq 2993281626, win 65495, options [mss 65495,sackOK,TS val 3513443251 ecr 0,nop,wscale 7], length 0
20:53:55.146488 IP (tos 0x0, ttl 64, id 16646, offset 0, flags [DF], proto TCP (6), length 60)
127.0.0.1.48082 > 127.0.0.1.9999: Flags [S], cksum 0xfe30 (incorrect -> 0x235d), seq 2993281626, win 65495, options [mss 65495,sackOK,TS val 3513445267 ecr 0,nop,wscale 7], length 0
20:53:59.402345 IP (tos 0x0, ttl 64, id 16647, offset 0, flags [DF], proto TCP (6), length 60)
127.0.0.1.48082 > 127.0.0.1.9999: Flags [S], cksum 0xfe30 (incorrect -> 0x12bd), seq 2993281626, win 65495, options [mss 65495,sackOK,TS val 3513449523 ecr 0,nop,wscale 7], length 0
20:54:07.593987 IP (tos 0x0, ttl 64, id 16648, offset 0, flags [DF], proto TCP (6), length 60)
127.0.0.1.48082 > 127.0.0.1.9999: Flags [S], cksum 0xfe30 (incorrect -> 0xf2bc), seq 2993281626, win 65495, options [mss 65495,sackOK,TS val 3513457715 ecr 0,nop,wscale 7], length 0
可以看到,client最后一个线程重发5次syn,server端不予理会,connect返回负值。