最近遇到一个问题,从业务上出现ftp异常:

ftp .**.**.**
ftp: connect: Cannot assign requested address

这台服务器上的socket统计如下:

ss -s
Total: (kernel )
TCP: (estab , closed , orphaned , synrecv , timewait /), ports 12036-----这个是cat /proc/slabinfo |grep -i tcp_bind_bucket 的inuse那列 Transport Total IP IPv6
* - -
RAW
UDP
TCP
INET
FRAG

可以看到,closed的值很高,ports也很高,占用了12036个端口,

ss -s执行的源代码是:

int print_summary(void)
{
struct sockstat s;
struct snmpstat sn; if (get_sockstat(&s) < )---------------读取/proc/net/sockstat
perror("ss: get_sockstat");
if (get_snmp_int("Tcp:", "CurrEstab", &sn.tcp_estab) < )
perror("ss: get_snmpstat"); printf("Total: %d (kernel %d)\n", s.socks, slabstat.socks); printf("TCP: %d (estab %d, closed %d, orphaned %d, synrecv %d, timewait %d/%d), ports %d\n",
s.tcp_total + slabstat.tcp_syns + s.tcp_tws,
sn.tcp_estab,
s.tcp_total - (s.tcp4_hashed+s.tcp6_hashed-s.tcp_tws),
s.tcp_orphans,
slabstat.tcp_syns,
s.tcp_tws, slabstat.tcp_tws,
slabstat.tcp_ports-----------这个就是slabinfo中的tcp_bind_bucket 的activeobj
); printf("\n");
printf("Transport Total IP IPv6\n");
printf("* %-9d %-9s %-9s\n", slabstat.socks, "-", "-");
printf("RAW %-9d %-9d %-9d\n", s.raw4+s.raw6, s.raw4, s.raw6);
printf("UDP %-9d %-9d %-9d\n", s.udp4+s.udp6, s.udp4, s.udp6);
printf("TCP %-9d %-9d %-9d\n", s.tcp4_hashed+s.tcp6_hashed, s.tcp4_hashed, s.tcp6_hashed);
printf("INET %-9d %-9d %-9d\n",
s.raw4+s.udp4+s.tcp4_hashed+
s.raw6+s.udp6+s.tcp6_hashed,
s.raw4+s.udp4+s.tcp4_hashed,
s.raw6+s.udp6+s.tcp6_hashed);
printf("FRAG %-9d %-9d %-9d\n", s.frag4+s.frag6, s.frag4, s.frag6); printf("\n"); return ;
}

可以看到,ss -s 关于ipv4的输出是通过读取/proc/net/sockstat 的内容,

cat /proc/net/sockstat
sockets: used
TCP: inuse orphan tw alloc mem
UDP: inuse mem
UDPLITE: inuse
RAW: inuse
FRAG: inuse memory

和前面数据有些相差是因为不是严格一个时间点取的,同时,alloc是比较接近closed的,(注意ss中关于closed的算法,要获取精确值,应该关注 /proc/net/sockstat

中的alloc的值)

而正常的设备大概是:

cat /proc/net/sockstat
sockets: used
TCP: inuse orphan tw alloc mem
UDP: inuse mem
UDPLITE: inuse
RAW: inuse
FRAG: inuse memory

可以明显看到alloc的数量相比正常设备很高,而且不是属于inuse状态。

ss -t -a |wc -l 
63

所以这个被ss列入到close状态的大多数就是alloc状态,closed被展示为: s.tcp_total - (s.tcp4_hashed+s.tcp6_hashed-s.tcp_tws),

而s.tcp_total格式化为:

    else if (strcmp(id, "TCP:") == )
sscanf(rem, "%*s%d%*s%d%*s%d%*s%d%*s%d",
&s->tcp4_hashed,---------这个就是inuse那列
&s->tcp_orphans, &s->tcp_tws, &s->tcp_total, &s->tcp_mem);-------tcp_mem就是tcp申请的内存,内核中的tcp_memory_allocated变量

其实就是:

TCP: inuse 81 orphan 0 tw 6 alloc 41019 mem 286

中的alloc那列,tcp_total里面就是alloc的tcp socket总数,但是inuse的又远远低于alloc状态,这个是为啥?

而对应的closed这项为:

    printf("TCP:   %d (estab %d, closed %d, orphaned %d, synrecv %d, timewait %d/%d), ports %d\n",
s.tcp_total + slabstat.tcp_syns + s.tcp_tws,
sn.tcp_estab,
s.tcp_total - (s.tcp4_hashed+s.tcp6_hashed-s.tcp_tws),---------这项closed就是alloc的tcp socket加上timewait 然后减去inused
s.tcp_orphans,
slabstat.tcp_syns,
s.tcp_tws, slabstat.tcp_tws,
slabstat.tcp_ports
);

按照内核代码,alloc的数量也就是:

sockets = percpu_counter_sum_positive(&tcp_sockets_allocated);

这个值在 tcp_v4_init_sock和tcp sock的 sk_clone函数 中增加,并在销毁tcp socket的 tcp_v4_destroy_sock函数中减少,两者处于配对的关系。

写了一个测试程序,才确认,ss -s显示closed的状态的socket,其实就是socket系统调用之后,还没有使用的socket,没有建联,也没侦听,也没关闭。想起tcp的状态变迁图,确实一开始的状态是closed,走了弯路,因为一开始排查,以为是跟close-wait相关,结果使用 ss -o state close-wait 看了发现数量也不对,才知道查错了方向。

回到一开始的业务本身,Cannot assign requested address 确定是绑定端口失败,端口不够用了。

而该设备上配置的ip端口范围是:

sysctl -a |grep port_range
net.ipv4.ip_local_port_range =

