Я связываю клиентский сокет TCP с определенным локальным портом. Обработать ситуацию, где сокет остается в TIME_WAIT
заявляйте в течение некоторого времени, я использую setsockopt()
с SO_REUSEADDR
на сокете.
Это работает над Linux, но не работает над Windows, я добираюсь WSAEADDRINUSE
на connect()
звоните, когда предыдущее соединение будет все еще в TIME_WAIT
.
MSDN не является точно четким, что должно произойти с клиентскими сокетами:
[...] Для серверных приложений, которые должны связать несколько сокетов с тем же номером порта, рассмотреть использование
setsockopt
(SO_REUSEADDR
). Клиентские приложения обычно не должны звонить, связывают вообще — подключение выбирает неиспользуемый порт автоматически. [...]
Как я избегаю этого?
Когда вы создаете сокет с помощью 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
Вы не указываете, на какой платформе Windows вы работаете, это может повлиять на такие вещи, как и принцип безопасности, под которым вы работаете (т. Е. вы админ?) ...
Это может помочь: http://blogs.msdn.com/wndp/archive/2005/08/03/Anthony-Jones.aspx