2010-11-10 70 views
21

我有一個TCP連接。服務器只是從客戶端讀取數據。現在,如果連接丟失,客戶端在將數據寫入管道(斷開的管道)時會出錯,但服務器仍在該管道上偵聽。有沒有什麼方法可以找到連接是否UP?如何在C中找到套接字連接狀態?

+2

我想你應該澄清無論您是爲Windows或Linux開發/ UNIX .. – nairdaen 2010-11-10 07:14:21

+0

在出現錯誤發送的結果,你應該首先檢查什麼樣的錯誤,它實際上是,然後決定是一個可恢復的錯誤(它可以修復它自己嗎?)或者如果它意味着你的連接肯定死了。如果確實死了,關閉插座。如果仍然可以通過任何流量,另一側將看到套接字已關閉。如果你無法通過任何交通,你仍然失去。在這種情況下,另一方可以注意到的唯一方法是發送一些本應該引發答覆的東西,如果沒有這樣的答覆,那麼另一方已經死亡。 – Mecki 2017-07-18 15:57:47

回答

30

您能夠打電話的getsockopt就像如下:

int error = 0; 
socklen_t len = sizeof (error); 
int retval = getsockopt (socket_fd, SOL_SOCKET, SO_ERROR, &error, &len); 

要測試插座起來:

if (retval != 0) { 
    /* there was a problem getting the error code */ 
    fprintf(stderr, "error getting socket error code: %s\n", strerror(retval)); 
    return; 
} 

if (error != 0) { 
    /* socket has a non zero error status */ 
    fprintf(stderr, "socket error: %s\n", strerror(error)); 
} 
+12

雖然它不會檢測到一個無聲破損的插座。一種常見的情況是中間的NAT網關超時連接(並且沒有人得到通知) - 像其他人所說的那樣,檢測到您必須定期發送某些內容,或者通過在您的應用程序協議上實施某種形式的心跳,或者至少使用TCP_KEEPALIVE – nos 2010-11-10 08:15:49

+0

任何人都想知道,我認爲'SOL_SOCKET'代表'套接字選項級別:套接字'和'SO_ERROR'是套接字上的最後一個錯誤代碼。 – n611x007 2013-08-30 12:05:46

+1

這隻返回最後一個錯誤。它不檢測未決錯誤。 – EJP 2015-12-20 06:40:42

-1

對於BSD套接字我會找Beej's guide。當recv返回0時,您知道另一端已斷開連接。

現在你可能會問,檢測另一側斷開的最簡單方法是什麼?做這件事的一種方法是讓一個線程總是進行recv。該線程將能夠立即分辨客戶端何時斷開連接。

+7

不,它不會。如果(a)它從對方那裏收到一個這樣的數據包,或者(b)它超時等待發送的數據包的確認,tcp堆棧只能檢測到套接字已關閉。在recv的情況下,如果中間路由器發生故障或遠程主機異常關閉了套接字(不發送關閉),則不會有數據包到達,從而通知套接字堆棧recv正在等待關閉連接。 – 2010-11-10 08:03:32

3

TCP存活套接字選項(SO_KEEPALIVE)將有助於在這種情況下關閉服務器套接字以防連接丟失。

+2

它不會關閉任何東西。它會在下次發送或接收時發生錯誤,但套接字保持打開狀態。 – EJP 2015-12-20 06:34:37

10

可靠地檢測套接字是否仍連接的唯一方法是定期嘗試發送數據。定義客戶端忽略的應用程序級別「ping」數據包通常更方便,但如果協議已經被指定出來而沒有這種功能,則應該可以通過設置SO_KEEPALIVE套接字選項來配置tcp套接字來完成此操作。我鏈接到了winsock文檔,但是所有類似BSD的套接字棧都應該具有相同的功能。

+0

感謝您的時間。我考慮過SO_KEEPALIVE選項,但在Linux中激活KeepAlive探針的默認時間是2小時。如果我將其降低到幾秒鐘,其他應用程序可能會受到影響。我會給它一個反正:) – Blacklabel 2010-11-10 08:44:37

+1

這就是爲什麼它通常訴諸應用程序級ping數據包,服務器可以發送每當連接已安靜一段時間。如果 - 就像在http中一樣 - 你不能在沒有收到客戶端請求的情況下發送數據,那麼你別無選擇,只能簡單地關閉已經保持「太長時間」的連接,並在需要時依賴客戶端重新連接。 – 2010-11-10 09:41:48

+5

@Blacklabel Linux(和其他)允許您設置每個套接字的存活超時時間,請參閱'man 7 tcp' – nos 2010-11-10 20:12:22

2

我有類似的問題。我想知道服務器是連接到客戶端還是客戶端連接到服務器。在這種情況下,recv函數的返回值可以派上用場。如果套接字未連接,它將返回0字節。因此,使用這個我打破了循環,不必使用任何額外的函數線程。如果專家認爲這是正確的方法,您也可以使用相同的方法。

1

get sock opt可能有些用處,但是,另一種方法是爲SIGPIPE安裝信號處理程序。基本上,無論套接字連接何時中斷,內核都會向進程發送一個SIGPIPE信號,然後您可以執行必要的操作。但是這仍然不能提供知道連接狀態的解決方案。希望這可以幫助。

1

您應該嘗試使用:getpeername函數。

現在,當連接斷開時,您將收到errno: ENOTCONN - 套接字未連接。 這意味着你的羽絨服。

else(如果沒有其他故障)那裏的返回碼將會是0 - >這意味着UP。

資源: 手冊頁:http://man7.org/linux/man-pages/man2/getpeername.2.html

+1

Nope中的TCP_KEEPIDLE套接字選項。 ENOTCONN表示在傳遞給getpeername函數的套接字上沒有調用connect函數。套接字可能會「連接」,但這並不意味着連接已啓動。 – 2015-12-17 18:17:13

+0

你好,你看過一個TCP連接但它沒有連接嗎? – 2015-12-26 21:06:59

+0

「連接」我的意思是一個套接字,其中connect已成功調用。說「起來」,我的意思是連接不是半開放的。當服務器僅作爲監聽者時,它可以永久地在連接的套接字上永久地進行監聽,該套接字並非實際上(即半開)。本文詳細解釋了半開連接:http://blog.stephencleary.com/2009/05/detection-of-half-open-dropped.html – 2015-12-26 21:17:16

0

可以使用SS_ISCONNECTEDgetsockopt()功能。 SS_ISCONNECTEDsocketvar.h中定義。

+2

看起來像內核內核對我來說。 – EJP 2015-12-20 06:39:39

0

有一個簡單的方法來通過調用poll來檢查套接字連接狀態。首先,您需要輪詢套接字,是否有POLLIN事件。

  1. 如果套接字未關閉並且有數據要讀取,那麼read將返回大於零。
  2. 如果插座上沒有新的數據,然後POLLIN將在revents
  3. 如果關閉套接字然後POLLIN標誌將被設置爲一個讀將返回0

這裏是設置爲0一小段代碼:

int client_socket_1, client_socket_2; 
if ((client_socket_1 = accept(listen_socket, NULL, NULL)) < 0) 
{ 
    perror("Unable to accept s1"); 
    abort(); 
} 
if ((client_socket_2 = accept(listen_socket, NULL, NULL)) < 0) 
{ 
    perror("Unable to accept s2"); 
    abort(); 
} 
pollfd pfd[]={{client_socket_1,POLLIN,0},{client_socket_2,POLLIN,0}}; 
char sock_buf[1024]; 
while (true) 
{ 
    poll(pfd,2,5); 
    if (pfd[0].revents & POLLIN) 
    { 
     int sock_readden = read(client_socket_1, sock_buf, sizeof(sock_buf)); 
     if (sock_readden == 0) 
      break; 
     if (sock_readden > 0) 
      write(client_socket_2, sock_buf, sock_readden); 
    } 
    if (pfd[1].revents & POLLIN) 
    { 
     int sock_readden = read(client_socket_2, sock_buf, sizeof(sock_buf)); 
     if (sock_readden == 0) 
      break; 
     if (sock_readden > 0) 
      write(client_socket_1, sock_buf, sock_readden); 
    } 
} 
+0

這是一個好的開始,但是OP所要求的是關於錯誤......如果套接字產生錯誤,你會得到一個'POLLERR','POLLHUP','POLLRDHUP'或'POLLNVAL'。如果你的'revents'設置了這些標誌中的任何一個,那麼你就知道這個套接字現在已經斷開,你可以正確使用它。此外,除非套接字返回「POLLOUT」,否則不應該寫入()。最後,至少在Linux下,它是'POLLIN'(否'_')。在https://github.com/m2osw/snapcpp/blob/master/snapwebsites/libsnapwebsites/src/snap_communicator.cpp找到圍繞7000行的完整功能版本 – 2017-01-23 05:52:13

相關問題