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

时间:2010-04-09 05:02:52

标签: c winapi sockets tcp winsock

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

它适用于Linux,但不适用于Windows,当前一个连接仍在WSAEADDRINUSE时,我会在connect()来电TIME_WAIT。{/ p>

MSDN并不完全清楚客户端套接字会发生什么:

  

[...]对于需要将多个套接字绑定到同一端口号的服务器应用程序,请考虑使用setsockoptSO_REUSEADDR)。客户端应用程序通常不需要在所有连接上调用bind-connect自动选择未使用的端口。 [...]

我该如何避免这种情况?

3 个答案:

答案 0 :(得分:10)

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

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

当您尝试connect()到同一主机和相同端口时会发生什么情况,它使用默认套接字的名称/地址/端口/等,但僵尸已经在使用此组合套接字。为避免这种情况,您可以通过在创建套接字后调用bind()来更改用于建立连接的本地地址:端口,提供填充了本地地址的sockaddr结构和随机 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);
}

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

TIME_WAIT

答案 1 :(得分:0)

您没有指定您正在运行的Windows平台,这可能会影响您正在运行的安全主体(例如您是管理员吗?)...

这可能有所帮助:http://blogs.msdn.com/wndp/archive/2005/08/03/Anthony-Jones.aspx

答案 2 :(得分:0)

小心绑定本地端口NOT使用环回地址“127.0.0.1”,否则您将获得连接超时。最好不要填充sa_loc.sin_addr.s_addr - 这很好。