关于listen函数的backlog

本文探讨了UNIX网络编程中listen函数的backlog参数,解释了在Linux 2.2之后,backlog指定的是完全建立的、等待被accept的TCP连接队列长度。当已完成队列满时,如果tcp_abort_on_overflow为1,服务器将发送RST结束连接;否则忽略ACK包。backlog设置为参考值,实际可能受到somaxconn限制。举例展示了当backlog设置为3时,超过此值的连接会被拒绝。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

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返回负值。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值