2013-02-08 6 views
6

Jestem debugowania programu gniazda linux c na podstawie. Jak wszystkimi dostępnymi na stronach internetowych przykładach zastosowałem następującą strukturę:Linux Socket: Jak wykryć rozłączoną sieć w programie klienckim?

sockfd= socket(AF_INET, SOCK_STREAM, 0); 

connect(sockfd, (struct sockaddr *) &serv_addr, sizeof(serv_addr)); 

send_bytes = send(sockfd, sock_buff, (size_t)buff_bytes, MSG_DONTWAIT); 

mogę wykryć odłączenie gdy serwer usuń zamyka program serwera. Ale jeśli odłączę kabel ethernetowy, funkcja wysyłania nadal zwraca wartości dodatnie zamiast -1.

Jak mogę sprawdzić połączenie sieciowe w programie klienta, zakładając, że nie mogę zmienić strony serwera?

Odpowiedz

28

Ale jeśli odłączyć kabel ethernet, funkcja wyślij jeszcze powrócić pozytywne wartości niż -1.

Przede wszystkim należy wiedzieć send faktycznie nie wysyłać nic, to po prostu wywołanie funkcji/System pamięci kopiowania. Kopiuje dane z twojego procesu do jądra - jakiś czas później jądro pobierze te dane i wyśle ​​je na drugą stronę po zapakowaniu w segmenty i pakiety. Dlatego send może jedynie zwrócić błąd, jeśli:

  • gniazdo jest nieważna (na przykład podrobiony deskryptora pliku)
  • Połączenie jest wyraźnie nieważne, na przykład, że nie zostało ustalone lub została już zakończona w niektórych sposób (FIN, RST, timeout - patrz niżej)
  • nie ma więcej miejsca, aby skopiować dane

głównym punktem jest to, że send nie wysyła nic i dlatego jego kod zwrotny nie powiedzieć, byle co o danych faktycznie docierających do drugiej strony.

Wracając do pytania, kiedy TCP wysyła dane, oczekuje na prawidłowe potwierdzenie w rozsądnym czasie. Jeśli go nie dostanie, wyśle ​​ponownie. Jak często wysyła się ponownie? Każdy stos TCP robi rzeczy inaczej, ale normą jest użycie wykładniczego wycofywania. Oznacza to, że najpierw poczekaj 1 sekundę, potem 2, potem 4 i tak dalej. Na niektórych stosach proces ten może potrwać kilka minut.

Głównym powodem jest to, że w przypadku przerwy TCP zadeklaruje połączenie zerwane dopiero po poważnie długim okresie milczenia (w systemie Linux robi to co 15 prób - więcej niż 5 minut).

Jednym ze sposobów rozwiązania tego problemu jest wdrożenie mechanizmu potwierdzania w aplikacji. Możesz na przykład wysłać zapytanie do serwera "odpowiedź w ciągu 5 sekund lub zadeklaruję to połączenie martwe", a następnie recv z limitem czasu.

1

sprawdzić wartość zwracaną i sprawdzić, czy jest równa tej wartości:

EPIPE
To gniazdo zostało podłączone, ale połączenie zostało przerwane. W takim przypadku send najpierw generuje sygnał SIGPIPE; jeśli ten sygnał zostanie zignorowany lub zablokowany, lub jeśli jego program obsługi zostanie zwrócony, wysyłanie zakończy się niepowodzeniem z EPIPE.

Dodaj również kontrolkę dla sygnału SIGPIPE w programie obsługi, aby był bardziej kontrolowany.

0

Nie można wykryć odłączonego kabla ethernetowego tylko za pomocą wywołania funkcji write(). Dzieje się tak z powodu retransmisji tcp działającej na stosie tcp bez twojej świadomości. Oto rozwiązania.

