Когда вы создаете сокет с 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