C语言 使用 Winsock 将客户端 TCP 套接字绑定到特定本地端口时,SO_REUSEADDR 没有任何影响
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/2605182/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
When binding a client TCP socket to a specific local port with Winsock, SO_REUSEADDR does not have any effect
提问by Alex B
I'm binding a clientTCP socket to a specific local port. To handle the situation where the socket remains in TIME_WAITstate for some time, I use setsockopt()with SO_REUSEADDRon a socket.
我正在将客户端TCP 套接字绑定到特定的本地端口。为了处理套接字保持TIME_WAIT状态一段时间的情况,我在套接字上使用setsockopt()with SO_REUSEADDR。
It works on Linux, but does not work on Windows, I get WSAEADDRINUSEon connect()call when the previous connection is still in TIME_WAIT.
它可以在Linux,但没有工作在Windows上,我得到WSAEADDRINUSE的connect()电话时,先前的连接仍然在TIME_WAIT。
MSDN is not exactly clear what should happen with client sockets:
MSDN 并不清楚客户端套接字会发生什么:
[...] For server applications that need to bind multiple sockets to the same port number, consider using
setsockopt(SO_REUSEADDR). Client applications usually need not call bind at all—connect chooses an unused port automatically. [...]
[...] 对于需要将多个套接字绑定到同一端口号的服务器应用程序,请考虑使用
setsockopt(SO_REUSEADDR)。客户端应用程序通常根本不需要调用 bind——connect 会自动选择一个未使用的端口。[...]
How do I avoid this?
我如何避免这种情况?
回答by jweyrich
When you create a socket with socket(), it has only a type and a protocol family. The ideal is to bind()it to a local address:port too.
当您使用 来创建套接字时socket(),它只有一个类型和一个协议族。理想的情况是bind()它也到本地地址:端口。
The error you mentioned normally happens when the last connection to the same host:port didn't have a graceful shutdown (FIN/ACK FIN/ACK). In these cases, the socket stays in TIME_WAITstate for a certain period of time (OS dependent, but adjustable).
您提到的错误通常发生在上次连接到同一主机时:端口没有正常关闭(FIN/ACK FIN/ACK)。在这些情况下,套接字会在TIME_WAIT一段时间内保持状态(取决于操作系统,但可调整)。
What happens then is when you try to connect()to the same host and same port, it uses the default socket's name/address/port/etc, but this combination is already in use by your zombiesocket. To avoid this, you can change the local address:port used to establish the connection by calling bind()after the socket creation, providing the sockaddrstruct filled with your local address and a randomport.
然后发生的情况是,当您尝试访问connect()相同的主机和相同的端口时,它使用默认套接字的名称/地址/端口/等,但是这种组合已经被您的僵尸套接字使用了。为了避免这种情况,您可以通过bind()在创建套接字后调用来更改用于建立连接的本地地址:端口,提供sockaddr填充了您的本地地址和随机端口的结构。
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: As using a specific local port is a requirement, consider setting SO_LINGERwith l_onoff=1and l_linger=0so your socket won't block upon close/closesocket, it will just ignore queued data and (hopefully) close the fd. As a last resort you can adjust the TIME_WAITdelay by changing the value of this registry key (highly discouraged!):
更新:由于需要使用特定的本地端口,因此请考虑设置SO_LINGERwith以便您的套接字不会阻塞/ ,它只会忽略排队的数据并(希望)关闭 fd。作为最后的手段,您可以通过更改此注册表项的值来调整延迟(非常不鼓励!):l_onoff=1l_linger=0closeclosesocketTIME_WAIT
HKLM\SYSTEM\CurrentControlSet\Services\Tcpip\Parameters\TcpTimedWaitDelay
回答by Len Holgate
You don't specify which Windows platform you're running on, that may affect things as may the security principal that you're running under (i.e. are you admin?)...
您没有指定您正在运行的 Windows 平台,这可能会影响您正在运行的安全主体(即您是管理员?)...
This may help: http://blogs.msdn.com/wndp/archive/2005/08/03/Anthony-Jones.aspx
这可能会有所帮助:http: //blogs.msdn.com/wndp/archive/2005/08/03/Anthony-Jones.aspx
回答by Pierre
Be careful in binding the local port NOT to use the loopback address "127.0.0.1", or you will get connection timeouts. Better not to populate the sa_loc.sin_addr.s_addr at all - that works just fine.
绑定本地端口时要小心,不要使用环回地址“127.0.0.1”,否则会导致连接超时。最好不要填充 sa_loc.sin_addr.s_addr - 工作得很好。

