使用 Winsock 将客户端 TCP 套接字绑定到特定的本地端口时,SO_REUSEADDR 没有任何影响 [英] When binding a client TCP socket to a specific local port with Winsock, SO_REUSEADDR does not have any effect
问题描述
我将一个客户端 TCP 套接字绑定到一个特定的本地端口.为了处理套接字保持在 TIME_WAIT
状态一段时间的情况,我在套接字上使用 setsockopt()
和 SO_REUSEADDR
.
I'm binding a client TCP socket to a specific local port. To handle the situation where the socket remains in TIME_WAIT
state for some time, I use setsockopt()
with SO_REUSEADDR
on a socket.
它适用于 Linux,但不适用于 Windows,当前一个连接仍处于 TIME_WAIT
时,我在 connect()
调用中得到 WSAEADDRINUSE
代码>.
It works on Linux, but does not work on Windows, I get WSAEADDRINUSE
on connect()
call when the previous connection is still in TIME_WAIT
.
MSDN 并不清楚客户端套接字会发生什么:
MSDN is not exactly clear what should happen with client sockets:
[...] 对于需要将多个套接字绑定到同一端口号的服务器应用程序,请考虑使用 setsockopt
(SO_REUSEADDR
).客户端应用程序通常根本不需要调用 bind——connect 会自动选择一个未使用的端口.[...]
[...] 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. [...]
如何避免这种情况?
推荐答案
当您使用 socket()
创建套接字时,它只有一个类型和一个协议族.理想的做法是将其 bind()
也bind()
绑定到本地地址:端口.
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.
您提到的错误通常发生在上次连接到同一主机时:端口没有正常关闭(FIN/ACK FIN/ACK).在这些情况下,套接字会在一段时间内保持 TIME_WAIT
状态(取决于操作系统,但可调整).
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_WAIT
state for a certain period of time (OS dependent, but adjustable).
然后发生的事情是,当您尝试 connect()
到同一主机和同一端口时,它使用默认套接字的名称/地址/端口/等,但这种组合已被使用你的 zombie 插座.为避免这种情况,您可以在创建套接字后通过调用 bind()
来更改用于建立连接的本地地址:端口,提供填充本地地址的 sockaddr
结构和一个随机端口.
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 zombie socket. To avoid this, you can change the local address:port used to establish the connection by calling bind()
after the socket creation, providing the sockaddr
struct filled with your local address and a random port.
int main() {
int ret, fd;
struct sockaddr_in sa_dst;
struct sockaddr_in sa_loc;
char buffer[1024] = "GET / HTTP/1.1
Host: www.google.com
";
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
", buffer);
}
UPDATE:由于需要使用特定的本地端口,请考虑将 SO_LINGER
设置为 l_onoff=1
和 l_linger=0
所以你的套接字不会在 close
/closesocket
时阻塞,它只会忽略排队的数据并(希望)关闭 fd.作为最后的手段,您可以通过更改此注册表项的值来调整 TIME_WAIT
延迟(非常不鼓励!):
UPDATE: As using a specific local port is a requirement, consider setting SO_LINGER
with l_onoff=1
and l_linger=0
so 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_WAIT
delay by changing the value of this registry key (highly discouraged!):
HKLMSYSTEMCurrentControlSetServicesTcpipParametersTcpTimedWaitDelay
这篇关于使用 Winsock 将客户端 TCP 套接字绑定到特定的本地端口时,SO_REUSEADDR 没有任何影响的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!