Dlaczego bez próby wejścia / wyjścia niemożliwe jest wykrycie, że gniazdo TCP zostało bezpiecznie zamknięte przez partnera?


93

W odpowiedzi na niedawne pytanie zastanawiam się, dlaczego w Javie nie jest możliwe wykrycie, że gniazdo zostało bezpiecznie zamknięte przez równorzędnego, bez próby odczytu / zapisu w gnieździe TCP? Wydaje się, że tak jest niezależnie od tego, czy używa się pre-NIO, Socketczy NIO SocketChannel.

Kiedy peer z wdziękiem zamyka połączenie TCP, stos TCP po obu stronach połączenia wie o tym fakcie. Strona serwera (ta, która inicjuje zamknięcie) kończy się w stanie FIN_WAIT2, podczas gdy strona klienta (ta, która nie reaguje jawnie na zamknięcie) kończy się w stanie CLOSE_WAIT. Dlaczego nie jest to metoda w Socketlub SocketChannelże może zapytać stos TCP, aby zobaczyć, czy połączenie TCP zostało zakończone? Czy to dlatego, że stos TCP nie dostarcza takich informacji o stanie? A może jest to decyzja projektowa, aby uniknąć kosztownego wywołania jądra?

Z pomocą użytkowników, którzy już opublikowali kilka odpowiedzi na to pytanie, myślę, że wiem, skąd może pochodzić problem. Strona, która nie zamyka jawnie połączenia, przechodzi w stan TCP, CLOSE_WAITco oznacza, że ​​połączenie jest w trakcie zamykania i czeka, aż strona wykona własną CLOSEoperację. Przypuszczam, że to sprawiedliwe, że isConnectedwraca truei isClosedwraca false, ale dlaczego nie ma czegoś takiego isClosing?

Poniżej znajdują się klasy testowe, które używają gniazd pre-NIO. Ale identyczne wyniki uzyskuje się przy użyciu NIO.

import java.net.ServerSocket;
import java.net.Socket;

public class MyServer {
  public static void main(String[] args) throws Exception {
    final ServerSocket ss = new ServerSocket(12345);
    final Socket cs = ss.accept();
    System.out.println("Accepted connection");
    Thread.sleep(5000);
    cs.close();
    System.out.println("Closed connection");
    ss.close();
    Thread.sleep(100000);
  }
}


import java.net.Socket;

public class MyClient {
  public static void main(String[] args) throws Exception {
    final Socket s = new Socket("localhost", 12345);
    for (int i = 0; i < 10; i++) {
      System.out.println("connected: " + s.isConnected() + 
        ", closed: " + s.isClosed());
      Thread.sleep(1000);
    }
    Thread.sleep(100000);
  }
}

Gdy klient testowy łączy się z serwerem testowym, dane wyjściowe pozostają niezmienione nawet po zainicjowaniu przez serwer zamykania połączenia:

connected: true, closed: false
connected: true, closed: false
...

Pomyślałem, że wspomnę: Protokół SCTP nie ma tego „problemu”. SCTP nie ma stanu półzamkniętego jak TCP, innymi słowy, jedna strona nie może kontynuować wysyłania danych, podczas gdy druga strona zamknęła swoje gniazdo wysyłające. To powinno ułatwić sprawę.
Tarnay Kálmán

2
Mamy dwie skrzynki pocztowe (gniazda) ........................................... ...... Skrzynki pocztowe wysyłają pocztę do siebie za pomocą RoyalMail (IP) zapomnij o TCP ............................. .................... Wszystko jest w porządku i eleganckie, skrzynki pocztowe mogą wysyłać / odbierać pocztę do siebie nawzajem (ostatnio dużo opóźnień) wysyłając bez problemu. ............. Gdyby jedna skrzynka pocztowa została potrącona przez ciężarówkę i zawiodła… skąd druga skrzynka miałaby wiedzieć? Musiałoby to zostać powiadomione przez Royal Mail, która z kolei nie wiedziałaby, dopóki nie spróbuje wysłać / odebrać poczty z tej nieudanej skrzynki pocztowej .. ...... erm .....
divinci

