socket.shutdown vs socket.close

122

Niedawno widziałem fragment kodu, który wyglądał tak (oczywiście z sockiem będącym obiektem gniazda):

sock.shutdown(socket.SHUT_RDWR)
sock.close()

Jaki dokładnie jest cel wywołania shutdown w gnieździe, a następnie jego zamknięcia? Jeśli to robi różnicę, to gniazdo jest używane do nieblokującego wejścia / wyjścia.

Jason Baker
źródło

Odpowiedzi:

38

Oto jedno wyjaśnienie :

Gdy gniazdo nie jest już potrzebne, program wywołujący może odrzucić gniazdo, stosując procedurę close do deskryptora gniazda. Jeśli niezawodne gniazdo dostarczania ma dane powiązane z nim w momencie zamknięcia, system kontynuuje próbę przesłania danych. Jeśli jednak dane nadal są niedostarczone, system je odrzuca. Jeśli aplikacja nie będzie potrzebować żadnych oczekujących danych, może użyć procedury zamykania w gnieździe przed jego zamknięciem.

Bob Nadler
źródło
241

Wywołanie closei shutdownma dwa różne efekty na podstawowym gnieździe.

Pierwszą rzeczą, na którą należy zwrócić uwagę, jest to, że gniazdo jest zasobem w bazowym systemie operacyjnym i wiele procesów może mieć uchwyt dla tego samego podstawowego gniazda.

Gdy closego wywołasz , zmniejsza liczbę uchwytów o jeden i jeśli liczba uchwytów osiągnęła zero, gniazdo i skojarzone z nim połączenie przechodzą przez normalną procedurę zamykania (skutecznie wysyłając FIN / EOF do peera), a gniazdo jest zwalniane.

Należy tutaj zwrócić uwagę na to, że jeśli liczba uchwytów nie osiąga zera, ponieważ inny proces nadal ma uchwyt do gniazda, połączenie nie jest zamykane, a gniazdo nie jest zwalniane.

Z drugiej strony wywołanie shutdownodczytu i zapisu zamyka podstawowe połączenie i wysyła FIN / EOF do peera, niezależnie od tego, ile procesów ma uchwyty do gniazda. Jednak nie zwalnia gniazda i nadal trzeba wywołać polecenie close.

Robert S. Barnes
źródło
świetna odpowiedź, nigdy nie zadałem sobie trudu, aby dowiedzieć się, co shutdown()robi :)
Matt Joiner,
2
Czy funkcja shutdown () do odczytu może spowodować wysłanie pakietu FIN? czyli zamknięcie (sock_fd, 1);
ernesto
Czy mądrze byłoby zawsze dzwonić .shutdown()i dzwonić do następnej linii .close()? A może pomiędzy nimi powinno być opóźnienie?
Luc
@Luc Całkowicie zależy od tego, co robisz.
Robert S. Barnes
2
@Luc Wtedy po prostu zamknij jest w porządku, o ile żadne inne procesy nie mają uchwytu do gniazda.
Robert S. Barnes
17

Wyjaśnienie zamykania i zamykania: bezpieczne zamykanie (msdn)

Shutdown (w twoim przypadku) wskazuje na drugi koniec połączenia, że ​​nie ma dalszej intencji odczytu lub zapisu do gniazda. Następnie zamknięcie zwalnia pamięć związaną z gniazdem.

Pominięcie wyłączenia może spowodować, że gniazdo pozostanie w stosie systemów operacyjnych, dopóki połączenie nie zostanie prawidłowo zamknięte.

IMO nazwy „zamknij” i „zamknij” są mylące, a „zamknij” i „zniszcz” podkreślałyby ich różnice.

Dale Reidy
źródło
Z drugiej strony nie wskazuje, że nie ma dalszej intencji czytania. Zamknięcie do odczytu nie wysyła niczego do peera.
Markiz Lorne
7

jest to wspomniane bezpośrednio w Socket Programming HOWTO ( py2 / py3 )

Rozłączanie

Ściśle mówiąc, powinieneś użyć shutdownna gnieździe przed closenim. shutdownJest doradczy do gniazda na drugim końcu. W zależności od argumentu, który go zdasz, może to oznaczać „ Nie zamierzam już wysyłać, ale nadal będę słuchać ” lub „ Nie słucham, dobra zabawa! ”. Jednak większość bibliotek gniazd jest tak przyzwyczajeni do programistów, którzy zaniedbują stosowanie tej etykiety, zgodnie z którą normalnie a closejest tym samym coshutdown(); close() . Dlatego w większości sytuacji jawne wyłączenie nie jest potrzebne.

...

mykhal
źródło
Ta informacja jest nieprawidłowa. Zamknięcie gniazda do zapisu jest zawsze konieczne tylko wtedy, gdy (1) rozwidliłeś proces i na pewno chcesz teraz wysłać FIN , lub (2) jesteś zaangażowany we wzajemny protokół odczytu do EOS, tak że oba peery są zamknięte w tym samym czasie. W przeciwnym razie close()wystarczy. Dokumentacja Pythona powinna zostać poprawiona.
Markiz Lorne
4

Czy ten kod nie jest zły?

Wywołanie close bezpośrednio po wywołaniu shutdown może spowodować, że jądro i tak porzuci wszystkie bufory wychodzące.

Według http://blog.netherlabs.nl/articles/2009/01/18/the-ultimate-so_linger-page-or-why-is-my-tcp-not-reliable należy odczekać od wyłączenia do zamknij, dopóki odczyt nie zwróci 0.

chrześcijanin
źródło
Niepoprawne. Jądro odrzuci bufory wychodzące tylko wtedy, gdy połączenie zostało zresetowane, co może się zdarzyć, jeśli lokalna aplikacja nie odczytała wszystkich oczekujących danych przychodzących, które już dotarły, lub jeśli peer pomieszał z SO_LINGER, czego nie powinien robić . Nie ma też konieczności spania, ani nawet wzywania wyłączenia przed zamknięciem. Istnieje wiele błędnych informacji na ten temat.
Markiz Lorne
1

Shutdown (1), wymusza na gnieździe nie wysyłanie dalszych danych

Jest to przydatne

1- Płukanie bufora

2- Dziwne wykrywanie błędów

3- Bezpieczna ochrona

Pozwól, że wyjaśnię więcej, kiedy wysyłasz dane z A do B, nie ma gwarancji, że zostanie wysłany do B, gwarantuje się tylko wysłanie do bufora A os, który z kolei przesyła je do bufora B os

Więc wywołując shutdown (1) na A, opróżniasz bufor A i zgłaszany jest błąd, jeśli bufor nie jest pusty, tj .: dane nie zostały jeszcze wysłane do peera

Jednak jest to nieodwracalne, więc możesz to zrobić po całkowitym wysłaniu wszystkich danych i chcesz mieć pewność, że są one przynajmniej w buforze peer OS

user306166
źródło
Zamknięcie nie wymusza spłukiwania. Sytuacja buforowania pozostaje niezmieniona. A jeśli bufor nie jest pusty, nie jest zgłaszany błąd. FIN jest umieszczany w kolejce za oczekującymi danymi. Odpowiedź jest całkowicie błędna.
Markiz Lorne