实验环境:Ubuntu16.04,内核版本:4.4.0-59-generic
 
根据man listen得到的解释如下:
 
backlog参数定义了存放pending状态(挂起、护着搁置)的连接的队列的最大长度;如果在队列满的时候,一个连接请求到达,客户端可能会收到一个错误:ECONREFUSED。
 
然后man listen的下面有一个小提示:
 
现在backlog这个参数指示的是存放已经建立连接(established)并等待被accept的sockets的队列的长度。
没有完成的socket队列的长度可以通过 /proc/sys/net/ipv4/tcp_max_syn_backlog 这个参数来设置。
如果backlog参数大于 /proc/sys/net/core/somaxconn 的值,那么该值将被自动截断为somaxconn的值,它的值默认是128。
 
下面是我做的一个关于backlog的小实验:
tcpSvr端调用完listen后,sleep,
客户端根据参数,开启n个线程,每个线程都尝试与tcpSvr建立连接。
 /*
* gcc -std=c99 -o tcpCli ./tcpCli.c -lpthread
*/
#include <unistd.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <errno.h>
#include <pthread.h> const int PORT = ; void* func(void *arg)
{
int fd = socket(AF_INET, SOCK_STREAM, );
if (fd == -)
{
perror("socket");
return (void*);
} struct sockaddr_in addr;
memset(&addr, , sizeof(addr));
addr.sin_family = AF_INET;
addr.sin_addr.s_addr = inet_addr("127.0.0.1");
addr.sin_port = htons(PORT); // get send buffer size
int iWBufSize;
socklen_t optLen = sizeof(iWBufSize);
getsockopt(fd, SOL_SOCKET, SO_SNDBUF, &iWBufSize, &optLen);
printf("Write buffer size = %d\n", iWBufSize); int iRBufSize;
optLen = sizeof(iRBufSize);
getsockopt(fd, SOL_SOCKET, SO_RCVBUF, &iRBufSize, &optLen);
printf("Read buffer size = %d\n", iRBufSize); if (connect(fd, (struct sockaddr*)&addr, sizeof(addr)) != )
{
perror("connect");
return (void*);
} char buf[] = "hello world!";
int iCount = ;
while ()
{
sprintf(buf, "%d", iCount);
int iRet = send(fd, buf, strlen(buf), );
if (iRet == -)
{
perror("sendto");
break;
}
else
{
// printf("Send data len [%d]\n", iRet);
iCount++;
}
if (iCount % == )
{
// printf("Send package count %d\n", iCount);
sleep();
}
}
printf("Send package count %d\n", iCount);
close(fd); return (void*);
}
int main(int argc, char **argv)
{
if (argc != )
{
printf("Usage:%s thread_num\n", argv[]);
return ;
} int iThreadNum = atoi(argv[]);
printf("ThreadNum [%d]\n", iThreadNum);
pthread_t *pTid = (pthread_t*)malloc(sizeof(pthread_t) * iThreadNum);
for (int i = ; i < iThreadNum; ++i)
{
pthread_create(&pTid[i], NULL, func, NULL);
} for (int i = ; i < iThreadNum; ++i)
{
pthread_join(pTid[i], NULL);
} return ;
}

tcpCli

 /*
* gcc -o tcpSvr ./tcpSvr.c
*/
#include <unistd.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <stdio.h>
#include <string.h>
#include <errno.h>
#include <pthread.h> const int PORT = ; int main(int argc, char **argv)
{
int fd = socket(AF_INET, SOCK_STREAM, );
if (fd == -)
{
perror("socket");
return errno;
} printf("Port %d\n", PORT);
struct sockaddr_in addr;
memset(&addr, , sizeof(addr));
addr.sin_family = AF_INET;
addr.sin_addr.s_addr = INADDR_ANY;
addr.sin_port = htons(PORT); if (- == bind(fd, (struct sockaddr*)&addr, sizeof(addr)))
{
perror("bind");
return errno;
} if (- == listen(fd, ))
{
perror("listen");
return errno;
} struct sockaddr_in cli_addr;
socklen_t cli_addr_len = sizeof(cli_addr);
sleep(); int client = accept(fd, (struct sockaddr*)&cli_addr, &cli_addr_len);
if (client < )
{
perror("accept");
return errno;
} char buf[];
int iCount = ;
int iZero = ;
while()
{
memset(buf, , );
int iRet = recv(client, buf, , );
printf("Recv package count[%d]\t", iCount);
printf("recv content [%s]\n", buf);
iRet = send(client, buf, iRet, ); iRet = iRet/iZero;
}
close(fd); return ;
}

