假如端口被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 |