Jeśli nie zamierzasz czytać z gniazda ani pisać do gniazda, dlaczego Cię to obchodzi? A jeśli masz zamiar czytać z gniazda lub pisać do gniazda, po co dodatkowo sprawdzać? Jaki jest przypadek użycia?
David Schwartz,

2
Socket.closenie jest wdzięcznym zamknięciem.
user253751

@immibis Z całą pewnością jest to wdzięczne zamknięcie, chyba że w buforze odbiorczym gniazda znajdują się nieprzeczytane dane lub zadziałałeś z SO_LINGER.
Markiz Lorne

Odpowiedzi:


29

Często korzystałem z gniazd, głównie z selektorami, i chociaż nie jestem ekspertem od Network OSI, z mojego rozumienia, wywołanie shutdownOutput()Socket w rzeczywistości wysyła coś w sieci (FIN), co budzi mój Selector po drugiej stronie (to samo zachowanie w C język). Tutaj masz wykrywanie : faktycznie wykrywanie operacji odczytu, która zakończy się niepowodzeniem, gdy spróbujesz.

W podanym przez Ciebie kodzie zamknięcie gniazda spowoduje zamknięcie zarówno strumienia wejściowego, jak i wyjściowego, bez możliwości odczytu danych, które mogą być dostępne, a tym samym ich utrata. Metoda Java Socket.close()wykonuje „wdzięczne” rozłączenie (odwrotnie niż początkowo sądziłem) w ten sposób, że dane pozostawione w strumieniu wyjściowym zostaną wysłane, a następnie FIN, aby zasygnalizować jego zamknięcie. Druga strona potwierdzi FIN, tak jak każdy zwykły pakiet 1 .

Jeśli musisz poczekać, aż druga strona zamknie gniazdo, musisz poczekać na jego FIN. I aby to osiągnąć, to trzeba wykryć Socket.getInputStream().read() < 0, co oznacza, że powinna nie zamknąć gniazdo, gdyż zamknięcie jejInputStream .

Z tego, co zrobiłem w C, a teraz w Javie, osiągnięcie takiego zsynchronizowanego zamknięcia powinno odbywać się w następujący sposób:

  1. Wyjście gniazda zamykającego (wysyła FIN na drugim końcu, jest to ostatnia rzecz, jaka kiedykolwiek zostanie wysłana przez to gniazdo). Wejście jest nadal otwarte, więc możesz read()i wykryć pilotaclose()
  2. Czytaj gniazdo, InputStreamdopóki nie otrzymamy odpowiedzi-FIN z drugiego końca (ponieważ wykryje FIN, przejdzie przez ten sam wdzięczny proces odłączania). Jest to ważne w niektórych systemach operacyjnych, ponieważ w rzeczywistości nie zamykają one gniazda, o ile jeden z jego buforów nadal zawiera dane. Nazywa się je gniazdem „duchowym” i zużywa numery deskryptorów w systemie operacyjnym (może to już nie stanowić problemu w przypadku współczesnego systemu operacyjnego)
  3. Zamknij gniazdo (przez wywołanie Socket.close()lub zamknięcie jego InputStreamlub OutputStream)

Jak pokazano na poniższym fragmencie kodu Java:

public void synchronizedClose(Socket sok) {
    InputStream is = sok.getInputStream();
    sok.shutdownOutput(); // Sends the 'FIN' on the network
    while (is.read() > 0) ; // "read()" returns '-1' when the 'FIN' is reached
    sok.close(); // or is.close(); Now we can close the Socket
}

Oczywiście obie strony muszą stosować ten sam sposób zamykania lub część wysyłająca może zawsze wysyłać wystarczającą ilość danych, aby whilepętla była zajęta (np. Jeśli część wysyłająca wysyła tylko dane i nigdy nie czyta w celu wykrycia zakończenia połączenia. ale możesz nie mieć nad tym kontroli).