tcpSvr

实验结果:

执行:tcpCli 15 的结果

这是使用grep过滤了tcpSvr的显示结果:

这是使用grep过滤了tcpCli的显示结果:

这是过了一段时间后,tcpCli输出的结果:

TCP状态转移图:

然后根据这些结果以及TCP状态转移图,可以得出如下结论:

在server端处在ESTABLISHED状态的有6个连接,有4个处在SYN_RECV状态(这四个连接过一段时间会关闭)
但是在client端,15个连接全部处在ESTABLISHED状态,并且此次修改了tcpCli的代码,在connect之后打印“Connect Succ”显示15个连接都成功了。
安装TCP状态转移图,svr端收到TCK就应该转移到ESTABLISHED状态了,根据tcpdump抓包也看到回的ACK包,但是为什么netstat看不到这些连接、或者看到的SYN_RECV状态呢?
到底在tcp未处理连接请求达到backlog值之后,对于新到来的连接请求,tcp协议栈做什么处理呢?
 
根据tcpdump抓包数据分析:
无状态的端口对应的包:
svr端在不停的发送第二次握手的包(syn, ack=syn+1),这就意味着svr端未收到cli发的第三次握手的包(ack包),但是tcpdump抓包发现,每次svr发送过(syn,ack=syn+1)后,cli都回包了。这么分析只有一个原因,虽然tcp收到了ack包,但是没有接受、或者说抛弃了。于是tcp协议层就认为没有收到ack,于是发生重传。
 
SYN_RECV状态对应的包:
情况同上面类似,但是为什么它们的状态时SYN_RECV呢?
 
ESTABLISHED状态对应的包:
这就是正常的三次握手的包了。
 
2017-03-23 更新:
经过知乎上大牛的指点,关于服务端是SYN_RECV状态,而客户端是ESTABLISHED状态的疑问,有了答案,参考:tcp/dccp: drop SYN packets if accept queue is full

这段解释的意思就是,当服务端未处理连接队列满的时候,它就会丢掉client端发送的三次握手中的最后一个ACK包,这就会导致,client端以为自己已经建立连接了,但是实际在server端没有连接,同时也解释了tcpdump抓包看到的,为什么server一直不停的发送三次握手的第二次数据包(SYN+ACK)。

另外,关于这种类似的问题,就像知乎浅墨说的,策略在不停的改变,不要听信网上别人的说法,自己动手试一下就可以了。

