Is there a way to reopen a socket?

2020-07-09 08:35发布

I create many "short-term" sockets in some code that look like that :

nb=1000
for i in range(nb):
    sck = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    sck.connect((adr, prt)
    sck.send('question %i'%i)
    sck.shutdown(SHUT_WR)
    answer=sck.recv(4096)
    print 'answer %i : %s' % (%i, answer)
    sck.close()

This works fine, as long as nb is "small" enough.

As nb might be quite large though, I'd like to do something like this

sck = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
sck.connect((adr, prt)
for i in range(nb):
    reopen(sck) # ? ? ?
    sck.send('question %i'%i)
    sck.shutdown(SHUT_WR)
    answer=sck.recv(4096)
    print 'answer %i : %s' % (%i, answer)
sck.close()

So the question is :
Is there any way to "reuse" a socket that has been shutdown ?

4条回答
啃猪蹄的小仙女
2楼-- · 2020-07-09 08:44

I'm not sure what the extra overhead would be like, but you could fully close and reopen the socket. You need to set SO_REUSEADDR, and bind to a specific port you can reuse.

sck = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
sck.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
查看更多
我欲成王,谁敢阻挡
3楼-- · 2020-07-09 08:52

No, this is a limitation of the underlying C sockets (and the TCP/IP protocol, for that matter). My question to you is: why are you shutting them down when you can architect your application to use them?

The problem with many short-term sockets is that shutting them down puts them in a state where they cannot be used for a while (basically, twice the packet lifetime, to ensure any packets in the network either arrive and are discarded, or get discarded by the network itself). Basically what happens is that, in the 4-tuple that needs to be unique (source ip, source port, destination ip, destination port), the first one and last two tend to always be the same so, when you run out of source ports, you're hosed.

We've struck this problem in software before where it only became evident when we ran on faster machines (since we could use many more sessions).

Why dont you just open up the socket and continue to use it? It looks like your protocol is a simple request/response one, which should be easily do-able with that approach.

Something like:

sck = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
sck.connect((adr, prt)
for i in range(nb):
    sck.send('question %i'%i)
    answer=sck.recv(4096)
    print 'answer %i : %s' % (%i, answer)
sck.close()

Update:

One possibility (and we've done this before) if you're running out of connection due to this continual open/close, is to detect the problem and throttle it. Consider the following code (the stuff I've added is more pseudo-code than Python since I haven't touched Python for quite a while):

for i in range(nb):
    sck = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    sck.connect((adr, prt)

    while sck.error() == NO_SOCKETS_AVAIL:
        sleep 250 milliseconds
        sck.connect((adr, prt)

    sck.send('question %i'%i)
    sck.shutdown(SHUT_WR)
    answer=sck.recv(4096)
    print 'answer %i : %s' % (%i, answer)
    sck.close()

Basically, it lets you run at full speed while there are plenty of resources but slows down when you strike your problem area. This is actually what we did to our product to "fix" the problem of failing when resources got low. We would have re-architected it except for the fact it was a legacy product approaching end of life and we were basically in the fix-at-minimal-cost mode for service.

查看更多
叛逆
4楼-- · 2020-07-09 08:52

If you keep opening and closing sockets for the same port then it is better to open this socket once and keep it open, then you will have much better performance, since opening and closing will take some time.

If you have many short-term sockets, you may also consider datagram sockets (UDP). Note that you do not have a guarantee for arrival in this case, also order of the packets is not guaranteed.

查看更多
戒情不戒烟
5楼-- · 2020-07-09 08:56

You cannot reuse the socket but it would not help if you could, since you are running out of ports, not sockets. Each port will stay in TIME_WAIT state for twice the maximum segment lifetime after you initiate the shutdown. It would be best not to require so many ports within such a short period, but if you need to use a large number you may be able to increase the ephemeral port range.

Port numbers are 16 bits and therefore there are only 65536 of them. If you are on Windows or Mac OS X then by default, ephemeral ports are chosen from the range 49152 to 65535. This is the official range designated by IANA, but on Linux and Solaris (often used for high traffic servers) the default range starts at 32768 to allow for more ports. You may want to make a similar change to your system if it is not already set that way and you are in need of more ephemeral ports.

It is also possible to reduce the maximum segment lifetime on your system, reducing the amount of time that each socket is in TIME_WAIT state, or to use SO_REUSEADDR or SO_LINGER in some cases to reuse ports before the time has expired. However this can at least theoretically cause older connections to be mixed up with newer connections that happen to be using the same port number, if some packets from the older connections are slow to arrive, so is generally not a good idea.

查看更多
登录 后发表回答