问题描述
我绑定的客户的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上,我得到
打电话的时候,在previous连接仍然处于 WSAEADDRINUSE
不起作用连接() TIME_WAIT
。
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都连自动选择一个未使用的端口。 [...]
我如何避免这种情况?
推荐答案
当您创建插座插座()
,它只有一个类型和一个协议族。最理想的是绑定()
到一个本地地址:端口太
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).
当您尝试连接()
到同一个主机和相同的端口会发生什么事则是,它使用默认的套接字的名称/地址/端口/等,但此组合已在使用由您的僵尸的插座。为了避免这种情况,您可以更改本地地址:端口用于建立通过调用绑定()
插座创建后,提供了的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\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);
}
更新:由于使用特定的本地端口是一个要求,考虑设置
,它会忽略排队的数据和(希望)关闭FD。作为最后的手段,你可以通过更改此注册表项的值调整 SO_LINGER
与 l_onoff = 1
和 l_linger = 0
让你的插座将无法在关闭
/ 关闭套接字 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 adjusting the TIME_WAIT
delay by changing the value of this registry key (highly discouraged!):
HKLM\SYSTEM\CurrentControlSet\Services\Tcpip\Parameters\TcpTimedWaitDelay
这篇关于当绑定客户端TCP套接字到一个特定的本地端口的Winsock,SO_REUSEADDR没有任何影响的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!