zamknięcie vs zamknięcie gniazda?

W C, zrozumiałem, że jeśli zamkniemy Gniazdo, oznacza to, że gniazdo zostanie zniszczone i może być ponownie użyte później.

A co z wyłączeniem? Opis mówi, że zamyka połowę połączenia dupleksu z tym gniazdem. Ale czy to gniazdo zostanie zniszczone jak close wywołanie systemowe?

Author: Tshepang, 2010-11-12

8 answers

To jest wyjaśnione w przewodniku Beeja dotyczącym sieci. shutdown jest elastycznym sposobem blokowania komunikacji w jednym lub obu kierunkach. Gdy drugim parametrem jest SHUT_RDWR, blokuje zarówno wysyłanie, jak i odbieranie (jak close). Jednak close jest sposobem na zniszczenie gniazda.

Z shutdown, nadal będziesz w stanie odbierać oczekujące dane, które już wysłał peer (dzięki Joey Adams za odnotowanie tego).

 154
Author: Matthew Flaschen,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-02-01 13:06:32

Żadna z istniejących odpowiedzi nie mówi ludziom, jak shutdown i close działają na poziomie protokołu TCP, więc warto to dodać.

Standard TCP kończy się finalizacją 4-kierunkową:
  1. gdy Uczestnik nie ma więcej danych do wysłania, wysyła pakiet FIN do drugiego
  2. druga strona zwraca ACK dla FIN.
  3. gdy druga strona również zakończyła transfer danych, wysyła kolejny pakiet FIN
  4. początkowy uczestnik zwraca ACK i finalizuje transfer.
Jednak istnieje inny "emergentny" sposób na zamknięcie połączenia TCP:
  1. uczestnik wysyła pakiet RST i rezygnuje z połączenia
  2. druga strona otrzymuje RST, a następnie również porzuca 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ę stanie, twój Receive otrzyma wynik o rozmiarze 0. Więc jeśli jesteś pierwszym, który wyłączył "wyślij", powinieneś zamknąć gniazdo po zakończeniu odbierania danych.

Z drugiej strony, Je ¶ li wywo3asz close, gdy po3 ± czenie jest nadal aktywne (druga strona jest nadal aktywna i mo ¿esz mieæ niezbêdne dane w buforze systemowym), pakiet RST zostanie wys3any do drugiej strony. Jest to dobre dla błędów. Na przykład, jeśli uważasz, że druga strona podała błędne dane lub odmówiła podać dane (atak DOS?), można od razu zamknąć Gniazdo.

Moje zdanie na temat zasad brzmiałoby:

  1. rozważ shutdown przed close Kiedy to możliwe
  2. Jeśli odebrałeś (odebrano dane o rozmiarze 0) przed podjęciem decyzji o wyłączeniu, Zamknij połączenie po zakończeniu ostatniego wysłania (jeśli w ogóle).
  3. Jeśli chcesz normalnie zamknąć połączenie, zamknij je (używając shut_wr, a jeśli nie zależy ci na odbieraniu danych po tym punkcie, używając SHUT_RD jako dobrze), i poczekaj, aż otrzymasz dane o rozmiarze 0, a następnie zamknij Gniazdo.
  4. w każdym przypadku, jeśli wystąpi jakikolwiek inny błąd (na przykład timeout), po prostu zamknij Gniazdo.

Idealne implementacje dla SHUT_RD i SHUT_WR

Poniższe nie zostały przetestowane, zaufaj na własne ryzyko. Uważam jednak, że jest to rozsądny i praktyczny sposób działania.

Jeśli stos TCP otrzyma zamknięcie tylko z SHUT_RD, oznacza to połączenie jako nie oczekuje się więcej danych. Wszystkie oczekujące i kolejne żądania read (niezależnie od tego, w którym wątku się znajdują) zostaną zwrócone z wynikiem o rozmiarze zerowym. Jednak połączenie jest nadal aktywne i użyteczne-nadal można na przykład odbierać dane OOB. Ponadto system operacyjny upuści wszelkie dane, które otrzyma za to połączenie. Ale to wszystko, żadne paczki nie będą wysyłane na drugą stronę.

Jeśli stos TCP otrzyma zamknięcie tylko z shut_wr, oznacza to połączenie, ponieważ żadne więcej danych nie może być wysłane. Wszystkie oczekujące żądania zapisu zostaną zakończone, ale kolejne żądania zapisu nie powiodą się. Ponadto, pakiet FIN zostanie wysłany do innej strony, aby poinformować ich, że nie mamy więcej danych do wysłania.

 103
Author: Earth Engine,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-07-29 23:29:19

