Żadna z istniejących odpowiedzi nie mówi ludziom, jak shutdown
i close
działa na poziomie protokołu TCP, dlatego warto to dodać.
Standardowe połączenie TCP zostaje zakończone przez czterostronną finalizację:
- Gdy uczestnik nie ma już danych do wysłania, wysyła pakiet FIN do drugiego
- Druga strona zwraca ACK dla FIN.
- Gdy druga strona również zakończy transfer danych, wysyła kolejny pakiet FIN
- Początkowy uczestnik zwraca ACK i finalizuje transfer.
Istnieje jednak inny „wschodzący” sposób zamknięcia połączenia TCP:
- Uczestnik wysyła pakiet RST i porzuca połączenie
- Druga strona otrzymuje RST, a następnie porzuca również połączenie
W moim teście z Wireshark, z domyślnymi opcjami gniazd, shutdown
wysyła pakiet FIN na drugi koniec, ale to wszystko, co robi. Dopóki druga strona nie wyśle Ci pakietu FIN, nadal możesz odbierać dane. Gdy to się Receive
stanie, otrzymasz wynik w rozmiarze 0. Jeśli więc jako pierwszy wyłączysz wysyłanie, powinieneś zamknąć gniazdo po zakończeniu odbierania danych.
Z drugiej strony, jeśli zadzwonisz, close
gdy połączenie jest nadal aktywne (druga strona jest nadal aktywna i możesz mieć również niewysłane dane w buforze systemowym), pakiet RST zostanie wysłany na drugą stronę. To jest dobre na błędy. Na przykład, jeśli uważasz, że druga strona podała nieprawidłowe dane lub odmówiła podania danych (atak DOS?), Możesz od razu zamknąć gniazdo.
Moje zdanie na temat zasad brzmiałoby:
- Zastanów się
shutdown
, zanim close
, jeśli to możliwe
- Jeśli zakończyłeś odbieranie (otrzymano dane o rozmiarze 0) przed podjęciem decyzji o zamknięciu, zamknij połączenie po zakończeniu ostatniego wysyłania (jeśli w ogóle).
- Jeśli chcesz normalnie zamknąć połączenie, zamknij połączenie (za pomocą SHUT_WR, a jeśli nie zależy ci na otrzymywaniu danych po tym momencie, również za pomocą SHUT_RD) i poczekaj, aż otrzymasz dane o rozmiarze 0, a następnie zamknij gniazdo elektryczne.
- W każdym razie, jeśli wystąpi inny błąd (na przykład przekroczenie limitu czasu), po prostu zamknij gniazdo.
Idealne implementacje dla SHUT_RD i SHUT_WR
Następujące nie zostały przetestowane, zaufaj na własne ryzyko. Uważam jednak, że jest to rozsądny i praktyczny sposób robienia rzeczy.
Jeśli stos TCP otrzyma zamknięcie tylko za pomocą SHUT_RD, oznacza to połączenie jako brak oczekiwanych danych. Wszelkie oczekujące i kolejne read
żądania (niezależnie od tego, w którym wątku się znajdują) zostaną zwrócone z wynikiem zerowym. Jednak połączenie jest nadal aktywne i użyteczne - na przykład nadal możesz odbierać dane OOB. Ponadto system operacyjny usunie wszystkie dane otrzymane dla tego połączenia. Ale to wszystko, żadne paczki nie zostaną wysłane na drugą stronę.
Jeśli stos TCP otrzyma zamknięcie tylko za pomocą SHUT_WR, oznacza to połączenie, ponieważ nie można wysłać więcej danych. Wszystkie oczekujące żądania zapisu zostaną zakończone, ale kolejne żądania zapisu zakończą się niepowodzeniem. Ponadto pakiet FIN zostanie wysłany na inną stronę, aby poinformować ich, że nie mamy więcej danych do wysłania.