簡體   English   中英

在 epoll linux 中檢測連接 state

[英]detecting connection state in epoll linux

有很多關於如何使用各種方法檢測套接字是否已連接的線程,例如 getpeername / getsockopt w/ SO_ERROR。 https://man7.org/linux/man-pages/man2/getpeername.2.html對我來說是檢測套接字是否已連接的好方法。 問題是,它沒有說明連接是否在進行中......所以如果我調用連接,它正在進行中,然后我調用 getpeername,它會說這是一個錯誤(-1)即使連接還在進行中?

如果是這樣,我可以實現一個類似計數器的系統,如果它在 x 秒后仍在進行中,最終將終止套接字。

男人連接

如果發起套接字是連接模式,......如果不能立即建立連接並且沒有為套接字的文件描述符設置 O_NONBLOCK,則 connect() 將阻塞直到建立連接之前未指定的超時間隔. 如果超時間隔在連接建立之前到期,connect() 將失敗並且連接嘗試將被中止。

如果 connect() 被阻塞等待建立連接時捕獲的信號中斷,connect() 將失敗並將 errno 設置為 [EINTR],但連接請求不應中止,連接應異步建立。

如果無法立即建立連接,並且套接字的文件描述符設置為 O_NONBLOCK,connect() 將失敗並將 errno 設置為 [EINPROGRESS],但連接請求不應中止,連接應異步建立。

當異步建立連接時,select() 和 poll() 應指示套接字的文件描述符已准備好寫入。

如果套接字處於阻塞模式, connect將在連接進行時阻塞。 connect返回后,您將知道連接是否已建立(或未建立)。

信號可以中斷(阻塞/等待)過程,然后連接例程將切換到異步模式。

如果套接字處於非阻塞模式( O_NONBLOCK )並且無法立即建立連接,連接將失敗並出現錯誤EINPROGRESS並且像上面一樣切換到異步模式,這意味着,您必須使用selectpoll來確定是否套接字已准備好寫入(表示已建立連接)。

簡答

我認為,如果getpeername()返回ENOTCONN ,那僅僅意味着 tcp 連接請求尚未成功。 為了返回ENOTCONN ,我認為客戶端需要從服務器接收到 syn+ack 並發送自己的 ack,而服務器端需要接收到客戶端的 ack。

此后所有的賭注都取消了。 連接隨后可能會中斷,但getpeername()無法知道這已經發生。

長答案

這在很大程度上取決於人們想要了解連接是否正常的挑剔程度和短期程度。

嚴格來講...

嚴格來說,大驚小怪,一個人無法知道。 在數據包交換網絡中,網絡中沒有任何東西知道(在任何一個時間點)確定對等點之間可能存在連接。 這是一個“試試看”的事情。

這與電路交換網絡(例如普通的舊電話呼叫)形成對比,在電路交換網絡中,對等體(電話)之間有專供使用的實時電路; 如果電流在流動,即使電話另一端的人保持沉默,您也知道電路是完整的。

請注意,如果兩台計算機通過一根 Ethe.net 電纜連接(沒有路由器,沒有交換機,只有 NIC 之間的電纜),那實際上是一個固定電路(甚至不是電路交換網絡)。

放松一點...

專注於人們對分組交換網絡中的連接的了解。 正如其他人已經說過的那樣,答案是,實際上,必須不斷發送和接收數據包才能知道網絡是否仍然可以連接兩個對等點。

這樣的數據包交換發生在 tcp 套接字connect() - 連接方發送一個特殊的數據包說“請問我可以連接到你”,並且服務方回復“是”,然后客戶端說“謝謝”, (syn->, <-syn+ack.ack->),但此后數據包僅在應用程序發送和接收數據時在對等點之間流動。 或選擇關閉連接 (fin)。

根據您的要求,我認為調用類似getpeername()的東西有點誤導。 如果您相信網絡基礎設施和遠程計算機及其應用程序不會中斷,也不會崩潰,那很好。

connect()有可能成功,然后 .network 中的某個地方出現問題(例如,對等方的 .network 連接被拔掉,或者對等方崩潰),並且在您的 .network 端不知道發生了什么.

您可以了解的第一件事是,如果您發送了一些流量但沒有得到響應。 最初的響應是 tcp acks(它允許您的網絡堆棧清除它的一些緩沖區),然后可能是從對等應用程序返回的實際消息。 如果你繼續向外發送數據,.network 會很樂意盡可能地路由數據包,但是你的 tcp 堆棧的緩沖區會由於缺乏從對等方返回的確認而填滿。 最終,your.network 套接字會阻塞對write()的調用,因為本地緩沖區已滿。

各種選擇...

  • 如果您正在編寫兩個應用程序(服務器和客戶端),則可以將應用程序編寫為定期“乒乓”連接; 只需發送一條消息,意思就是“告訴我你聽到了這個”。 成功的 ping-ponging 意味着,至少在最后幾秒內,連接正常。

  • 使用像 ZeroMQ 這樣的庫。 這個庫解決了使用網絡連接的許多問題,並且還包括(在現代版本中)套接字心跳(即乒乓)。 這很簡潔,因為 ZeroMQ 負責處理建立、恢復和監視帶有心跳的連接的雜亂業務,並且可以在連接 state 發生變化時通知應用程序。 同樣,您需要同時編寫客戶端和服務器應用程序,因為 ZeroMQ 在 tcp 之上有自己的協議,它與普通的舊套接字不兼容。 如果您對這種方法感興趣,請在 API 文檔中查找單詞socket monitorZMQ_HEARTBEAT_IVL

  • 如果真的只有一端需要知道連接仍然可用,則可以通過讓另一端發送“ping”來完成。 這可能適合您不在兩端編寫軟件的情況。 例如,服務器應用程序可能被配置(而不是重寫)為 stream 輸出數據,而不管客戶端是否需要,並且客戶端會忽略大部分數據。 但是,客戶端知道如果它正在接收數據,那么它也知道存在連接。 服務器不知道(它只是盲目地發送數據,直到其writes()最終阻塞),但可能不需要知道。

Ping ponging 也很好,因為它給出了網絡性能的一些指示。 如果一端期望在發送 ping 后 5 秒內收到 pong 但沒有收到,則表明一切都不符合預期(即使數據包最終出現)。

這允許在有效工作的網絡和正在傳送數據包但速度太慢而無用的網絡之間進行區分。 后者在技術上仍然是“連接的”並且可能被其他測試表示為連接(例如調用getpeername() ),但它也可能不是。

當地知識有限...

一個人可以在本地對同伴做的事情是有限的。 對等點可以知道它與網絡的連接是否存在(例如,NIC 報告活動連接),但僅此而已。

我的想法

就個人而言,如果可能的話,這些天我默認使用 ZeroMQ。 即使這意味着重新編寫軟件,也沒有看起來那么糟糕。 這是因為通常會用 zmq_connect() 替換connect()等代碼,用zmq_connect()替換recv() zmq_revc() 。通常也會刪除很多代碼。 ZeroMQ 是面向消息的,一個 tcp 套接字是面向 stream 的。 相當多的應用程序必須將 tcp 適配為面向消息的,而 ZeroMQ 替換了所有這樣做的代碼。

ZeroMQ 在綁定和/或重新實現方面也得到了多種語言的良好支持。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM