Lorsque vous liez un socket TCP client à un port local spécifique avec Winsock, SO_REUSEADDR n’a aucun effet.

Je lie un socket TCP client à un port local spécifique. Pour gérer la situation où le socket rest à l’état TIME_WAIT pendant un certain temps, j’utilise setsockopt() avec SO_REUSEADDR sur un socket.

Cela fonctionne sous Linux, mais pas sous Windows. WSAEADDRINUSE lors de l’ WSAEADDRINUSE de connect() alors que la connexion précédente était encore dans TIME_WAIT .

MSDN ne sait pas exactement ce qui devrait se passer avec les sockets client:

[…] Pour les applications serveur qui doivent lier plusieurs sockets au même numéro de port, envisagez d’utiliser setsockopt ( SO_REUSEADDR ). Les applications client n’ont généralement pas besoin d’appeler une liaison. Connect choisit automatiquement un port inutilisé. […]

Comment puis-je éviter cela?

Lorsque vous créez une socket avec socket() , elle ne comporte qu’un type et une famille de protocoles. L’idéal est de le bind() à une adresse locale: le port aussi.

L’erreur que vous avez mentionnée se produit normalement lorsque la dernière connexion au même hôte: port n’a pas eu d’arrêt normal (FIN / ACK FIN / ACK). Dans ces cas, le socket rest à l’état TIME_WAIT pendant un certain temps (dépendant du système d’exploitation, mais réglable).

Ce qui se passe alors, c’est que lorsque vous essayez de vous connect() au même hôte et au même port, il utilise le nom / adresse / port / etc du socket par défaut, mais cette combinaison est déjà utilisée par votre socket zombie . Pour éviter cela, vous pouvez modifier l’adresse locale: port utilisé pour établir la connexion en appelant bind() après la création du socket, en fournissant la structure sockaddr remplie de votre adresse locale et d’un port aléatoire .

 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); } 

UPDATE : Comme l’utilisation d’un port local spécifique est une exigence, définissez SO_LINGER avec l_onoff=1 et l_linger=0 pour que votre socket ne se bloque pas lors de la close / la close closesocket , il ignorera simplement les données en queue et (espérons-le) ferme le fd. En dernier recours, vous pouvez ajuster le délai TIME_WAIT en modifiant la valeur de cette clé de registre (fortement déconseillée!):

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

Vous ne spécifiez pas sur quelle plate-forme Windows vous exécutez, cela pourrait affecter des choses comme le principal de sécurité sous lequel vous vous exécutez (c.-à-d. Êtes-vous administrateur?) …

Cela peut aider: http://blogs.msdn.com/wndp/archive/2005/08/03/Anthony-Jones.aspx

Veillez à ne pas utiliser l’adresse de bouclage “127.0.0.1” dans le port local, car vous obtiendrez des délais de connexion. Mieux vaut ne pas peupler le sa_loc.sin_addr.s_addr du tout – cela fonctionne très bien.