Istnieją pewne ograniczenia z close(), których można uniknąć, jeśli zamiast tego używa się shutdown().

close() zakończy oba kierunki na połączeniu TCP. Czasami chcesz powiedzieć drugiemu punktowi końcowemu, że wysyłasz dane, ale nadal chcesz je odbierać.

close() zmniejsza liczbę odwołań deskryptorów (utrzymywaną we wpisie tabeli plików i zlicza liczbę aktualnie otwartych deskryptorów, które odnoszą się do pliku/gniazda) i nie zamyka gniazda / pliku jeśli deskryptor nie jest równy 0. Oznacza to, że jeśli rozwidlasz się, oczyszczanie następuje dopiero po spadku liczby referencji do 0. Za pomocą shutdown() można zainicjować normalną sekwencję zamkniętą TCP ignorując liczbę referencji.

Parametry są następujące:

int shutdown(int s, int how); // s is socket descriptor

int how może być:

SHUT_RD lub 0 Dalsze otrzymywanie jest niedozwolone

SHUT_WR lub 1 Dalsze wysyłanie nie jest dozwolone

SHUT_RDWR lub 2 Dalsze wysyłanie i odbieranie są niedozwolone

 31
Author: Milan,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-01-17 00:50:05

Może to być specyficzne dla platformy, jakoś w to wątpię, ale w każdym razie najlepsze wyjaśnienie, jakie widziałem, to tutaj na tej stronie msdn, gdzie wyjaśniają o zamykaniu, opcjach pozostawania, zamykaniu gniazd i ogólnych sekwencjach zakończenia połączenia.

Podsumowując, użyj shutdown, aby wysłać sekwencję zamykania na poziomie TCP i użyj close, aby zwolnić zasoby używane przez struktury danych gniazd w procesie. Jeśli nie wydałeś jawnej sekwencji wyłączania do czasu wywołania Zamknij wtedy jeden jest inicjowany dla Ciebie.

 14
Author: Len Holgate,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2010-11-18 08:33:34

Odniosłem również sukces pod Linuksem używając shutdown() z jednego pthreada, aby zmusić inny pthread aktualnie zablokowany w connect() do wcześniejszego przerwania.

Pod innymi osami (przynajmniej OSX), stwierdziłem, że wywołanie close() wystarczyło, aby uzyskać connect() fail.

 6
Author: Toby,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2010-11-15 08:56:33

"shutdown () nie zamyka deskryptora pliku-po prostu zmienia jego użyteczność. Aby uwolnić deskryptor gniazda, musisz użyć metody close ()."1

 6
Author: sand_storm_of_code.txt,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-11-12 01:22:43

Zamknij

Po zakończeniu używania gniazda, możesz po prostu zamknąć jego deskryptor pliku za pomocą close; jeśli nadal istnieją dane oczekujące na przesłanie przez połączenie, normalnie close próbuje zakończyć tę transmisję. Można kontrolować to zachowanie za pomocą opcji so_linger socket, aby określić okres czasu oczekiwania; zobacz Opcje Socket.

ShutDown

Możesz również wyłączyć tylko odbiór lub transmisję na połączeniu, dzwoniąc wyłączenie.

Funkcja shutdown wyłącza połączenie gniazda. Jego argument jak określa jaką akcję wykonać: 0 Przestań odbierać dane dla tego gniazda. Jeśli pojawią się dalsze dane, odrzuć je. 1 Przestań próbować przesyłać dane z tego gniazda. Odrzuć wszystkie dane oczekujące na wysłanie. Przestań szukać potwierdzenia już wysłanych danych; nie przesyłaj ich ponownie, jeśli zostaną utracone. 2 Zatrzymać zarówno odbiór, jak i transmisję.

Wartość zwracana jest równa 0 W przypadku sukcesu i -1 W przypadku niepowodzenia.

 1
Author: Neha Agrawal,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-07-21 11:57:41

W moim teście.

close wysyła pakiet fin i niszczy FD natychmiast, gdy gniazdo nie jest współdzielone z innymi procesami

shutdown SHUT_RD, proces nadal może odczytywać dane z gniazda, ale recv zwróci 0, jeśli bufor TCP jest pusty.Gdy peer wyśle więcej danych, recv zwróci dane ponownie.

shutdown SHUT_WR wyśle pakiet fin, aby wskazać, że dalsze wysyłanie nie jest dozwolone. peer może recv danych, ale będzie recv 0, jeśli jego bufor TCP jest empty

shutdown shut_rdwr (równe obu SHUT_RD i SHUT_WR) wyśle pakiet rst, jeśli peer wyśle więcej danych.

 0
Author: simpx,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-09-17 06:06:28