php小編小新在網絡編程中,關閉客戶端連接后,如何從tcp套接字完全讀取緩沖內容是一個常見問題。當客戶端關閉連接時,服務器端可能仍然存在未讀取的數據。為了確保完整讀取緩沖內容,可以采用以下幾種方法:1.使用循環讀取,直到讀取到數據結束標志;2.設置超時時間,如果在規定時間內未讀取到數據則跳出循環;3.使用非阻塞套接字,通過輪詢檢查是否有數據可讀。以上方法可以保證從tcp套接字中完全讀取緩沖內容,提升網絡通信的穩定性和可靠性。
問題內容
我有一個 go 服務器正在偵聽 tcp 套接字,在關閉期間我希望它告訴客戶端停止發送更多數據,但也讀取客戶端迄今為止發送的所有內容。我看到的是,一旦客戶端連接關閉,服務器就會停止讀取,但它永遠不會收到客戶端認為它發送的所有內容。我認為發生的情況是操作系統正在緩沖收到的 tcp 數據包,然后在服務器關閉客戶端連接時丟棄它們。
這是一個顯示該問題的程序。服務器監聽,然后打印出它收到的內容;客戶端發送并打印出發送的內容。服務器中斷客戶端以停止它,但最后我希望這兩個列表相同。
此示例使用 bufio.scanner 讀取套接字,但我也嘗試過僅從連接讀取字節,但得到相同的結果。
conn.(*net.tcpconn).closewrite()
聽起來正是我想要的,但這似乎根本沒有中斷客戶端。
conn.(*net.tcpconn).setreadbuffer(0)
– 也嘗試過此操作,但 0 不是允許的值(恐慌)。
package main import ( "bufio" "fmt" "net" "strconv" "sync" "time" ) const port = ":8888" var wg sync.waitgroup func main() { wg.add(1) go func() { // wait for the server to start time.sleep(1000 * time.millisecond) client() }() // server listens wg.add(1) server() wg.wait() } func server() { defer wg.done() listener, err := net.listen("tcp", port) if err != nil { panic(err) } received := make([]string, 0) conn, err := listener.accept() if err != nil { panic(err) } defer conn.close() scanner := bufio.newscanner(conn) for i := 0; scanner.scan(); i++ { received = append(received, scanner.text()) // arbitrary condition: simulate a server shutdown - interrupt the client if len(received) == 2 { _ = conn.(*net.tcpconn).close() } } fmt.println("server received: ", received) } func client() { defer wg.done() conn, err := net.dial("tcp", port) if err != nil { panic(err) } sent := make([]string, 0) defer conn.close() for i := 0; i < 50000; i++ { v := strconv.itoa(i) _, err := conn.write([]byte(v + "\n")) if err != nil { fmt.println("client interrupted:", err) break } else { sent = append(sent, v) // slow down the sends to make output readable //time.sleep(1 * time.millisecond) } } fmt.println("client sent: ", sent) }
登錄后復制
在我的機器上輸出是這樣的:
Server received: [0 1 2] Client interrupted: write tcp 127.0.0.1:49274->127.0.0.1:8888: write: broken pipe Client sent: [0 1 2 3 4 5 6 7 8 9 10 11 12 13]
登錄后復制
解決方法
TCP僅提供可靠的雙向字節流,沒有任何固有語義。如果服務器希望客戶端發出信號表明它不應再發送任何數據,則必須將此功能實現為應用程序協議的一部分,即 TCP 提供的純數據傳輸之上的一層。