Jak zauważył @WarrenDew w swoim komentarzu, odrzucenie danych w programie (warstwie aplikacji) wywołuje niezręczne rozłączenie w warstwie aplikacji: chociaż wszystkie dane zostały odebrane w warstwie TCP ( whilepętla), są one odrzucane.

1 : Z „ Podstawowej sieci w Javie ”: patrz rys. 3,3 s. 45 i cały § 3.7, s. 43-48


Java wykonuje zgrabne zamknięcie. To nie jest „brutalne”.
Markiz Lorne,

2
@EJP, „wdzięczne rozłączenie” to specyficzna wymiana odbywająca się na poziomie TCP, gdzie Klient powinien zasygnalizować swoją wolę rozłączenia się z serwerem, który z kolei wysyła pozostałe dane przed zamknięciem swojej strony. Część „wyślij pozostałe dane” musi być obsłużona przez program (chociaż ludzie przez większość czasu nie wysyłaliby niczego). Wywołanie socket.close()jest „brutalne”, ponieważ nie uwzględnia sygnalizacji klienta / serwera. Serwer byłby powiadamiany o rozłączeniu klienta tylko wtedy, gdy jego własny bufor wyjściowy gniazda jest pełny (ponieważ żadne dane nie są potwierdzane przez drugą stronę, która została zamknięta).
Matthieu,

Aby uzyskać więcej informacji, zobacz MSDN .
Matthieu,

1
@Matthieu Jeśli Twoja aplikacja nie odczytuje wszystkich dostępnych danych, może to być nieczytelne w warstwie aplikacji, ale w warstwie transportowej TCP, dane są nadal odbierane, a połączenie z wdziękiem kończone. To samo byłoby prawdą, gdyby aplikacja odczytywała wszystkie dane ze strumienia wejściowego i po prostu je odrzucała.
Warren Dew

2
@LeonidUsov To jest po prostu niepoprawne. Java read()zwraca -1 na końcu strumienia i nadal to robi, niezależnie od tego, ile razy to wywołasz. AC read()lub recv()zwraca zero na końcu strumienia i nadal to robi, niezależnie od tego, ile razy to wywołasz.
Markiz Lorne

18

Myślę, że jest to bardziej kwestia programowania gniazd. Java po prostu podąża za tradycją programowania w gniazdach.

Z Wikipedii :

Protokół TCP zapewnia niezawodne, uporządkowane dostarczanie strumienia bajtów z jednego programu na jednym komputerze do innego programu na innym komputerze.

Po zakończeniu uzgadniania TCP nie rozróżnia dwóch punktów końcowych (klienta i serwera). Określenia „klient” i „serwer” są używane głównie dla wygody. Zatem „serwer” może wysyłać dane, a „klient” może wysyłać do siebie inne dane jednocześnie.

Termin „zamknij” również wprowadza w błąd. Jest tylko deklaracja FIN, co oznacza: „Nie zamierzam wysyłać Ci więcej rzeczy”. Ale to nie znaczy, że nie ma żadnych pakietów w locie lub drugi nie ma nic więcej do powiedzenia. Jeśli zaimplementujesz pocztę ślimakową jako warstwę łącza danych lub jeśli Twój pakiet podróżował różnymi trasami, możliwe jest, że odbiorca odbierze pakiety w złej kolejności. TCP wie, jak to naprawić.

Również Ty, jako program, możesz nie mieć czasu na sprawdzanie zawartości bufora. Tak więc w dogodnym dla Ciebie czasie możesz sprawdzić, co jest w buforze. Podsumowując, obecna implementacja gniazd nie jest taka zła. Jeśli faktycznie istniała isPeerClosed (), to jest to dodatkowe wywołanie, które musisz wykonać za każdym razem, gdy chcesz wywołać read.