也就是12000个,当ports占用之后,端口就不够了,而这些端口占用,并没有实际链接,也就是closed状态的4万多个socket中,有接近12000个端口被占用且不提供服务(业务代码bug)。

其实它想占那么多端口的,4万多个tcp的socket,有30000多bind 端口失败。所以才出现了closed 4万多,而ports为12036的状态,当然,多出来的36个,我还没来得及分析,业务进程就重启了,所以看不到具体占用的了。

最近再次遇到了这个问题:

cat /proc/net/sockstat
sockets: used
TCP: inuse orphan tw alloc mem
UDP: inuse mem
UDPLITE: inuse
RAW: inuse
FRAG: inuse memory ss -s
Total: (kernel )
TCP: (estab , closed , orphaned , synrecv , timewait /), ports Transport Total IP IPv6
* - -
RAW
UDP
TCP
INET
FRAG

为了避免后来人犯同样的错,简单记录之。

一个socket数量的问题的更多相关文章

  1. [Swift通天遁地]四、网络和线程-(13)创建一个Socket客户端

    ★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★➤微信公众号:山青咏芝(shanqingyongzhi)➤博客园地址:山青咏芝(https://www.cnblogs. ...

  2. ZeroMQ接口函数之 :zmq_bind - 绑定一个socket

    ZeroMQ 官方地址 : http://api.zeromq.org/4-0:zmq-bind zmq_bind(3) ZMQ Manual - ZMQ/3.2.5 Name zmq_bind -  ...

  3. ZeroMQ接口函数之 :zmq_connect - 由一个socket创建一个对外连接

    ZeroMQ 官方地址 :http://api.zeromq.org/4-0:zmq_connect zmq_connect(3)  ØMQ Manual - ØMQ/3.2.5 Name zmq_c ...

  4. ZeroMQ接口函数之 :zmq_disconnect - 断开一个socket的连接

    ZeroMQ 官方地址 :http://api.zeromq.org/4-0:zmq_disconnect zmq_disconnect(3) ØMQ Manual - ØMQ/3.2.5 Name ...

  5. ZeroMQ接口函数之 :zmq_msg_recv - 从一个socket中接受一个消息帧

    ZeroMQ 官方地址 :http://api.zeromq.org/4-2:zmq_msg_recv zmq_msg_recv(3) ØMQ Manual - ØMQ/3.2.5 Name zmq_ ...

  6. ZeroMQ接口函数之 :zmq_msg_send – 从一个socket发送一个消息帧

    ZeroMQ 官方地址 :http://api.zeromq.org/4-0:zmq_msg_send zmq_msg_send(3) ØMQ Manual - ØMQ/3.2.5 Name zmq_ ...

  7. ZeroMQ接口函数之 :zmq_recv – 从一个socket上接收一个消息帧

    ZeroMQ 官方地址 :http://api.zeromq.org/4-1:zmq_recv zmq_recv(3)        ØMQ Manual - ØMQ/4.1.0 Name zmq_r ...

  8. ZeroMQ接口函数之 :zmq_recvmsg – 从一个socket上接收一个消息帧

    ZeroMQ 官方地址 :http://api.zeromq.org/4-1:zmq-recvmsg zmq_recvmsg(3)         ØMQ Manual - ØMQ/4.1.0 Nam ...

  9. ZeroMQ接口函数之 :zmq_sendmsg – 从一个socket上发送一个消息帧

    ZeroMQ 官方地址 :http://api.zeromq.org/4-1:zmq-sendmsg zmq_sendmsg(3)        ØMQ Manual - ØMQ/4.1.0 Name ...

随机推荐

  1. 电路 - 基尔霍夫定律(KLL);节点流入电流等于流出电流。

    下面是我在学习STM32 中ADC测量电压,时候接触掉ADC的测量范围在0~3.3V 之间,不满足于实际使用,用于电路知识设计电压放大电路.(图片来自野火) 上面个的电路,可以等效出一个电路公式:(V ...

  2. Mxd文档更新比例尺

    在AE中,更新Mxd文档的比例尺,比较特殊.写代码以记录,更新比例尺代码如图所示: [DllImport("User32.dll")] public static extern i ...

  3. python3-基础5

    #函数 1 什么是函数? 2 为什么要用函数? 3 函数的分类:内置函数与自定义函数 4 如何自定义函数 5 语法 6 定义有参数函数,及有参函数的应用场景 7 定义无参数函数,及无参函数的应用场景 ...

  4. FASTMM内存泄漏处理

    https://blog.csdn.net/akof1314/article/details/6524767

  5. Struts2多文件上传原理和示例

    一.创建上传文件的页面,代码如下所示     1.Struts2也可以很方便地实现多文件上传. 在输入表单域增加多个文件域:multifileupload.jsp    <%@ page lan ...

  6. Python之while循环

    1.While循环基础 2.While循环进阶 3.其他

  7. 同一主机设置多个密钥与不同github账号关联,或同一主机同一密钥分别关联github和gitlab

    前言 github一把公钥只能用于一个github账户,如果想在同一主机上给两个属于不同账户的仓库提交时,必须在本地创建两对公/私钥匙,分别把两把公钥给两个帐号. 或者有时候,你公司内部使用的gitl ...

  8. EChart 猜猜乐

    http://m.bkbtcaicaile.hyl.life/index.html#/

  9. Docker镜像仓库Harbor搭建及配置

    一.harbor简介 Harbor是一个用于存储和分发Docker镜像的企业级Registry服务器,通过添加一些企业必需的功能特性,例如安全.标识和管理等,扩展了开源Docker Distribut ...

  10. Python爬虫简单介绍

    相关环境: Python3 requests库 BeautifulSoup库 一.requests库简单使用 简单获取一个网页的源代码: import requests sessions = requ ...