W C zrozumiałem, że jeśli zamkniemy gniazdo, oznacza to, że gniazdo zostanie zniszczone i może być ponownie użyte później.
Co powiesz na zamknięcie? Opis mówi, że zamyka połowę połączenia dupleksowego z tym gniazdem. Ale czy to gniazdo zostanie zniszczone jak close
wywołanie systemowe?
c
sockets
networking
tshepang
źródło
źródło
Odpowiedzi:
Jest to wyjaśnione w sieciowy Beej użytkownika.
shutdown
to elastyczny sposób blokowania komunikacji w jednym lub obu kierunkach. Gdy drugim parametrem jestSHUT_RDWR
, blokuje zarówno wysyłanie, jak i odbieranie (jakclose
). Jednakclose
jest sposób, aby rzeczywiście zniszczyć gniazdo.Dzięki
shutdown
nadal będziesz mógł odbierać oczekujące dane, które zostały już wysłane przez partnera (dziękuję Joeyowi Adamsowi, że to zauważył).źródło
shutdown
obu kierunków, ale nieclose
jest to, że użyłeśFILE
odwołania do gniazda używającegofdopen
. Jeśli maszclose
gniazdo, nowo otwarty plik może mieć przypisane to samo fd, a późniejsze użycieFILE
spowoduje odczyt / zapis niewłaściwego miejsca, co może być bardzo złe. Jeśli takshutdown
, kolejne użycieFILE
spowoduje tylko błędy, dopóki niefclose
zostanie wywołane.shutdown
: do sygnalizowania EOF peerowi i nadal być w stanie odbierać oczekujące dane wysłane przez peer.Żadna z istniejących odpowiedzi nie mówi ludziom, jak
shutdown
iclose
działa na poziomie protokołu TCP, dlatego warto to dodać.Standardowe połączenie TCP zostaje zakończone przez czterostronną finalizację:
Istnieje jednak inny „wschodzący” sposób zamknięcia połączenia TCP:
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:
shutdown
, zanimclose
, jeśli to możliweIdealne 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.
źródło
shutdown()
połączenie, a wtedy nie jest już żywe. Nadal masz deskryptor pliku. Możesz nadalrecv()
z bufora odbiorczego. I nadal musisz zadzwonić,close()
aby pozbyć się deskryptora pliku.Istnieją pewne ograniczenia
close()
, których można uniknąć, jeśli się je stosujeshutdown()
.close()
zakończy oba kierunki w połączeniu TCP. Czasami chcesz powiedzieć drugiemu punktowi końcowemu, że skończyłeś wysyłać dane, ale nadal chcesz otrzymywać dane.close()
zmniejsza liczbę odwołań deskryptorów (utrzymywanych we wpisie tabeli plików i zlicza liczbę aktualnie otwartych deskryptorów odnoszących się do pliku / gniazda) i nie zamyka gniazda / pliku, jeśli deskryptor nie ma wartości 0. Oznacza to, że jeśli rozwidlasz się, czyszczenie odbywa się tylko wtedy, gdy liczba referencji spadnie do 0. Za pomocąshutdown()
można zainicjować normalną sekwencję zamykania TCP ignorując liczbę referencji.Parametry są następujące:
int how
może być:SHUT_RD
lub0
Dalsze otrzymywania są niedozwoloneSHUT_WR
lub1
Dalsze wysyłki są niedozwoloneSHUT_RDWR
lub2
Dalsze wysyłanie i odbieranie jest niedozwoloneźródło
Może to być specyficzne dla platformy, w jakiś sposób w to wątpię, ale tak czy inaczej, najlepsze wyjaśnienie, jakie widziałem, znajduje się tutaj na tej stronie msdn, gdzie wyjaśniają one na temat zamykania, opcji linger, zamknięcia gniazda i ogólnych sekwencji zakończenia połączenia.
Podsumowując, użyj shutdown, aby wysłać sekwencję zamykania na poziomie TCP i użyj close, aby zwolnić zasoby wykorzystywane przez struktury danych gniazd w twoim procesie. Jeśli nie wydałeś wyraźnej sekwencji zamykania do czasu, gdy zadzwonisz close, zostanie ona dla Ciebie zainicjowana.
źródło
Miałem również sukces pod Linuksem, używając
shutdown()
jednego pthreada, aby zmusić inny pthread, który jest obecnie zablokowany,connect()
aby przerwać wcześniej.W innych systemach operacyjnych (przynajmniej OSX) stwierdziłem, że wywołanie
close()
wystarczyło, abyconnect()
zakończyć się niepowodzeniem.źródło
„shutdown () tak naprawdę nie zamyka deskryptora pliku - po prostu zmienia jego użyteczność. Aby zwolnić deskryptor gniazda, musisz użyć close ().” 1
źródło
Blisko
Po zakończeniu korzystania z gniazda możesz po prostu zamknąć jego deskryptor pliku za pomocą polecenia close; Jeśli nadal istnieją dane oczekujące na przesłanie przez połączenie, zwykle zamknięcie próbuje zakończyć tę transmisję. Możesz kontrolować to zachowanie za pomocą opcji gniazda SO_LINGER, aby określić limit czasu; patrz Opcje gniazd.
Zamknąć
Możesz także wyłączyć tylko odbiór lub transmisję połączenia, wywołując zamknięcie.
Funkcja zamykania wyłącza połączenie gniazda. Jego argument określa, jakie działanie wykonać: 0 Zatrzymaj odbieranie danych 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 danych już wysłanych; nie przesyłaj go ponownie, jeśli zostanie zgubiony. 2 Zatrzymaj odbiór i transmisję.
Zwracana wartość to 0 w przypadku sukcesu i -1 w przypadku niepowodzenia.
źródło
w moim teście.
close
wyśle pakiet fin i natychmiast zniszczy fd, gdy gniazdo nie zostanie udostępnione innym procesomshutdown
SHUT_RD , proces może nadal odzyskiwać dane z gniazda, alerecv
zwróci 0, jeśli bufor TCP jest pusty. Po przesłaniu przez peer więcej danych,recv
ponownie zwróci dane.shutdown
SHUT_WR wyśle pakiet fin, aby wskazać, że dalsze wysyłanie jest niedozwolone. peer może odzyskać dane, ale odzyska 0, jeśli jego bufor TCP będzie pustyshutdown
SHUT_RDWR (równy użyciu SHUT_RD i SHUT_WR ) wyśle pierwszy pakiet, jeśli peer wyśle więcej danych.źródło
close()
wysłałem RST na Linuksie zamiast FIN.recv()
zwróci dane” jest niepoprawne. 2. Zachowanie, jeśli peer wysyła później więcej danych,SHUT_RD
zależy od platformy.