假如端口被socket使用过,并且利用socket.close()来关闭连接,但此时端口还没有释放,要经过一个TIME_WAIT的过程之后才能使用。为了实现端口的马上复用,可以选择setsockopt()函数来达到目的。
python:
import socket
tcp1=socket.socket(socket.AF_INET,socket.SOCK_STREAM)
tcp1.setsockopt(socket.SOL_SOCKET,socket.SO_REUSEADDR,1)
tcp1.bind('1.1.1.1',12345)
此为tcp的例子,udp一样
c:
s = socket(AF_INET, SOCK_STREAM, 0);
/* What you need to do is add the following two line to your code */
unsigned value = 1;
setsockopt(s, SOL_SOCKET, SO_REUSEADDR, &value, sizeof(valu
e));
/* Then do other things */
listen(s, SOMAXCONN);
/* ... */
写Socket程序的时候经常会遇到这个问题:如果自己的程序不小心崩溃了,重新启动程序的时候往往会在bind调用上失败,错误原因为Address Already In Use,往往要等待两分钟才能再次绑定。但是在很多的程序(比如nginx)中好像并不存在这个问题,就算被KILL了也能立刻重启。这个区别还是比较头痛的。
其实我猜Unix Socket编程这样的书上有讨论过这方面的问题,不过我竟然没有这方面的书籍(完全靠man看来也是行不通啊)。我曾经天真的以为,在收到SIGTERM这样的信号的时候把所有套接字全部关闭可以解决问题。后来才发现无济于事。Google了这方面的文章才知道,解决这个问题理论上有三种办法。
1.SOCK_REUSEADDR:曾经在研究内网穿透的时候跟这个东西打过交道。如果一个监听的套接字被设置为允许地址复用,那么套接字绑定到的地址不会被独占,所以必然不会存在Address already in use的问题。而且如果有两个套接字绑定到同一个端口,也只允许一个套接字进行监听,后一个套接字在调用listen的时候会报错。因此这个方案显然是最佳方案。对于完全不知道我在说什么的童鞋,你们只要这么做
s = socket(AF_INET, SOCK_STREAM, 0);
/* What you need to do is add the following two line to your code */
unsigned value = 1;
setsockopt(s, SOL_SOCKET, SO_REUSEADDR, &value, sizeof(value));
/* Then do other things */listen(s, SOMAXCONN);/* ... */
2.不过据说这样的做法容易产生安全性问题,某些操作系统(没有指明Linux或是BSD或是Windows)允许分别属于两个进程的两个套接字绑定到两个地址的同一个端口。大多数程序把套接字绑定到0.0.0.0这个地址上进行监听(也即监听所有网络设备),这种情形下另外一个进程可以绑定到127.0.0.1或者是其他网络设备的IP地址的同一个端口,并进行监听,就可能把外部的连接给拦截下来(因为127.0.0.1这样的IP是属于特定设备的,比0.0.0.0这虚拟设备更specific)。
而解决这个安全性的问题的方法其实也不难(其实换个没问题的操作系统就可以了不是?),只要把套接字绑定绑定到具体的网络设备的IP地址(比如绑定到127.0.0.1,或者a.b.c.d)即可,大不了为每个网络设备建一个套接字。如果实施起来有难度,只能考虑后面的两种方法。
3.让客户端先关闭连接。如果所有的连接关闭事件都在客户端首先发生,那么也不会存在这个问题。不过这种做法可能需要修改协议,而且貌似很容易恶意连接攻击。修改系统TCP超时时间,这种方法很不推荐。
原文:http://www.2cto.com/kf/201208/150347.html
-----------------------------
附: python socket模块函数列表---2012-12-09:
socket | index c:\python27\lib\socket.py |
This module provides socket operations and some related functions.
On Unix, it supports IP (Internet Protocol) and Unix domain sockets.
On other systems, it only supports IP. Functions specific for a
socket are available as methods of the socket object.
Functions:
socket() -- create a new socket object
socketpair() -- create a pair of new socket objects [*]
fromfd() -- create a socket object from an open file descriptor [*]
gethostname() -- return the current hostname
gethostbyname() -- map a hostname to its IP number
gethostbyaddr() -- map an IP number or hostname to DNS info
getservbyname() -- map a service name and a protocol name to a port number
getprotobyname() -- map a protocol name (e.g. 'tcp') to a number
ntohs(), ntohl() -- convert 16, 32 bit int from network to host byte order
htons(), htonl() -- convert 16, 32 bit int from host to network byte order
inet_aton() -- convert IP addr string (123.45.67.89) to 32-bit packed format
inet_ntoa() -- convert 32-bit packed format IP to string (123.45.67.89)
ssl() -- secure socket layer support (only available if configured)
socket.getdefaulttimeout() -- get the default timeout value
socket.setdefaulttimeout() -- set the default timeout value
create_connection() -- connects to an address, with an optional timeout and
optional source address.
[*] not available on all platforms!
Special objects:
SocketType -- type object for socket objects
error -- exception raised for I/O errors
has_ipv6 -- boolean value indicating if IPv6 is supported
Integer constants:
AF_INET, AF_UNIX -- socket domains (first argument to socket() call)
SOCK_STREAM, SOCK_DGRAM, SOCK_RAW -- socket types (second argument)
Many other constants may be defined; these may be used in calls to
the setsockopt() and getsockopt() methods.
Modules | ||||||
|
Classes | ||||||||||||||||||||||||||||||||||||||||||
|
Functions | ||
|
Data | ||
AF_APPLETALK = 16 AF_DECnet = 12 AF_INET = 2 AF_INET6 = 23 AF_IPX = 6 AF_IRDA = 26 AF_SNA = 11 AF_UNSPEC = 0 AI_ADDRCONFIG = 1024 AI_ALL = 256 AI_CANONNAME = 2 AI_NUMERICHOST = 4 AI_NUMERICSERV = 8 AI_PASSIVE = 1 AI_V4MAPPED = 2048 CAPI = <capsule object "_socket.CAPI"> EAI_AGAIN = 11002 EAI_BADFLAGS = 10022 EAI_FAIL = 11003 EAI_FAMILY = 10047 EAI_MEMORY = 8 EAI_NODATA = 11001 EAI_NONAME = 11001 EAI_SERVICE = 10109 EAI_SOCKTYPE = 10044 INADDR_ALLHOSTS_GROUP = -536870911 INADDR_ANY = 0 INADDR_BROADCAST = -1 INADDR_LOOPBACK = 2130706433 INADDR_MAX_LOCAL_GROUP = -536870657 INADDR_NONE = -1 INADDR_UNSPEC_GROUP = -536870912 IPPORT_RESERVED = 1024 IPPORT_USERRESERVED = 5000 IPPROTO_ICMP = 1 IPPROTO_IP = 0 IPPROTO_RAW = 255 IPPROTO_TCP = 6 IPPROTO_UDP = 17 IPV6_CHECKSUM = 26 IPV6_DONTFRAG = 14 IPV6_HOPLIMIT = 21 IPV6_HOPOPTS = 1 IPV6_JOIN_GROUP = 12 IPV6_LEAVE_GROUP = 13 IPV6_MULTICAST_HOPS = 10 IPV6_MULTICAST_IF = 9 IPV6_MULTICAST_LOOP = 11 IPV6_PKTINFO = 19 IPV6_RECVRTHDR = 38 IPV6_RTHDR = 32 IPV6_UNICAST_HOPS = 4 IPV6_V6ONLY = 27 IP_ADD_MEMBERSHIP = 12 IP_DROP_MEMBERSHIP = 13 IP_HDRINCL = 2 IP_MULTICAST_IF = 9 IP_MULTICAST_LOOP = 11 IP_MULTICAST_TTL = 10 IP_OPTIONS = 1 IP_RECVDSTADDR = 25 IP_TOS = 3 IP_TTL = 4 MSG_CTRUNC = 512 MSG_DONTROUTE = 4 MSG_OOB = 1 MSG_PEEK = 2 MSG_TRUNC = 256 NI_DGRAM = 16 NI_MAXHOST = 1025 NI_MAXSERV = 32 NI_NAMEREQD = 4 NI_NOFQDN = 1 NI_NUMERICHOST = 2 NI_NUMERICSERV = 8 RCVALL_MAX = 3 RCVALL_OFF = 0 RCVALL_ON = 1 RCVALL_SOCKETLEVELONLY = 2 SHUT_RD = 0 SHUT_RDWR = 2 SHUT_WR = 1 SIO_KEEPALIVE_VALS = 2550136836L SIO_RCVALL = 2550136833L SOCK_DGRAM = 2 SOCK_RAW = 3 SOCK_RDM = 4 SOCK_SEQPACKET = 5 SOCK_STREAM = 1 SOL_IP = 0 SOL_SOCKET = 65535 SOL_TCP = 6 SOL_UDP = 17 SOMAXCONN = 2147483647 SO_ACCEPTCONN = 2 SO_BROADCAST = 32 SO_DEBUG = 1 SO_DONTROUTE = 16 SO_ERROR = 4103 SO_EXCLUSIVEADDRUSE = -5 SO_KEEPALIVE = 8 SO_LINGER = 128 SO_OOBINLINE = 256 SO_RCVBUF = 4098 SO_RCVLOWAT = 4100 SO_RCVTIMEO = 4102 SO_REUSEADDR = 4 SO_SNDBUF = 4097 SO_SNDLOWAT = 4099 SO_SNDTIMEO = 4101 SO_TYPE = 4104 SO_USELOOPBACK = 64 TCP_MAXSEG = 4 TCP_NODELAY = 1 __all__ = ['getfqdn', 'create_connection', 'AF_APPLETALK', 'AF_DECnet', 'AF_INET', 'AF_INET6', 'AF_IPX', 'AF_IRDA', 'AF_SNA', 'AF_UNSPEC', 'AI_ADDRCONFIG', 'AI_ALL', 'AI_CANONNAME', 'AI_NUMERICHOST', 'AI_NUMERICSERV', 'AI_PASSIVE', 'AI_V4MAPPED', 'CAPI', 'EAI_AGAIN', 'EAI_BADFLAGS', ...] errorTab = {10004: 'The operation was interrupted.', 10009: 'A bad file handle was passed.', 10013: 'Permission denied.', 10014: 'A fault occurred on the network??', 10022: 'An invalid operation was attempted.', 10035: 'The socket operation would block', 10036: 'A blocking operation is already in progress.', 10048: 'The network address is in use.', 10054: 'The connection has been reset.', 10058: 'The network has been shut down.', ...} has_ipv6 = True |