Nawet jeśli już ustawiłeś opcję keepalive na swoje gniazdo aplikacji, nie możesz na czas wykryć stanu martwego połączenia gniazda, na wypadek, gdyby aplikacja zapisywała na gnieździe. Dzieje się tak z powodu retransmisji tcp przez stos tcp jądra. tcp_retries1 i tcp_retries2 są parametrami jądra do konfiguracji limitu czasu retransmisji tcp. Trudno jest przewidzieć dokładny czas opóźnienia retransmisji, ponieważ jest on obliczany przez mechanizm RTT. Możesz zobaczyć to obliczenie w rfc793. (3.7. Komunikacja komputerowa)

https://www.rfc-editor.org/rfc/rfc793.txt

Każdy platformy mają konfiguracje kernela dla TCP retransmisji.

Linux : tcp_retries1, tcp_retries2 : (exist in /proc/sys/net/ipv4) 

http://linux.die.net/man/7/tcp

HPUX : tcp_ip_notify_interval, tcp_ip_abort_interval 

http://www.hpuxtips.es/?q=node/53

AIX : rto_low, rto_high, rto_length, rto_limit 

http://www-903.ibm.com/kr/event/download/200804_324_swma/socket.pdf

należy ustawić niższą wartość dla tcp_retries2 (domyślnie 15), jeśli chcesz, aby wcześnie wykryć martwe połączenie, ale to nie tak dokładny czas jak ja powiedziany. Ponadto obecnie nie można ustawić tych wartości tylko dla pojedynczego gniazda. Są to globalne parametry jądra. Była próba zastosowania opcji gniazda retransmisji tcp dla pojedynczego gniazda (http://patchwork.ozlabs.org/patch/55236/), ale nie sądzę, że została zastosowana w głównej linii jądra. Nie mogę znaleźć tych opcji w plikach nagłówkowych systemu.

Dla odniesienia, możesz monitorować twój keepalive gniazdo przez 'netstat --timers' jak poniżej. https://stackoverflow.com/questions/34914278

netstat -c --timer | grep "192.0.0.1:43245    192.0.68.1:49742" 

tcp  0  0 192.0.0.1:43245    192.0.68.1:49742   ESTABLISHED keepalive (1.92/0/0) 
tcp  0  0 192.0.0.1:43245    192.0.68.1:49742   ESTABLISHED keepalive (0.71/0/0) 
tcp  0  0 192.0.0.1:43245    192.0.68.1:49742   ESTABLISHED keepalive (9.46/0/1) 
tcp  0  0 192.0.0.1:43245    192.0.68.1:49742   ESTABLISHED keepalive (8.30/0/1) 
tcp  0  0 192.0.0.1:43245    192.0.68.1:49742   ESTABLISHED keepalive (7.14/0/1) 
tcp  0  0 192.0.0.1:43245    192.0.68.1:49742   ESTABLISHED keepalive (5.98/0/1) 
tcp  0  0 192.0.0.1:43245    192.0.68.1:49742   ESTABLISHED keepalive (4.82/0/1) 

Ponadto, gdy keepalive ocurrs limitu czasu, można spotkać różne wydarzenia powrotne w zależności od platformy używasz, więc nie musi decydować stan martwego połączenia tylko przez wydarzenia w obie strony. Na przykład HP zwraca zdarzenie POLLERR, a system AIX zwraca tylko zdarzenie POLLIN po przekroczeniu limitu czasu keepalive. W tym czasie wystąpi błąd ETIMEDOUT w wywołaniu recv().

W najnowszej wersji jądra (od wersji 2.6.37) można użyć opcji TCP_USER_TIMEOUT. Ta opcja może być używana dla pojedynczego gniazda.

Wreszcie można użyć funkcji odczytu z flagą MSG_PEEK, która pozwala sprawdzić, czy gniazdo jest w porządku. (MSG_PEEK po prostu sprawdza, czy dane dotarły do ​​bufora stosu jądra i nigdy nie kopiuje danych do bufora użytkownika.) Możesz więc użyć tej flagi tylko do sprawdzenia, czy gniazdo jest w porządku bez żadnych skutków ubocznych.

Powiązane problemy