Ninguna de las respuestas existentes le dice a la gente cómo shutdown
y close
funciona a nivel de protocolo TCP, por lo que vale la pena agregar esto.
Una conexión TCP estándar se termina con una finalización de 4 vías:
- Una vez que un participante no tiene más datos para enviar, envía un paquete FIN al otro
- La otra parte devuelve un ACK para el FIN.
- Cuando la otra parte también finaliza la transferencia de datos, envía otro paquete FIN
- El participante inicial devuelve un ACK y finaliza la transferencia.
Sin embargo, hay otra forma "emergente" de cerrar una conexión TCP:
- Un participante envía un paquete RST y abandona la conexión.
- El otro lado recibe un RST y luego abandona la conexión también
En mi prueba con Wireshark, con las opciones de socket predeterminadas, shutdown
envía un paquete FIN al otro extremo, pero es todo lo que hace. Hasta que la otra parte le envíe el paquete FIN, aún podrá recibir datos. Una vez que esto sucedió, Receive
obtendrá un resultado de tamaño 0. Entonces, si usted es el primero en cerrar "enviar", debe cerrar el socket una vez que haya terminado de recibir datos.
Por otro lado, si llama close
mientras la conexión aún está activa (el otro lado todavía está activo y es posible que también tenga datos no enviados en el búfer del sistema), se enviará un paquete RST al otro lado. Esto es bueno para los errores. Por ejemplo, si cree que la otra parte proporcionó datos incorrectos o se negó a proporcionar datos (¿ataque DOS?), Puede cerrar el socket de inmediato.
Mi opinión sobre las reglas sería:
- Considere
shutdown
antes close
cuando sea posible
- Si terminó de recibir (datos de tamaño 0 recibidos) antes de decidir cerrar, cierre la conexión después de que finalice el último envío (si lo hubiera).
- Si desea cerrar la conexión normalmente, apague la conexión (con SHUT_WR, y si no le importa recibir datos después de este punto, también con SHUT_RD), y espere hasta que reciba datos de tamaño 0, y luego cierre el enchufe.
- En cualquier caso, si ocurrió algún otro error (tiempo de espera, por ejemplo), simplemente cierre el zócalo.
Implementaciones ideales para SHUT_RD y SHUT_WR
Lo siguiente no ha sido probado, confíe bajo su propio riesgo. Sin embargo, creo que esta es una forma razonable y práctica de hacer las cosas.
Si la pila TCP recibe un apagado solo con SHUT_RD, marcará esta conexión como no se esperan más datos. Las solicitudes pendientes y posteriores read
(independientemente de la secuencia en la que se encuentren) se devolverán con un resultado de tamaño cero. Sin embargo, la conexión sigue activa y utilizable; por ejemplo, aún puede recibir datos OOB. Además, el sistema operativo eliminará cualquier dato que reciba para esta conexión. Pero eso es todo, no se enviarán paquetes al otro lado.
Si la pila TCP recibe un apagado solo con SHUT_WR, marcará esta conexión ya que no se pueden enviar más datos. Se finalizarán todas las solicitudes de escritura pendientes, pero las solicitudes de escritura posteriores fallarán. Además, se enviará un paquete FIN a otro lado para informarles que no tenemos más datos para enviar.