c - 使用 Winsock 将客户端 TCP 套接字绑定(bind)到特定本地端口时,SO_REUSEADDR 没有任何效果

标签 c winapi sockets tcp winsock

我正在将一个客户端 TCP 套接字绑定(bind)到一个特定的本地端口。为了处理套接字在一段时间内保持TIME_WAIT 状态的情况,我在套接字上使用了带有SO_REUSEADDRsetsockopt()

它适用于 Linux,但不适用于 Windows,当上一个连接仍在 TIME_WAITconnect() 调用时得到 WSAEADDRINUSE.

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. [...]

如何避免这种情况?

最佳答案

当您使用socket() 创建一个套接字时,它只有一个类型和一个协议(protocol)族。理想的是 bind() 它也到本地地址:端口。

您提到的错误通常发生在与同一主机的最后一个连接时:端口没有正常关闭(FIN/ACK FIN/ACK)。在这些情况下,套接字会在一段时间内保持TIME_WAIT 状态(取决于操作系统,但可调整)。

当您尝试 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);
}

更新:由于需要使用特定的本地端口,请考虑将 SO_LINGER 设置为 l_onoff=1l_linger=0 所以你的套接字不会在 close/closesocket 时阻塞,它只会忽略排队的数据并(希望)关闭 fd。作为最后的手段,您可以通过更改此注册表项的值来调整 TIME_WAIT 延迟(非常不鼓励!):

HKLM\SYSTEM\CurrentControlSet\Services\Tcpip\Parameters\TcpTimedWaitDelay

关于c - 使用 Winsock 将客户端 TCP 套接字绑定(bind)到特定本地端口时,SO_REUSEADDR 没有任何效果,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/2605182/

相关文章:

c - 为什么在函数内部声明后结构体不存在?

c - 如何将 u32_t 中的十六进制值转换为其相应的 char/ascii 值?

c - 是否可以在没有线程0的情况下运行omp?

Node.js REST 与套接字

c++ - 为什么无效套接字在 WinSock2.h (c++) 中定义为 ~0?

c - C中的字符串初始化

c++ - 为什么我可以在没有调用 CoInitializeEx 的情况下调用 StringFromCLSID?

c - LPSTREAM 将整个 Stream 内容读入 unsigned char* 数组

c# - Send Message 和 Post Message 之间有什么区别,它们与 C#、WPF 和纯 Windows 编程有什么关系?

c++ - boost io_service 不会重置