1
Nie sądzę, możesz testować stany w kodzie C zarówno w systemie Windows, jak i Linux !!! Java z jakiegoś powodu może nie ujawniać niektórych rzeczy, na przykład fajnie byłoby ujawnić funkcje getsockopt, które są w systemie Windows i Linux. W rzeczywistości, poniższe odpowiedzi zawierają kod w języku C po stronie linuxa.
Dean Hiller

Nie sądzę, aby posiadanie metody „isPeerClosed ()” w jakiś sposób zobowiązuje Cię do wywołania jej przed każdą próbą odczytu. Możesz to po prostu nazwać tylko wtedy, gdy wyraźnie tego potrzebujesz. Zgadzam się, że obecna implementacja gniazda nie jest taka zła, mimo że wymaga zapisywania do strumienia wyjściowego, jeśli chcesz dowiedzieć się, czy zdalna część gniazda jest zamknięta, czy nie. Bo jeśli nie, to z zapisanymi danymi trzeba sobie poradzić po drugiej stronie, a to po prostu tyle przyjemności, co siedzenie na pionowo ustawionym gwoździu;)
Jan Hruby

1
To nie oznacza „nie ma więcej pakietów w locie”. FIN jest odbierany po wszelkich danych w locie. Jednak nie oznacza to, że peer zamknął gniazdo wejściowe. Aby to wykryć, musisz ** coś wysłać * i zresetować połączenie. FIN mógł po prostu oznaczać wyłączenie wyjścia.
Markiz Lorne

11

Podstawowy interfejs API gniazd nie ma takiego powiadomienia.

Wysyłający stos TCP i tak nie wyśle ​​bitu FIN aż do ostatniego pakietu, więc może być wiele danych buforowanych od momentu, gdy aplikacja wysyłająca logicznie zamknęła swoje gniazdo, zanim te dane zostały wysłane. Podobnie dane, które są buforowane, ponieważ sieć jest szybsza niż aplikacja odbierająca (nie wiem, może przekazujesz je przez wolniejsze połączenie) mogą mieć znaczenie dla odbiorcy i nie chcesz, aby aplikacja odbierająca je odrzuciła tylko dlatego, że bit FIN został odebrany przez stos.


W moim przykładzie testowym (może powinienem podać tutaj ...) celowo nie ma danych wysyłanych / odbieranych przez połączenie. Tak więc jestem prawie pewien, że stos otrzymuje FIN (wdzięczny) lub RST (w niektórych nie-wdzięcznych scenariuszach). Potwierdza to również netstat.
Alexander,

1
Jasne - jeśli nic nie jest zbuforowane, to FIN zostanie wysłany natychmiast, w innym przypadku pustym pakiecie (bez ładunku). Jednak po FIN, żadne pakiety danych nie są wysyłane do tego końca połączenia (nadal potwierdzi wszystko, co zostało do niego wysłane).
Mike Dimmick

1
Dzieje się tak, że strony połączenia kończą się w <code> CLOSE_WAIT </code> i <code> FIN_WAIT_2 </code> iw tym stanie <code> isConcected () </code> i <code> isClosed () </code> nadal nie widzi, że połączenie zostało zakończone.
Alexander,

Dzięki za sugestie! Myślę, że teraz lepiej rozumiem ten problem. Doprecyzowałem pytanie (zobacz trzeci akapit): dlaczego nie ma „Socket.isClosing” do testowania połączeń w połowie zamkniętych?
Alexander,

8

Ponieważ żadna z dotychczasowych odpowiedzi w pełni nie odpowiada na pytanie, podsumowuję moje obecne rozumienie problemu.

Po ustanowieniu połączenia TCP i wywołaniu jednego z równorzędnych close()lub shutdownOutput()w jego gnieździe gniazdo po drugiej stronie połączenia przechodzi w CLOSE_WAITstan. Zasadniczo można dowiedzieć się ze stosu TCP, czy gniazdo jest w CLOSE_WAITstanie bez wywoływania read/recv(np. W getsockopt()systemie Linux: http://www.developerweb.net/forum/showthread.php?t=4395 ), ale tak nie jest przenośny.

SocketWydaje się, że klasa Javy ma na celu zapewnienie abstrakcji porównywalnej z gniazdem BSD TCP, prawdopodobnie dlatego, że jest to poziom abstrakcji, do którego ludzie są przyzwyczajeni podczas programowania aplikacji TCP / IP. Gniazda BSD to uogólnienie obsługujące gniazda inne niż tylko INET (np. TCP), więc nie zapewniają przenośnego sposobu sprawdzania stanu TCP gniazda.

Nie ma takiej metody, isCloseWait()ponieważ ludzie przyzwyczajeni do programowania aplikacji TCP na poziomie abstrakcji oferowanej przez gniazda BSD nie oczekują, że Java zapewni dodatkowe metody.


3
Java nie może również zapewnić żadnych dodatkowych metod, przenośnie. Może mogliby stworzyć metodę isCloseWait (), która zwróciłaby wartość false, gdyby platforma jej nie obsługiwała, ale ilu programistów zostałoby ugryzionych przez to rozwiązanie, gdyby testowali tylko na obsługiwanych platformach?
ephemient

1
wygląda na to, że może być przenośny dla mnie ... Windows ma to msdn.microsoft.com/en-us/library/windows/desktop/ ... a linux to pubs.opengroup.org/onlinepubs/009695399/functions/ ...
Dean Hiller

1
Nie chodzi o to, że programiści są do tego przyzwyczajeni; chodzi o to, że interfejs gniazda jest przydatny dla programistów. Należy pamiętać, że abstrakcja gniazda jest używana nie tylko do protokołu TCP.
Warren Dew

W Javie nie ma takiej metody, isCloseWait()ponieważ nie jest obsługiwana na wszystkich platformach.
Markiz Lorne

Protokół ident (RFC 1413) umożliwia serwerowi utrzymanie połączenia otwartego po wysłaniu odpowiedzi lub zamknięcie go bez wysyłania dalszych danych. Klient ident w Javie może zdecydować się na pozostawienie otwartego połączenia, aby uniknąć potrójnego uzgadniania przy następnym wyszukiwaniu, ale skąd wie, że połączenie jest nadal otwarte? Czy powinien po prostu spróbować, zareagować na jakikolwiek błąd, ponownie otwierając połączenie? A może to błąd w projekcie protokołu?
david

7

Wykrywanie, czy zdalna strona połączenia gniazda (TCP) zostało zamknięte, można wykonać za pomocą metody java.net.Socket.sendUrgentData (int) i przechwycić IOException, który zgłasza, jeśli strona zdalna nie działa. Zostało to przetestowane między Java-Java i Java-C.

Pozwala to uniknąć problemu projektowania protokołu komunikacyjnego tak, aby używał pewnego rodzaju mechanizmu pingowania. Wyłączając OOBInline w gnieździe (setOOBInline (false), wszystkie odebrane dane OOB są dyskretnie odrzucane, ale dane OOB mogą być nadal wysyłane. Jeśli strona zdalna jest zamknięta, próba zresetowania połączenia kończy się niepowodzeniem i powoduje wyrzucenie wyjątku IOException .

Jeśli faktycznie używasz danych OOB w swoim protokole, Twój przebieg może się różnić.


4

stos Java IO zdecydowanie wysyła FIN, gdy zostanie zniszczony podczas nagłego rozerwania. Po prostu nie ma sensu, że nie możesz tego wykryć, b / c większość klientów wysyła FIN tylko wtedy, gdy zamykają połączenie.

... kolejny powód, dla którego naprawdę zaczynam nienawidzić klas NIO Java. Wygląda na to, że wszystko jest trochę pół dupkiem.


1
wydaje się również, że otrzymuję tylko koniec strumienia podczas odczytu (powrót -1), gdy obecny jest FIN. Jest to więc jedyny sposób, w jaki widzę, aby wykryć wyłączenie po stronie odczytu.

3
Możesz to wykryć. Otrzymujesz EOS podczas czytania. A Java nie wysyła FIN. TCP to robi. Java nie implementuje protokołu TCP / IP, po prostu korzysta z implementacji platformy.
Markiz Lorne

4

To ciekawy temat. Przed chwilą przekopałem kod Java, aby to sprawdzić. Z moich ustaleń wynika, że ​​istnieją dwa różne problemy: pierwszy to sam protokół TCP RFC, który umożliwia zdalnie zamkniętemu gniazdu przesyłanie danych w trybie półdupleksu, więc zdalnie zamknięte gniazdo jest nadal w połowie otwarte. Zgodnie z RFC, RST nie zamyka połączenia, musisz wysłać jawną komendę ABORT; więc Java pozwala na przesyłanie danych przez pół zamknięte gniazdo

(Istnieją dwie metody odczytywania stanu zamknięcia w obu punktach końcowych).

Innym problemem jest to, że implementacja mówi, że to zachowanie jest opcjonalne. Ponieważ Java stara się być przenośna, zaimplementowała najlepszą wspólną funkcję. Wydaje mi się, że utrzymanie mapy (system operacyjny, implementacja półdupleksu) byłoby problemem.


1
Przypuszczam, że mówisz o RFC 793 ( faqs.org/rfcs/rfc793.html ) Sekcja 3.5 Zamykanie połączenia. Nie jestem pewien, czy wyjaśnia to problem, ponieważ obie strony kończą wdzięczne zamknięcie połączenia i kończą w stanach, w których nie powinny wysyłać / odbierać żadnych danych.
Alexander,

Zależy. ile FIN widzisz na gnieździe? Może również występować problem specyficzny dla platformy: może system Windows odpowiada każdemu FIN za pomocą FIN i połączenie jest zamknięte na obu końcach, ale inne systemy operacyjne mogą nie zachowywać się w ten sposób i tutaj pojawia się problem 2
Lorenzo Boccaccia

1
Nie, niestety tak nie jest. isOutputShutdown i isInputShutdown to pierwsza rzecz, której każdy próbuje w obliczu tego „odkrycia”, ale obie metody zwracają fałsz. Właśnie przetestowałem to na Windows XP i Linux 2.6. Zwracane wartości wszystkich 4 metod pozostają takie same nawet po próbie odczytu
Alexander,

1
Dla przypomnienia, nie jest to półdupleks. Półdupleks oznacza, że ​​tylko jedna strona może wysyłać w czasie s; obie strony mogą nadal wysyłać.
rbp

1
isInputShutdown i isOutputShutdown testują lokalny koniec połączenia - są to testy mające na celu sprawdzenie, czy wywołałeś shutdownInput lub shutdownOutput na tym Socket. Nie mówią nic o zdalnym końcu połączenia.
Mike Dimmick

3

Jest to wada Javy (i wszystkich innych, które widziałem) klas gniazd OO - brak dostępu do wybranego wywołania systemowego.

Prawidłowa odpowiedź w C:

struct timeval tp;  
fd_set in;  
fd_set out;  
fd_set err;  

FD_ZERO (in);  
FD_ZERO (out);  
FD_ZERO (err);  

FD_SET(socket_handle, err);  

tp.tv_sec = 0; /* or however long you want to wait */  
tp.tv_usec = 0;  
select(socket_handle + 1, in, out, err, &tp);  

if (FD_ISSET(socket_handle, err) {  
   /* handle closed socket */  
}  

Możesz zrobić to samo z getsocketop(... SOL_SOCKET, SO_ERROR, ...).
David Schwartz

1
Zestaw deskryptorów pliku błędu nie będzie wskazywał na zamknięcie połączenia. Przeczytaj wybraną instrukcję: 'exceptfds - ten zestaw jest obserwowany pod kątem "wyjątkowych warunków". W praktyce tylko jeden taki warunek wyjątkowy jest powszechny: dostępność danych pozapasmowych (OOB) do odczytu z gniazda TCP ”. FIN to nie dane OOB.
Jan Wróbel

1
Możesz użyć klasy „Selector”, aby uzyskać dostęp do wywołania systemowego „select ()”. Używa jednak NIO.
Matthieu

1
Nie ma nic wyjątkowego w połączeniu, które zostało zamknięte przez drugą stronę.
David Schwartz,

1
Wiele platform, w tym Java, zrobić zapewniają dostęp do select () wywołania systemowego.
Markiz Lorne

2

Oto kiepskie obejście. Użyj SSL;), a SSL wykonuje ścisłe uzgadnianie po rozerwaniu, więc jesteś powiadamiany o zamknięciu gniazda (większość implementacji wydaje się robić właściwe porzucenie uzgadniania).


2
W jaki sposób można otrzymać „powiadomienie” o zamknięciu gniazda podczas korzystania z protokołu SSL w Javie?
Dave B,

2

Przyczyną tego zachowania (które nie jest specyficzne dla języka Java) jest fakt, że nie otrzymujesz żadnych informacji o statusie ze stosu TCP. W końcu gniazdo jest po prostu kolejnym uchwytem pliku i nie możesz dowiedzieć się, czy istnieją rzeczywiste dane do odczytania z niego bez faktycznej próby (select(2) nic w tym nie pomoże, tylko sygnalizuje, że możesz spróbować bez blokowania).

Więcej informacji można znaleźć w często zadawanych pytaniach dotyczących gniazd Unix .


2
Gniazda REALbasic (w systemie Mac OS X i Linux) są oparte na gniazdach BSD, ale RB daje ładny błąd 102, gdy połączenie jest przerywane na drugim końcu. Zgadzam się więc z oryginalnym plakatem, powinno to być możliwe i szkoda, że ​​Java (i Cocoa) tego nie zapewniają.
Joe Strout

1
@JoeStrout RB może to zrobić tylko wtedy, gdy wykonujesz pewne operacje we / wy. Nie ma interfejsu API, który poda stan połączenia bez wykonywania operacji we / wy. Kropka. To nie jest brak Javy. W rzeczywistości jest to spowodowane brakiem „sygnału wybierania” w TCP, co jest celową cechą konstrukcyjną.
Markiz Lorne

select() informuje, czy są dostępne dane lub EOS do odczytu bez blokowania. „Sygnały, które możesz wypróbować bez blokowania” nie mają znaczenia. Jeśli jesteś w trybie bez blokowania, zawsze możesz spróbować bez blokowania. select()jest sterowany danymi w buforze odbioru gniazda lub oczekującym FIN lub spacją w buforze wysyłania gniazda.
Markiz Lorne

@EJP A co z getsockopt(SO_ERROR)? W rzeczywistości nawet getpeernamepowie ci, czy gniazdo jest nadal podłączone.
David Schwartz,

0

Tylko zapisy wymagają wymiany pakietów, co pozwala na określenie utraty połączenia. Typowym obejściem jest użycie opcji KEEP ALIVE.


Myślę, że punkt końcowy może zainicjować bezpieczne zamknięcie połączenia, wysyłając pakiet z ustawionym FIN, bez zapisywania żadnego ładunku.
Alexander

@Alexander Oczywiście, że tak, ale to nie dotyczy tej odpowiedzi, która dotyczy wykrywania mniejszej liczby połączeń.
Markiz Lorne

-3

Jeśli chodzi o półotwarte gniazda Java, warto przyjrzeć się isInputShutdown () i isOutputShutdown () .


Nie. To mówi tylko o tym, jak dzwoniłeś, a nie o tym, co zadzwonił kolega.
Markiz Lorne,

Chcesz udostępnić swoje źródło tego oświadczenia?
JimmyB

3
Chcesz udostępnić swoje źródło przeciwne? To twoje oświadczenie. Jeśli masz dowód, miejmy go. Zapewniam, że się mylisz. Przeprowadź eksperyment i udowodnij, że się mylę.
Markiz Lorne

Trzy lata później i żadnego eksperymentu. QED
Markiz Lorne
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.