When binding a client TCP socket to a specific local port with Winsock SO_REUSEADDR has no effect

I bind a client TCP socket to a specific local port. To deal with the situation where the socket remains in TIME_WAIT state for some time, I use setsockopt() with SO_REUSEADDR in the socket.

It works on Linux, but does not work on Windows, I get a WSAEADDRINUSE on connect() call when the previous connection is still at TIME_WAIT .

MSDN is not entirely clear what should happen to client sockets:

[...] For server applications that need to bind multiple sockets to the same port number, consider using setsockopt ( SO_REUSEADDR ). Client applications, as a rule, do not need to bind to an all-connect connection, automatically selects an unused port. [...]

How can I avoid this?

+5
source share
3 answers

When you create a socket with socket() , it has only the type and protocol family. Ideal is to bind() it to a local address: port too.

The error you specified usually occurs when the last connection to the same host: the port did not have an elegant shutdown (FIN / ACK FIN / ACK). In these cases, the socket remains in TIME_WAIT state for a certain period of time (OS dependent, but configurable).

What happens when you try to use connect() for the same host and the same port, it uses the default socket name / address / port, etc., but this combination is already used by your zombie slot. To avoid this, you can change the local address: the port used to establish the connection by calling bind() after creating the socket, providing a sockaddr structure populated with your local address and random port.

 int main() { int ret, fd; struct sockaddr_in sa_dst; struct sockaddr_in sa_loc; char buffer[1024] = "GET / HTTP/1.1\r\nHost: www.google.com\r\n\r\n"; fd = socket(AF_INET, SOCK_STREAM, 0); // Local memset(&sa_loc, 0, sizeof(struct sockaddr_in)); sa_loc.sin_family = AF_INET; sa_loc.sin_port = htons(LOCAL_RANDOM_PORT); sa_loc.sin_addr.s_addr = inet_addr(LOCAL_IP_ADDRESS); ret = bind(fd, (struct sockaddr *)&sa_loc, sizeof(struct sockaddr)); assert(ret != -1); // Remote memset(&sa_dst, 0, sizeof(struct sockaddr_in)); sa_dst.sin_family = AF_INET; sa_dst.sin_port = htons(80); sa_dst.sin_addr.s_addr = inet_addr("64.233.163.104"); // google :) ret = connect(fd, (struct sockaddr *)&sa_dst, sizeof(struct sockaddr)); assert(ret != -1); send(fd, buffer, strlen(buffer), 0); recv(fd, buffer, sizeof(buffer), 0); printf("%s\r\n", buffer); } 

UPDATE Since using a specific local port is a requirement, consider setting SO_LINGER with l_onoff=1 and l_linger=0 so that your socket does not block when close / closesocket , it just ignores the data in the queue and (hopefully) closes fd. As a last resort, you can set the TIME_WAIT delay by changing the value of this registry key (very discouraged!):

 HKLM\SYSTEM\CurrentControlSet\Services\Tcpip\Parameters\TcpTimedWaitDelay 
+10
source

You did not indicate which Windows platform you are running on, which can affect things, and also that you can use the security subheading (i.e. are you an administrator?) ...

This may help: http://blogs.msdn.com/wndp/archive/2005/08/03/Anthony-Jones.aspx

0
source

Be careful when binding the local port. DO NOT use the return address "127.0.0.1", or you will get connection timeouts. It’s better not to fill out the sa_loc.sin_addr.s_addr file at all - this works fine.

0
source

Source: https://habr.com/ru/post/1308743/


All Articles