如何在关闭客户端连接后完全读取TCP套接字中的缓冲内容?

huangapple go评论89阅读模式
英文:

How to fully read buffered content from a tcp socket after closing the client connection?

问题

我有一个在tcp套接字上监听的Go服务器,在关闭时我希望它告诉客户端停止发送更多数据,但同时读取客户端到目前为止发送的所有数据。我发现一旦客户端连接关闭,服务器就停止读取,但它从未接收到客户端认为已发送的所有数据。我认为发生的情况是操作系统在接收到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() {
		// 等待服务器启动
		time.Sleep(1000 * time.Millisecond)
		client()
	}()

	// 服务器监听
	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 scanner.Scan() {
		received = append(received, scanner.Text())

		// 任意条件:模拟服务器关闭-中断客户端
		if len(received) == 2 {
			conn.(*net.TCPConn).CloseRead()
			break
		}
	}

	fmt.Println("服务器接收到的内容:", 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("客户端中断:", err)
			break
		} else {
			sent = append(sent, v)
			// 减慢发送速度以使输出可读
			//time.Sleep(1 * time.Millisecond)
		}
	}
	fmt.Println("客户端发送的内容:", sent)
}

在我的机器上,输出类似于:

服务器接收到的内容: [0 1 2]
客户端中断:write tcp 127.0.0.1:49274->127.0.0.1:8888: write: broken pipe
客户端发送的内容: [0 1 2 3 4 5 6 7 8 9 10 11 12 13]

希望这可以帮助到你!

英文:

I have a Go server listening on a tcp socket and during shutdown I want it to tell the client to stop sending more data, but also read everything the client has sent thus far. What I see is that once the client connection is closed, the server stops reading, but it never receives everything the client thinks it sent. I think what is happening is that the OS is buffering tcp packets received and then dropping them when the server closes the client connection.

Here is a program that shows the issue. The server listens, then prints out what it received; the client sends and prints out what it sent. The server interrupts the client to stop it, but at the end I want these two lists to be the same.

  • This example uses bufio.Scanner to read the socket but I have also tried just reading bytes from the Connection, but get the same result.

  • conn.(*net.TCPConn).CloseWrite() Sounds like exactly what I want, but that didn't seem to interrupt the client at all.

  • conn.(*net.TCPConn).SetReadBuffer(0) - also tried this, but 0 is not an allowed value (panic).

package main
import (
&quot;bufio&quot;
&quot;fmt&quot;
&quot;net&quot;
&quot;strconv&quot;
&quot;sync&quot;
&quot;time&quot;
)
const port = &quot;:8888&quot;
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(&quot;tcp&quot;, 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(&quot;Server received: &quot;, received)
}
func client() {
defer wg.Done()
conn, err := net.Dial(&quot;tcp&quot;, port)
if err != nil {
panic(err)
}
sent := make([]string, 0)
defer conn.Close()
for i := 0; i &lt; 50000; i++ {
v := strconv.Itoa(i)
_, err := conn.Write([]byte(v + &quot;\n&quot;))
if err != nil {
fmt.Println(&quot;Client interrupted:&quot;, err)
break
} else {
sent = append(sent, v)
// slow down the sends to make output readable
//time.Sleep(1 * time.Millisecond)
}
}
fmt.Println(&quot;Client sent: &quot;, sent)
}

The output is something like this on my machine:

Server received:  [0 1 2]
Client interrupted: write tcp 127.0.0.1:49274-&gt;127.0.0.1:8888: write: broken pipe
Client sent:  [0 1 2 3 4 5 6 7 8 9 10 11 12 13]

答案1

得分: 3

TCP提供的只是一个可靠的双向字节流,没有任何固有的语义。如果服务器希望客户端发出信号表示不再发送任何数据,那么这个功能必须作为应用协议的一部分来实现,即在TCP提供的纯数据传输之上的一层。

英文:

TCP provides just a reliable bidirectional byte stream without any inherent semantics. If the server wants the client to signal that it should not longer send any data, then this functionality has to be implemented as part of the application protocol, i.e. a layer above the pure data transport provided by TCP.

huangapple
  • 本文由 发表于 2023年5月9日 22:51:31
  • 转载请务必保留本文链接:https://go.coder-hub.com/76210642.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定