2012-01-15 22 views
23

Czy jest to bezpieczne/bezpieczne dla close() gniazdo bezpośrednio po ostatnim send()?zamknij() gniazdo bezpośrednio po wysłaniu(): niebezpieczne?

Wiem, że TCP powinien spróbować dostarczyć wszystkie pozostałe dane w buforze wysyłania, nawet po zamknięciu gniazda, ale czy naprawdę mogę na to liczyć?

Upewniam się, że nie ma żadnych pozostałych danych w buforze odbiorczym, aby po moim zamknięciu nie zostały wysłane żadne RST.


W moim przypadku, blisko jest faktycznie bardzo ostatnie stwierdzenie kodu przed wywołaniem exit().

Czy stos TCP naprawdę będzie próbował przesłać dane nawet po zakończeniu procesu wysyłania? Czy to jest tak niezawodne, jak czekanie na samowolny limit czasu przed wywołaniem close() poprzez ustawienie SO_LINGER?

Czy stosuje się te same limity czasu TCP, czy są one krótsze? Przy dużym buforze wysyłania i powolnym połączeniu czas na przeniesienie wszystkich buforowanych danych może być w końcu znaczny.


Nie jestem w ogóle zainteresowany otrzymywaniem powiadomienia o wysłaniu ostatniego bajtu; Po prostu chcę, żeby w końcu dotarli do zdalnego hosta tak niezawodnie, jak to tylko możliwe.

Potwierdzenia warstwy aplikacji nie są opcją (protokół to HTTP, a ja piszę mały serwer).

+0

To zależy od implementacji, ale ogólnie rzecz biorąc, tak, 'close()' wypróżni wszelkie dane pozostające w buforze, zanim faktycznie rozerwie gniazdo. Przebieg może się różnić, jeśli zakończysz cały proces oprócz wywołania 'close()'. – aroth

+2

@aroth Mileage * nie * będzie się różnić, jeśli zakończysz proces. Nie ma różnicy między tymi dwoma przypadkami. Jeśli chcesz zachować przeciwwagę, podaj autorytatywne odniesienie. – EJP

+0

@EJP - autorytatywne odniesienie do tego, co * może * zdarzyć się w arbitralnych programach? Nie sądzę, że coś takiego istnieje. Ale jest to dość proste, aby przyjąć prawdopodobny przykład. Załóżmy, że masz program, który deleguje wszystkie operacje sieciowe do osobnego wątku, jak większość programów. Istnieje możliwość, że główny wątek zakończy proces (z wdziękiem lub w inny sposób), podczas gdy wątek tła wywołuje funkcję 'close()'. Czy połączenie zostanie wykonane w takim przypadku? Wątpię. To prawda, że ​​to, co powinieneś powiedzieć, powinno być prawdziwe w przypadku programów jednowątkowych. Ale nie wszystkie programy są jednowątkowe. – aroth

Odpowiedz

20

Bardzo często czytam numer ultimate SO_LINGER page. Polecam też to przeczytać. Omówiono przypadki krańcowe dużych transferów danych w odniesieniu do gniazd TCP.

Nie jestem ekspertem w SO_LINGER, ale na moim kodu serwera (nadal w aktywnego rozwoju) I wykonaj następujące czynności:

  1. Po ostatni bajt jest wysyłany za pośrednictwem send(), ja nazywam " shutdown (skarpeta, SHUT_WR) ", aby wyzwolić FIN, który ma zostać wysłany.

  2. Następnie poczekaj na późniejsze wywołanie recv() na tym gnieździe, aby powrócić 0 (lub recv zwraca -1, a errno jest czymś innym niż EAGAIN/EWOULDBLOCK).

  3. Następnie serwer wykonuje zamknięcie() na gnieździe.

Założeniem jest, że klient zamknie swoje gniazdo najpierw po otrzymaniu wszystkich bajtów odpowiedzi.

Ale mam timeout wymuszony między ostatecznym send() i kiedy recv() wskazuje EOF. Jeśli klient nigdy nie zakończy połączenia, serwer zrezygnuje z oczekiwania i zamknie połączenie. Mam 45-90 sekund na ten czas.

Wszystkie moje gniazda nie są blokujące i używam poll/epoll do powiadomienia o zdarzeniach związanych z połączeniem jako podpowiedzi, aby sprawdzić, czy nadszedł czas, aby spróbować ponownie wywołać recv() lub send().

+2

To wydaje się bardzo dobrym rozwiązaniem, o ile klient najpierw zamknie gniazdo. Czy jednak nie mógł napisać - zamknąć swoją połowę połączenia natychmiast po wysłaniu prośby? Specyfikacja HTTP nie mówi nic na temat półzwrotów; Czy uważasz, że to zachowanie występuje kiedykolwiek w klientach HTTP? – lxgr

+0

Nie. Zrobiłem kilka ad-hoc wireshark sniffs do sprawdzenia poprawności. Zachęcam was do zrobienia tego samego. Z mojego testu sniffowania pakietów - czasami serwer wysyła FIN po odpowiedzi i klient wysyła FIN po otrzymaniu odpowiedzi. Ale większość klientów HTTP określa opcję "keep-alive" na serwerze, więc klienci i tak nie zainicjują zamknięcia. Innymi słowy, uważam, że bardzo bezpiecznie jest założyć, że klienci nie zrobią pół-zamknięcia. – selbie

+0

Ponadto klienci zwracają uwagę na nagłówek Content-Length. Może być prawdopodobne, że serwer nie będzie musiał zamknąć ani zamknąć po wysłaniu wszystkich swoich bajtów, ponieważ klient wie, ile bajtów można oczekiwać w odpowiedzi HTTP. (A serwer na pewno nie zamknie się, jeśli będzie utrzymywał połączenie podtrzymujące połączenie i jeśli zażądał tego klient). Ale w prostszych żądaniach bez podtrzymywania, serwer wysyła FIN po wysłaniu odpowiedzi. – selbie

Powiązane problemy