socket.error: [Errno 99] Cannot assign requested address

网上你去搜,基本都是说bind的时候,地址已经被用了,都是胡扯。地址被用报的错误应该是:

Address already in use才对

然后我看得都是英文的,说明外国人也不是想象中的那么一丝不苟,

言归正传。socket发起connect请求的时候会随机分配一个端口给你。这个分配的端口是有范围的,记录在:

/proc/sys/net/ipv4/ip_local_port_range

这个文件里面(fedora 17).当你用多个进程发起过多的请求的时候,端口用完了就会报这个错误。比如我就开了4个进程,一下发起了40000个请求。

你可以做个实验试试,切换到root用户,敲一下这条命令:

echo 32768 32769 > /proc/sys/net/ipv4/ip_local_port_range

这下你打开人人,微博就会发现很多图片加载不出来了。因为图片加载在浏览器里面就是并行加载的,由于你没有足够的端口数,所以图片加载都失败了。别当心,这个修改是临时的(是不是临时的我也不知道,听别人说的)

修改过来用下面这条命令:

echo 32768 61000 > /proc/sys/net/ipv4/ip_local_port_range

32768 到 61000 是系统默认的随机分配端口范围(再次声明,fedora 17版本)

(3) errno = 99的原因;

至于connect系统调用为什么返回失败,就只能看系统调用的实现了。

a) connect系统调用

connect系统调用在net/socket.c中实现,Sys_connect系统调用的调用栈如下:

Sys_connect--->
sock->ops->connect // inet_stream_connect
sk->sk_prot->connect // tcp_v4_connect

tcp_v4_connect的作用主要是完成TCP连接三次握手中的第一个握手,即向服务端发送SYNC = 1和一个32位的序号的连接请求包。要发送SYNC请求包,按照TCP/IP协议,就必须有源IP地址和端口,源IP地址的选择和路由相关,需要查询路由表,在ip_route_connect中实现,源端口的选择在__inet_hash_connect中实现,而且如果找不到一个可用的端口,这个函数会返回-EADDRNOTAVAIL,因此基本上可以确定是这个函数返回错误导致connect失败;

b) __inet_hash_connect

这个函数的主要作用是选择一个可用的端口,其主要的实现步骤如下:

i. 调用inet_get_local_port_range(&low, &high);获取可用的端口链表;

  1. 调用read_seqbegin(&sysctl_local_ports.lock);得到顺序锁;
  2. 得到可用端口的low和high:

*low = sysctl_local_ports.range[0];

*high = sysctl_local_ports.range[1];

ii. 对于每一个端口,进行下面的步骤:

  1. 在inet_hashinfo *hinfo中查找这个端口inet_hashinfo用于保存已经使用的端口信息,每个使用的端口在这个hash表中有一个entry;
  2. 对端口做hash得到链表头(使用链表解决hash冲突)
  3. 遍历链表中的每一个entry:

a) 判断是否与这个要使用的端口相同,如果相同转到步骤b,如果不相同则遍历下一个entry

b) 找到这个端口,调用check_established(__inet_check_established)判断这个端口是否可以重用(TIME_WAIT状态下的端口并且net.ipv4.tcp_tw_recycle = 1是端口可以重用)

  1. 如果在链表中没有找到这个端口,表示端口没有被使用,调用inet_bind_bucket_create在hash表中插入一个entry;

iii. 如果到最后都没有找到一个可用的端口就返回EADDRNOTAVAIL;

从这个函数的实现可以看出,主要是由于可用的端口被占满了,所以找不到一个可用的端口,导致连接失败。运行netstat可以发现确实存在很多TIME_WAIT状态的socket,这些socket将可用端口占满了。

[root@test miuistorage-dev]# netstat -n | awk '/^tcp/ {++state[$NF]} END {for(key in state)
print key,"\t",state[key]}'
TIME_WAIT 26837
ESTABLISHED 30

(4) 解决办法:

要解决端口被TIME_WAIT状态的socket占满的问题,可以有以下的解决办法:

a) 修改可用端口范围

查看当前的端口范围:

root@guojun8-desktop:/linux-2.6.34# sysctl net.ipv4.ip_local_port_range
net.ipv4.ip_local_port_range = 32768 61000

修改端口范围:

root@guojun8-desktop:linux-2.6.34# sysctl net.ipv4.ip_local_port_range="32768    62000"
net.ipv4.ip_local_port_range = 32768 62000