listen的参数backlog的意义的更多相关文章

  1. socket的同步异步的性能差别,以及listen的参数backlog

    先说listen的参数backlog,同步系统中分别设置为5,512,1024的跑分情况 跑分工具apache的ab,参数为:ab -n50000 -c300 backlog=5跑分结果 Reques ...

  2. [TCP/IP] TCP在listen时的参数backlog的意义

    linux内核中会维护两个队列:  1)未完成队列:接收到一个SYN建立连接请求,处于SYN_RCVD状态  2)已完成队列:已完成TCP三次握手过程,处于ESTABLISHED状态  3)当有一个S ...

  3. listen函数里面backlog的意义以及各种情况

    先看了这篇: http://www.cppblog.com/thisisbin/archive/2010/02/07/107444.html 里面说了会维护两个队列,established 和 syn ...

  4. Linux中,Tomcat 怎么承载高并发(深入Tcp参数 backlog)

    一.前言 这两天看tomcat,查阅 tomcat 怎么承载高并发时,看到了backlog参数.我们知道,服务器端一般使用mq来减轻高并发下的洪峰冲击,将暂时不能处理的请求放入队列,后续再慢慢处理.其 ...

  5. dojo表格分页之各个参数代表的意义(一)

    下面是dojo表格分页参数代表的意义 //每页可以显示10/15/20/25/30条记录 (1)pageSizes: [10, 15, 20, 25,30], //每页显示的记录从多少到多少,共多少条 ...

  6. socket listen参数中的backlog 的意义!

    服务器监听时,在每次处理一个客户端的连接时是需要一定时间的,这个时间非常的短(也许只有1ms 或者还不到),但这个时间还是存在的.而这个backlog 存在的意义就是:在这段时间里面除了第一个连接请求 ...

  7. tcp/ip协议listen函数中backlog参数的含义与php-fpm的502 Bad Gateway

    To understand the backlog argument, we must realize that for a given listening socket, the kernel ma ...

  8. linux tcp listen函数的参数backlog

    1 listen函数(http://man7.org/linux/man-pages/man2/listen.2.html) int listen(int sockfd, int backlog); ...

  9. listen()函数中backlog参数分析

    实例分析1 将服务器端的listen函数backlog设置为2,用20个客户端与服务器建立连接,查看连接的建立情况. 服务器代码: #include <stdio.h> #include& ...

随机推荐

  1. apache kafka系列之Producer处理逻辑

     最近研究producer的负载均衡策略,,,,我在librdkafka里边用代码实现了partition 值的轮询方法,,,但是在现场验证时,他的负载均衡不起作用,,,所以来找找原因: 下文是一篇描 ...

  2. 树莓派进阶之路 (027) - 在Linux中增加swap空间

    原贴地址:http://blog.csdn.net/chinalinuxzend/article/details/1759593  在Linux中增加swap空间 在安装Linux的时候,不知道swa ...

  3. ubuntu下制作u盘启动盘

    ubuntu12.04下成功制作了ubuntu13.10 U盘启动盘. 成功 ubuntu14.04下成功制作了centos.7 U盘启动盘.成功 1.安装u盘制作工具unetbootin sudo ...

  4. block(三)揭开神秘面纱(上)-b

    block到底是什么 我们使用clang的rewrite-objc命令来获取转码后的代码. 1.block的底层实现 我们来看看最简单的一个block: [caption id="attac ...

  5. Docker容器相互访问

    原文地址:https://blog.csdn.net/subfate/article/details/81396532?utm_source=copy 很多时候,同一台机器上,需要运行多个docker ...

  6. 【Linux】字符转换命令join

    join 看字面上的意义 (加入/参加) 就可以知道,他是在处理两个文件之间的数据,而且,主要是在处理『两个文件当中,有 "相同数据" 的那一行,才将他加在一起』的意思.我们利用底 ...

  7. 解决WIN32窗口不响应WM_LBUTTONDBLCLK消息

    原文链接: http://www.cnblogs.com/xukaixiang/archive/2012/05/27/2520059.html 今天在做一个软件时,发现win32创建的窗体不能响应WM ...

  8. VC CListCtrl 第一列列宽自适应

    原文链接: http://www.cnblogs.com/sephil/archive/2011/04/03/2004384.html 今天用VC写工具的时候用到CListView,并且ListCtr ...

  9. Facade 设计模式

    目的 在一个子系统的一组接口上提供一个统一的接口.Facade 设计模式定义了一个更高级别的接口,使子系统更容易使用. 通过一个更加简洁的接口来包装一个复杂的子系统. 解决的问题 客户端需要一个简化的 ...

  10. 【转载并整理】mysql 1293错误 建表两个timestamp

    http://www.jb51.net/article/50878.htm 这里要使用到mysql的触发器