Golang:TCP客户端/服务器数据分隔符

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

Golang: TCP client/server data delimiter

问题

我不确定如何表达这个问题,也不确定它是否只与Go语言有关,但我想做的是创建一个TCP服务器和客户端,它们之间可以交换数据。基本上,客户端会将大量数据流式传输到服务器,服务器会等待读取每个数据块,然后回复一个状态码,客户端会根据状态码进行其他操作。

我使用下面的函数作为一个测试,从客户端和服务器读取数据(请注意,我知道这个函数不完美,只是用于测试):

func createBufferFromConn(conn net.Conn) *bytes.Buffer {
	buffer := &bytes.Buffer{}
	doBreak := false
	for {
		incoming := make([]byte, BUFFER_SIZE)

		conn.SetReadDeadline(time.Now().Add(time.Second * 2))
		bytesRead, err := conn.Read(incoming)
		conn.SetReadDeadline(time.Time{})

		if err != nil {
			if err == io.EOF {
				fmt.Println(err)
			} else if neterr, ok := err.(net.Error); ok && neterr.Timeout() {
				fmt.Println(err)
			}
			doBreak = true
		}

		if doBreak == false && bytesRead == 0 {
			continue
		}

		if bytesRead > 0 {
			buffer.Write(incoming[:bytesRead])
			if bytes.HasSuffix(buffer.Bytes(), []byte("|")) {
				bb := bytes.Trim(buffer.Bytes(), "|")
				buffer.Reset()
				buffer.Write(bb)
				doBreak = true
			}
		}

		if doBreak {
			break
		}
	}
	return buffer
}

在我的情况下,如果我通过telnet连接(Go代码还包括一个client()函数用于连接到server()),然后输入类似于test 12345|的内容,一切都正常工作,缓冲区包含了从telnet写入的所有字节(除了通过Trim()调用删除的管道符号)。

如果我从代码中删除if bytes.HasSuffix(buffer.Bytes(), []byte("|")) {这一块,那么在2秒后我将会超时,这是预期的,因为在那段时间内没有接收到数据,服务器会关闭连接。如果我不设置连接的读取截止时间,它将永远等待读取数据,而且永远不知道何时停止。

我想问的是,如果我发送多个数据块,我是否需要指定自己的分隔符,以便知道何时停止从连接中读取数据,避免永远等待或等待服务器超时连接?

英文:

Not sure how to formulate the question and if it really relates only to go language, but what i am trying to do is to have a tcp server and client that will exchange data in between, basically the client will stream big amounts of data into smaller chunks to the server, the server will wait to read every chunk of data and then reply with a status code which will be read by the client and based on that it will do other work.

I use the function below as a test to read the data from client and server (please note, i am aware that is not perfect, but it's just testing) :

func createBufferFromConn(conn net.Conn) *bytes.Buffer {
	buffer := &bytes.Buffer{}
	doBreak := false
	for {
		incoming := make([]byte, BUFFER_SIZE)

		conn.SetReadDeadline(time.Now().Add(time.Second * 2))
		bytesRead, err := conn.Read(incoming)
		conn.SetReadDeadline(time.Time{})

		if err != nil {
			if err == io.EOF {
				fmt.Println(err)
			} else if neterr, ok := err.(net.Error); ok && neterr.Timeout() {
				fmt.Println(err)
			}
			doBreak = true
		}

		if doBreak == false && bytesRead == 0 {
			continue
		}

		if bytesRead > 0 {
			buffer.Write(incoming[:bytesRead])
			if bytes.HasSuffix(buffer.Bytes(), []byte("|")) {
				bb := bytes.Trim(buffer.Bytes(), "|")
				buffer.Reset()
				buffer.Write(bb)
				doBreak = true
			}
		}

		if doBreak {
			break
		}
	}
	return buffer
}

Now in my case if i connect via telnet(the go code also includes a client() to connect to the server()) and i type something like test 12345| fair enough everything works just fine and the buffer contains all the bytes written from telnet(except the pipe which is removed by the Trim() call).

If i remove the if bytes.HasSuffix(buffer.Bytes(), []byte("|")) { block from the code then i will get a timeout after 2 seconds, again, as expected because no data is received in that amount of time and the server closes the connection, and if i don't set a read deadline from the connection, it will wait forever to read data and will never know when to stop.

I guess my question is, if i send multiple chunks of data, do i have to specify a delimiter of my own so that i know when to stop reading from the connection and avoid waiting forever or waiting for the server to timeout the connection ?

答案1

得分: 4

是的。TCP是一种流协议,没有办法确定协议中的消息何时开始和结束,除非以某种方式对其进行分帧。

更常见的分帧方法是发送一个大小前缀,这样接收方就知道要读取多少数据,而不需要缓冲结果并扫描分隔符。这可以简单地表示为消息长度:数据...(参见netstringtype-length-value编码)。

英文:

> I guess my question is, if i send multiple chunks of data, do i have
> to specify a delimiter of my own so that i know when to stop reading
> from the connection and avoid waiting forever or waiting for the
> server to timeout the connection

Yes. TCP is a stream protocol, and there's no way to determine where messages within the protocol start and stop without framing them in some way.

A more common framing method used is to send a size prefix, so that the receiver knows how much to read without having to buffer the results and scan for a delimiter. This can be as simple as message_length:data.... (see also netstring, and type-length-value encoding).

huangapple
  • 本文由 发表于 2014年9月10日 21:04:11
  • 转载请务必保留本文链接:https://go.coder-hub.com/25766193.html
匿名

发表评论

匿名网友

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

确定