这种办法可能不能解决根本问题,因为如果使用短连接,即使增加可用端口还是会被占满的。

b) 设置net.ipv4.tcp_tw_recycle = 1(本人采用这种解决方式)

这个参数表示系统的TIME-WAIT sockets是否可以快速回收

root@guojun8-desktop:linux-2.6.34# sysctl net.ipv4.tcp_tw_recycle=1
net.ipv4.tcp_tw_recycle = 1

c) 设置net.ipv4.tcp_tw_reuse=1

这个参数表示是否可以重用TIME_WAIT状态的端口;

root@guojun8-desktop:linux-2.6.34# [root@test thumbnail]# sysctl net.ipv4.tcp_tw_reuse=1
net.ipv4.tcp_tw_reuse = 1

(5) 更深入的探讨:sysctl做了什么

可以用strace跟踪一下sysctl的系统调用:

root@guojun8-desktop:linux-2.6.34# strace sysctl net.ipv4.tcp_tw_recycle=1
execve("/sbin/sysctl", ["sysctl", "net.ipv4.tcp_tw_recycle=1"], [/* 20 vars */]) = 0
brk(0) = 0x952f000
…..
open("/proc/sys/net/ipv4/tcp_tw_recycle", O_WRONLY|O_CREAT|O_TRUNC, 0666) = 3
fstat64(3, {st_mode=S_IFREG|0644, st_size=0, ...}) = 0
mmap2(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0xb788e000
write(3, "1\n", 2) = 2
close(3) = 0
munmap(0xb788e000, 4096) = 0
fstat64(1, {st_mode=S_IFCHR|0620, st_rdev=makedev(136, 8), ...}) = 0
mmap2(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0xb788e000
write(1, "net.ipv4.tcp_tw_recycle = 1\n", 28net.ipv4.tcp_tw_recycle = 1
) = 28
exit_group(0) = ?

可以看到这个程序打开/proc/sys/net/ipv4/tcp_tw_recycle并向文件中写入1,但是这个设置时怎样其作用的呢?在内核中对/proc/sys目录下的文件的i_fop做了特殊的处理,在proc_sys_make_inode 中设置:inode->i_fop = &proc_sys_file_operationsproc_sys_file_operations的定义如下:

static const struct file_operations proc_sys_file_operations = {
.read = proc_sys_read,
.write = proc_sys_write,
};

proc_sys_write中会修改对应的文件,并且修改内存中的内容,不同的文件有不同的proc_handler,如tcp_tw_recycle对应的处理函数是proc_dointvec,这个函数会修改下面的变量:

tcp_death_row.sysctl_tw_recycle

这个变量在内核中表示TIME_WIAT状态的socket是否可以被快速回收。

(3) errno = 99的原因;

至于connect系统调用为什么返回失败,就只能看系统调用的实现了。

a) connect系统调用

connect系统调用在net/socket.c中实现,Sys_connect系统调用的调用栈如下:

Sys_connect--->
sock->ops->connect // inet_stream_connect
sk->sk_prot->connect // tcp_v4_connect

tcp_v4_connect的作用主要是完成TCP连接三次握手中的第一个握手,即向服务端发送SYNC = 1和一个32位的序号的连接请求包。要发送SYNC请求包,按照TCP/IP协议,就必须有源IP地址和端口,源IP地址的选择和路由相关,需要查询路由表,在ip_route_connect中实现,源端口的选择在__inet_hash_connect中实现,而且如果找不到一个可用的端口,这个函数会返回-EADDRNOTAVAIL,因此基本上可以确定是这个函数返回错误导致connect失败;

b) __inet_hash_connect

这个函数的主要作用是选择一个可用的端口,其主要的实现步骤如下:

i. 调用inet_get_local_port_range(&low, &high);获取可用的端口链表;

  1. 调用read_seqbegin(&sysctl_local_ports.lock);得到顺序锁;
  2. 得到可用端口的low和high:

*low = sysctl_local_ports.range[0];

*high = sysctl_local_ports.range[1];

ii. 对于每一个端口,进行下面的步骤:

  1. 在inet_hashinfo *hinfo中查找这个端口inet_hashinfo用于保存已经使用的端口信息,每个使用的端口在这个hash表中有一个entry;
  2. 对端口做hash得到链表头(使用链表解决hash冲突)
  3. 遍历链表中的每一个entry:

a) 判断是否与这个要使用的端口相同,如果相同转到步骤b,如果不相同则遍历下一个entry

b) 找到这个端口,调用check_established(__inet_check_established)判断这个端口是否可以重用(TIME_WAIT状态下的端口并且net.ipv4.tcp_tw_recycle = 1是端口可以重用)

  1. 如果在链表中没有找到这个端口,表示端口没有被使用,调用inet_bind_bucket_create在hash表中插入一个entry;

iii. 如果到最后都没有找到一个可用的端口就返回EADDRNOTAVAIL;

从这个函数的实现可以看出,主要是由于可用的端口被占满了,所以找不到一个可用的端口,导致连接失败。运行netstat可以发现确实存在很多TIME_WAIT状态的socket,这些socket将可用端口占满了。

[root@test miuistorage-dev]# netstat -n | awk '/^tcp/ {++state[$NF]} END {for(key in state)
print key,"\t",state[key]}'
TIME_WAIT 26837
ESTABLISHED 30

(4) 解决办法:

要解决端口被TIME_WAIT状态的socket占满的问题,可以有以下的解决办法:

a) 修改可用端口范围

查看当前的端口范围:

root@guojun8-desktop:/linux-2.6.34# sysctl net.ipv4.ip_local_port_range
net.ipv4.ip_local_port_range = 32768 61000

修改端口范围:

root@guojun8-desktop:linux-2.6.34# sysctl net.ipv4.ip_local_port_range="32768    62000"
net.ipv4.ip_local_port_range = 32768 62000

这种办法可能不能解决根本问题,因为如果使用短连接,即使增加可用端口还是会被占满的。

b) 设置net.ipv4.tcp_tw_recycle = 1

这个参数表示系统的TIME-WAIT sockets是否可以快速回收

root@guojun8-desktop:linux-2.6.34# sysctl net.ipv4.tcp_tw_recycle=1
net.ipv4.tcp_tw_recycle = 1

c) 设置net.ipv4.tcp_tw_recycle = 1

这个参数表示是否可以重用TIME_WAIT状态的端口;

root@guojun8-desktop:linux-2.6.34# [root@test thumbnail]# sysctl net.ipv4.tcp_tw_reuse=1
net.ipv4.tcp_tw_reuse = 1

(5) 更深入的探讨:sysctl做了什么

可以用strace跟踪一下sysctl的系统调用:

root@guojun8-desktop:linux-2.6.34# strace sysctl net.ipv4.tcp_tw_recycle=1
execve("/sbin/sysctl", ["sysctl", "net.ipv4.tcp_tw_recycle=1"], [/* 20 vars */]) = 0
brk(0) = 0x952f000
…..
open("/proc/sys/net/ipv4/tcp_tw_recycle", O_WRONLY|O_CREAT|O_TRUNC, 0666) = 3
fstat64(3, {st_mode=S_IFREG|0644, st_size=0, ...}) = 0
mmap2(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0xb788e000
write(3, "1\n", 2) = 2
close(3) = 0
munmap(0xb788e000, 4096) = 0
fstat64(1, {st_mode=S_IFCHR|0620, st_rdev=makedev(136, 8), ...}) = 0
mmap2(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0xb788e000
write(1, "net.ipv4.tcp_tw_recycle = 1\n", 28net.ipv4.tcp_tw_recycle = 1
) = 28
exit_group(0) = ?

可以看到这个程序打开/proc/sys/net/ipv4/tcp_tw_recycle并向文件中写入1,但是这个设置时怎样其作用的呢?在内核中对/proc/sys目录下的文件的i_fop做了特殊的处理,在proc_sys_make_inode 中设置:inode->i_fop = &proc_sys_file_operationsproc_sys_file_operations的定义如下:

static const struct file_operations proc_sys_file_operations = {
.read = proc_sys_read,
.write = proc_sys_write,
};

proc_sys_write中会修改对应的文件,并且修改内存中的内容,不同的文件有不同的proc_handler,如tcp_tw_recycle对应的处理函数是proc_dointvec,这个函数会修改下面的变量:

tcp_death_row.sysctl_tw_recycle

这个变量在内核中表示TIME_WIAT状态的socket是否可以被快速回收。

造成socket.error: [Errno 99] Cannot assign requested的更多相关文章

  1. socket.error: [Errno 99] Cannot assign requested address

    方法一:python 命令行下运行  vi /etc/hosts 将127.0.1.1 那一行的名字改成你的(用 vi /etc/hostname 获取) 127.0.0.1 localhost 12 ...

  2. python socket编程腾讯云下报错[Errno 99] Cannot assign requested address的解决方式

    先写服务端server.py: import socket import time HOST = '172.17.xx.xx' #服务器的私网IP #HOST = 'localhost' PORT = ...

  3. 解决commBind: Cannot bind socket FD 18 to [::1]: (99) Cannot assign requested address squid

    最近玩squid主要是为了爬虫代理,但是使用docker搭建squid的时候发现,docker一直默认使用的 ipv6,但是squid使用ipv4,导致无法绑定,出现commBind: Cannot ...

  4. python web开发遇到socket.error[errno 10013]

    socket.error[errno 10013],端口被占用 重新换一个端口,或者把占用该端口的程序关闭就可以了

  5. python socket.error: [Errno 10054] 解决方法

    我用的是python2.7   我搜网上10054错误解决方法的时候发现,大部分文章都是以python3为基础的,对于python2不适用. python socket.error: [Errno 1 ...

  6. python socket.error: [Errno 24] Too many open files

    以openwrt AR9331开发板为例,socket连接到1019个就报错 “python socket.error: [Errno 24] Too many open files” 1.查看开发板 ...

  7. Flask: socket.error: [Errno 48] Address already in use 问题

    参考: Mac OSX 解决socket.error: [Errno 48] Address already in use问题 Mac OS X中解决socket.error: [Errno 48] ...

  8. supervisor error: <class 'socket.error'>, [Errno 110]

    supervisorctr status报错 error: <class 'socket.error'>, [Errno 110] Connection timed out: file: ...

  9. connect() to 192.168.30.71:8082 failed (99: Cannot assign requested address) while connecting to upstream, client: 114.80.182.136, server: localhost, request: "GET /home/senior HTTP/1.1", upstream: "

    connect() to 192.168.30.71:8082 failed (99: Cannot assign requested address) while connecting to ups ...

随机推荐

  1. ng2中的ng-content用法

    用途:1.ng-content用于在组件中嵌入内容 2.ng-content可以在组件中嵌入模板代码,方便定制可复用的组件 select属性支持css选择器,如"#id",&quo ...

  2. 你所不知道的html5与html中的那些事(三)

    文章简介: 关于html5相信大家早已经耳熟能详,但是他真正的意义在具体的开发中会有什么作用呢?相对于html,他又有怎样的新的定义与新理念在里面呢?为什么一些专家认为html5完全完成后,所有的工作 ...

  3. solr--搭建全文搜索服务器

    1.下载http://www-eu.apache.org/dist/lucene/solr/6.5.0/ 2.新建: 云模式 3.打开浏览器: 简单的中文分词,暂时是一个一个的分词:

  4. 实验楼的php比赛题,网页数据提取。

    实验楼的php比赛题,网页数据提取. 题目的地址:https://www.shiyanlou.com/contests/lou5/challenges 以下代码是题目的答案 <?php head ...

  5. 将Opencv java中的Mat通过jni传递到C++中的方法

    public native void FindFeatures(long matAddrGr, long matAddrRgba); ... mRgba = new Mat(height, width ...

  6. LeetCode: 669 Trim a Binary Search Tree(easy)

    题目: Given a binary search tree and the lowest and highest boundaries as L and R, trim the tree so th ...

  7. 在 Ubuntu 上安装 Protobuf 3

    什么时候需要安装 如果使用 protoc 命令,遇到 Protoc not found,表示未安装.或者,执行时出现错误:This parser only recognizes "proto ...

  8. python读写xlsx

    1使用openpyxl库读写excel xlrd和xlwt处理的是xls文件,单个sheet最大行数是65535,如果有更大需要的,建议使用openpyxl函数,最大行数达到1048576.  如果数 ...

  9. Linux下查看磁盘剩余空间和文件夹大小

    1. du -sh 查看当前文件夹大小 2. du -sh * | sort -n 列出当前文件夹下的所有文件夹及其大小,并按照文件夹大小排序 du - sh *  //查看当前文件夹下所有文件的大小 ...

  10. React入门看这篇就够了

    摘要: 很多值得了解的细节. 原文:React入门看这篇就够了 作者:Random Fundebug经授权转载,版权归原作者所有. React 背景介绍 React 入门实例教程 React 起源于 ...