碰到一个奇怪的问题,在windows下socket.Read读取的长度大于传入BUFF的长度

image.png
我传入的BUFF len和cap都是4。结果返回值是27。。。

共 20 个回复


DRYGGJS

请问解决了吗,很好奇原因。。

# 0

yaohong

没有解决。。。。

# 1

me@bzza.com

io.ReadFull本来意思就是读完所有的意思,直到遇上出错或读取的内容长度>=len(buf)

func ReadAtLeast(r Reader, buf []byte, min int) (n int, err error) {
	if len(buf) < min {
		return 0, ErrShortBuffer
	}
	**for n < min && err == nil {
		var nn int
		nn, err = r.Read(buf[n:])
		n += nn
	}**
	if n >= min {
		err = nil
	} else if n > 0 && err == EOF {
		err = ErrUnexpectedEOF
	}
	return
}

传过去的io.Reader中的Read方法是你定义的,如何read,read多少都是你定义的,修改你的read

重要的内容说三次:Read方法是你定义的,Read方法是你定义的,Read方法是你定义的。

# 2

yaohong

这里的Read是TcpConn实现的(也是系统库)。。不是我自己实现的。。

# 3

HobaiRiku

看明白了又没看太明白二楼的意思,但是看了golang的ReadFull文档以及在example中试了一下,在strings.NewReader()下,n是不会超出len(buf)的,所以继续看文档的,当你把src/net/net.go180行左右的代码:

// Read implements the Conn Read method.
func (c *conn) Read(b []byte) (int, error) {
	if !c.ok() {
		return 0, syscall.EINVAL
	}
	n, err := c.fd.Read(b)
	if err != nil && err != io.EOF {
		err = &OpError{Op: "read", Net: c.fd.net, Source: c.fd.laddr, Addr: c.fd.raddr, Err: err}
	}
	return n, err
}

和二楼贴出的ReadFull调用的ReadAtLeast的源码(src/io/io.go 304行左右),你就会发现,ReadFull文档中的这句话就是现在你的这种情况:

On return, n == len(buf) if and only if err == nil. If r returns an error having read at least len(buf) bytes, the error is dropped.

也就是说,ReadFull会读取io.Reader中的所有数据,即使给的buf长度小于数据大小,其实这时候读取数据是已经出错的,但当已经读满了buf长度,被err=nil了,也就出现n=27,err=nil的情况。tcpConn的read一下子直接读了27,而像stringReader是用copy(),返回的n不会大于buf长度。
不知道分析得对不对。

# 4

yaohong

感觉都被带偏了。。重点不是io.ReadFull
我在发下截图吧
bug截图.png
是TcpConn.Read 读取的返回长度大于传入BUFF的长度。

# 5

yaohong

传入BUFF的长度是4
而TCPConn.Read返回的n是28

# 6

yaohong

这个是调试器断点的截图

# 7

HobaiRiku

我不确定你这个self.conn转换为*TCPConn是什么个意思,的确TCPConn经过一系列io的Read后,不会在一次处理超过buf长度的数据,所以你自己可以断点调试一下到底这个Read走的是哪里,也许实际走的Read是不一样的呢?还是二楼的那句话,Read是自己实现的,我觉得其实n和buf的长度没有直接关系,并没有规定n一定不能大于buf的长度。

# 8

yaohong

这个self.conn 就是net.Conn 这是为了直观可以一眼就知道是TCPConn。
这个read是 官方库自带的。。。

# 9

yaohong

在强调一下。。
Read不是我自己实现的。是net.TcpConn 的方法

# 10

yaohong

问题就是系统库自带的
tcpConn.Read 读取的数据长度大于传入BUFF的长度
我传入的len(buff)=4 ,结果告诉我返回了28

# 11

HobaiRiku

你不用强调什么,如果的就是Conn接口,你断点到源码调试就知道为什么会一次读取28了,我自己简单断点看到是没走超过buf长度的,超过会保留到下一次read,但底层conn和poll、io之类的我也看不太懂,所以也不能说出个什么。

# 12

yaohong

我这个并不是100%出现。。
是在做压力测试的时候才偶先。。。。

# 13

yaohong

就什么办法能调试到源码

# 14

yaohong

4344233 楼上的大佬

# 15

DRYGGJS

兄弟留个联系方式,一起解决,比较好奇这种问题
我qq 2667350913

# 16

DRYGGJS

又要2个月才看消息吗。。

# 17

rocket

我觉得楼主可以用 bufio 防止这个错误。

# 18

xigualala

这个self.conn转换为*TCPConn是什么个意思,的确TCPConn经过一系列上海快3io的Read后,不会在一次处理超过buf长度的数据,所以你自己可以断点调试一下到底这个Read走的是哪里,也许实际走的Read是不一样的呢?还是二楼的幸运飞艇 那句话,Read是自己实现的,我觉得其实n和buf的长度没有直接关系,并没有规定n一定不能大于buf的长度。

# 19