При привязке клиентского сокета TCP с определенным локальным портом с Winsock SO_REUSEADDR не имеет никакого эффекта

Я связываю клиентский сокет TCP с определенным локальным портом. Обработать ситуацию, где сокет остается в TIME_WAIT заявляйте в течение некоторого времени, я использую setsockopt() с SO_REUSEADDR на сокете.

Это работает над Linux, но не работает над Windows, я добираюсь WSAEADDRINUSE на connect() звоните, когда предыдущее соединение будет все еще в TIME_WAIT.

MSDN не является точно четким, что должно произойти с клиентскими сокетами:

[...] Для серверных приложений, которые должны связать несколько сокетов с тем же номером порта, рассмотреть использование setsockopt (SO_REUSEADDR). Клиентские приложения обычно не должны звонить, связывают вообще — подключение выбирает неиспользуемый порт автоматически. [...]

Как я избегаю этого?

5
задан Alex B 9 April 2010 в 05:02
поделиться

2 ответа

Когда вы создаете сокет с помощью socket () , он имеет только тип и семейство протоколов. В идеале 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 = 1 и l_linger = 0 , чтобы ваш сокет не будет блокироваться при close / closesocket , он просто проигнорирует данные в очереди и (надеюсь) закроет fd. В крайнем случае вы можете настроить задержку TIME_WAIT , изменив значение этого раздела реестра (крайне не рекомендуется!):

HKLM\SYSTEM\CurrentControlSet\Services\Tcpip\Parameters\TcpTimedWaitDelay
10
ответ дан 13 December 2019 в 22:04
поделиться

Вы не указываете, на какой платформе Windows вы работаете, это может повлиять на такие вещи, как и принцип безопасности, под которым вы работаете (т. Е. вы админ?) ...

Это может помочь: http://blogs.msdn.com/wndp/archive/2005/08/03/Anthony-Jones.aspx

0
ответ дан 13 December 2019 в 22:04
поделиться
Другие вопросы по тегам:

Похожие вопросы: