2010-01-29 20 views
7

Mam połączenie tcp w gnieździe między dwiema aplikacjami java. Kiedy jedna strona zamyka gniazdo, druga strona pozostaje otwarta. ale chcę, żeby była zamknięta. A także nie mogę się doczekać, czy będzie dostępny, czy nie, a po tym zamknąć. Chcę w jakiś sposób całkowicie go zamknąć z jednej strony. Co mogę zrobić?Czy można zamknąć gniazda Java po stronie klienta i serwera?

Odpowiedz

10

TCP nie działa w ten sposób. System operacyjny nie zwolni zasobów, a mianowicie deskryptora pliku, a tym samym portu, dopóki aplikacja jawnie nie zamknie gniazda lub nie zginie, nawet jeśli stos TCP wie, że druga strona go zamknęła. Nie ma wywołania zwrotnego od jądra do aplikacji użytkownika po otrzymaniu FIN od peera. System operacyjny potwierdza to drugiej stronie, ale czeka, aż aplikacja zadzwoni pod numer close() przed wysłaniem pakietu FIN. Spójrz na numer TCP state transition diagram - znajdujesz się w polu pasywnym zamykającym .

Jednym ze sposobów wykrycia takiej sytuacji bez dedykowania wątku do każdego gniazda jest użycie rodziny funkcji select/poll/epoll/kqueue. Gniazdo, które jest pasywnie zamknięte, zostanie zasygnalizowane jako czytelne, a próba odczytu zwróci EOF.

Mam nadzieję, że to pomoże.

+0

Łącze diagramu stanu połączenia TCP daje 404 –

6

Obie strony muszą odczytać połączenie, aby mogły wykryć zamknięcie peera. Kiedy read zwraca -1 oznacza to, że drugi koniec zamknął połączenie i to jest Twoja wskazówka, aby zamknąć swój koniec.

+0

Mój kod to wielowątkowy kod i nie mogę marnować jednego z moich wątków, szukając tylko połączenia i sprawdzając, czy jest zamknięty, czy nie. Powinienem wspomnieć, że mam tysiące takich połączeń, nie tylko jeden. – Shayan

+3

Następnie musisz przełączyć na NIO i wykonać multipleksowane IO. Musisz czytać z gniazda w ten czy inny sposób, jeśli chcesz wykryć pełne wdzięku zbliżenie od rówieśnika. – nos

0

Prawdopodobnie chcesz mieć pulę połączeń.

+0

Nie. Po prostu automatycznie zamyka się połączenie. – Shayan

4

Jeśli nadal czytasz z gniazda, wykryjesz wartość -1 po jej zamknięciu.

Jeśli nie czytasz już z gniazda, zamknij je.

Jeśli nie jest to żadna z tych rzeczy, prawdopodobnie oczekujesz wątku na zdarzenie. NIE jest to sposób, w jaki chcesz obsłużyć tysiące portów! Java zacznie dostawać pukey na około 3000 wątków w oknach - o wiele mniej w Linuksie (nie wiem dlaczego).

Upewnij się, że używasz NIO. Użyj jednego wątku do zarządzania wszystkimi portami (pulą połączeń). Powinien po prostu pobrać dane z wątku, przekazać je do kolejki. W tym momencie wydaje mi się, że mam pulę wątków, która pobiera dane z kolejek i przetwarza je, ponieważ przetwarzanie danych z portu zajmuje trochę czasu.

Dołączanie wątku do każdego portu NIE działa i jest najważniejszym powodem, dla którego NIO było potrzebne.

Ponadto, posiadanie pewnego rodzaju komunikatu "Zamknij" jako części twojego strumienia, aby aktywować zamykanie portu, może przyspieszyć działanie - ale nadal musisz obsłużyć -1, aby pokryć przypadek zepsutych strumieni

+0

Ta strona nie wie, czy jest ukończona, czy nie.Wysyłanie zbyt dużej wiadomości z drugiej strony jest zbyt duże. większość razy dla jednej wiadomości wysyłamy dwie wiadomości. – Shayan

+2

Nie jestem pewien co masz na myśli, ale możesz po prostu zakończyć wiadomość specjalną postacią lub kodem bajtowym, aby powiedzieć słuchaczowi, że to ostatnia wiadomość. To prawie za darmo. –

+1

Jeśli wysłanie kilku dodatkowych bajtów, aby zasygnalizować, że zamknięcie jest zbyt duże, masz albo specjalne wymagania, albo cierpisz na przedwczesną optymalizację. – Confusion

3

Zazwyczaj rozwiązaniem jest poinformowanie drugiej strony, że zamierzasz zamknąć połączenie, zanim je zamkniesz. Na przykład w przypadku protokołu SMTP serwer wyśle ​​komunikat "221 Bye", zanim zamknie połączenie.

+0

Mimo że jest to dobra optymalizacja, nie będzie obsługiwać zerwanych połączeń, a ostatecznie utworzysz otwarte porty. Jest to niedopuszczalne w przypadku systemu z tysiącami otwartych portów, ale zmniejsza częstotliwość skanowania "Czy mogę zamknąć ten port". –

Powiązane problemy