gnet icon indicating copy to clipboard operation
gnet copied to clipboard

高并发下gnet服务端接收报文错乱

Open nkypy opened this issue 3 years ago • 32 comments

Describe the bug 在2000个连接的情况下,发送大数据,比如64k的数据,React函数里面的frame会偶发出现数据不正确的情况。

To Reproduce Steps to reproduce the behavior: 最简单的客户端,和服务端。只在React函数部分增加检查数据一致性的操作 image

Expected behavior 发送客户端同样的数据,server端应该解析出一样的数据

Screenshots 正确情况下,前20位应该是 abcdefghijklmnopqrst 错误数据如下图 image

System Info (please complete the following information):

  • OS (e.g. Ubuntu 18.04): Windows 10
  • Go version (e.g. Go 1.13): Go 1.16.3
  • gnet version (e.g. v1.0.0): v1.2.3

Additional context Add any other context about the problem here.

nkypy avatar May 10 '21 10:05 nkypy

Thanks for opening a new issue. The team has been notified and will review it as soon as possible. For urgent issues and priority support, visit https://xscode.com/panjf2000/gnet

xscode-auto-reply[bot] avatar May 10 '21 10:05 xscode-auto-reply[bot]

tcp是流式数据,需要处理粘包的情况

yangjuncode avatar May 11 '21 03:05 yangjuncode

tcp是流式数据,需要处理粘包的情况

如果使用ants协程池,异步调用,怎么处理粘包和半包的情况呢

vlaualv avatar May 11 '21 03:05 vlaualv

异步调用和TCP分包没关系,你有在 gnet 里实现自己编解码器吗?

panjf2000 avatar May 11 '21 04:05 panjf2000

异步调用和TCP分包没关系,你有在 gnet 里实现自己编解码器吗?

有自己实现 codec,大致代码我贴在 Go Playground

nkypy avatar May 11 '21 05:05 nkypy

Decode 里最后的 body 数据怎么没调用 binary.Read() ?

panjf2000 avatar May 16 '21 04:05 panjf2000

Decode 里最后的 body 数据怎么没调用 binary.Read() ?

Decode 里面 body 数据 直接通过第 17 行 dataSize, data := c.ReadN(bodyLen) 取得 data 了

nkypy avatar May 17 '21 03:05 nkypy

	if err := binary.Write(buffer, binary.LittleEndian, data); err != nil {
		s := fmt.Sprintf("Pack data error , %v", err)
		return nil, errors.New(s)
	}

你这一段的目的是什么?

panjf2000 avatar May 17 '21 05:05 panjf2000

	if err := binary.Write(buffer, binary.LittleEndian, data); err != nil {
		s := fmt.Sprintf("Pack data error , %v", err)
		return nil, errors.New(s)
	}

你这一段的目的是什么?

这段忘改了,和直接Write效果一样。大小端对 []byte 没作用。数据错乱不是编解码这边的问题,我测试过固定长度大数据在大并发下,编解码内部不做处理,直接返回固定长度的数据,也是有数据错乱情况。

nkypy avatar May 17 '21 08:05 nkypy

你有没有在 linux 上测试过?是只有 windows 有这种情况还是 linux 也会出现?

panjf2000 avatar May 17 '21 08:05 panjf2000

你有没有在 linux 上测试过?是只有 windows 有这种情况还是 linux 也会出现?

Windows 10 和 WSL 下测试都会出现,mac 和纯 linux 主机下没测试过。

nkypy avatar May 17 '21 09:05 nkypy

这个问题最近我也遇到了,目前处理方式是自定义一个byte数组,同步的方式拷贝过来,就没有出现数据重复了,如下: Uploading image.png…

lsm1998 avatar May 18 '21 06:05 lsm1998

demo代码片段:

func (h *UdpHandler) React(frame []byte, c gnet.Conn) (out []byte, action gnet.Action) {

	data := make([]byte, len(frame))
	copy(data, frame)

	go func(frame []byte) {
		time.Sleep(100 * time.Millisecond)
		fmt.Printf("%p - %v \n", &frame, frame)
	}(data)
	return
}

lsm1998 avatar May 18 '21 06:05 lsm1998

嗯,如果只在 React 函数里处理数据而没有启动新的 goroutine 的话,则可以不用复制数据,否则的话则需要 copy 一份传给 goroutine,不然就有可能会有数据错乱的问题,因为 gnet 底层是共享同一段 []byte 的。 @nkypy 确认下是不是这个原因

panjf2000 avatar May 18 '21 11:05 panjf2000

pan神,遇到类似的问题,不过是客户端之间请求序号错乱了。。。而且只是用了框架自身的带的协议。 image image 300个客户端,每次发送16KB,会出现几次错误 image 客户端是windows ide 服务端部署在linux上的

huzhao37 avatar May 18 '21 13:05 huzhao37

看起来你们俩都是参考的 custom_codec 这个例子,这个例子可能写的有点问题,这个是别人贡献的,我当时也没细看,我这两天抽空看看。

panjf2000 avatar May 18 '21 13:05 panjf2000

好的,pan神,我也感觉这个编解码有点不对劲,测试8kb,16kb都是偶现且会出现在两台机器上

huzhao37 avatar May 18 '21 14:05 huzhao37

嗯,如果只在 React 函数里处理数据而没有启动新的 goroutine 的话,则可以不用复制数据,否则的话则需要 copy 一份传给 goroutine,不然就有可能会有数据错乱的问题,因为 gnet 底层是共享同一段 []byte 的。 @nkypy 确认下是不是这个原因

之前我是没有起新的 goroutine 处理数据出现的问题。按照 lsm1998 的方法,copy数据,起新的 goroutine 处理数据,依旧存在问题。

nkypy avatar May 20 '21 07:05 nkypy

请教下。Decode 返回的 []byte 有没有可能 被串改掉? // Decode ... func (cc *BuiltInFrameCodec) Decode(c Conn) ([]byte, error) { ....buf := c.Read() ....if len(buf) == 0 { ........return nil, nil ....} ....c.ResetBuffer() //2.如果这里释放了(bytebuffer.Put(c.byteBuffer) ) ....return buf, nil //3.如果byteBuffer 被其它 连接使用了,会不会造成 buf 的内容被改变? }

//=========================== func (c *conn) Read() []byte { ....if c.inboundBuffer.IsEmpty() { ........return c.buffer ....} ....c.byteBuffer = c.inboundBuffer.WithByteBuffer(c.buffer) ....return c.byteBuffer.Bytes() //1.这里返回的是 c.byteBuffer } func (c *conn) ResetBuffer() { ....c.buffer = c.buffer[:0] ....c.inboundBuffer.Reset() ....bytebuffer.Put(c.byteBuffer) ....c.byteBuffer = nil }

wumao avatar May 26 '21 13:05 wumao

潘神,,解码demo有结果了吗?高并发始终会出现 image Reactor之后数据变了,但是Reactor里面什么都没有做,就是out=frame return
image

huzhao37 avatar Jun 07 '21 10:06 huzhao37

补充下:Reactor() image

huzhao37 avatar Jun 07 '21 10:06 huzhao37

最近太忙了,还没细看。

panjf2000 avatar Jun 07 '21 15:06 panjf2000

你这个就太诡异了,你在 React() 里也打印一下看看呢?

panjf2000 avatar Jun 07 '21 15:06 panjf2000

问题已解决,之前为了支持tls,把linux下的epoll代码去掉了,只用了windows代码,然后在linux测试出现这种并发问题。再次加上就没有了,但是带来了新的问题,epoll无法支持tls。。。潘神有tls计划吗?是否需要参考py bio或者c++版本实现tls拦截验证

huzhao37 avatar Jun 09 '21 02:06 huzhao37

你到底是在什么操作系统上跑出问题来的?

panjf2000 avatar Jun 09 '21 04:06 panjf2000

Linux 上用户自己控制好分包组包的逻辑应该不会有数据错乱的问题,现在 gnet 已经跑在不少线上环境了,一般来说不太可能出现这个问题。

panjf2000 avatar Jun 09 '21 04:06 panjf2000

windows的代码跑在linux上面出现的

huzhao37 avatar Jun 09 '21 06:06 huzhao37

image

红线处的两次shiftN,如果出现粘包,后面数据就读不出来了,第一次shiftn把协议头部分清掉了,下次再decode就没有协议头了

bwb0101 avatar Jul 20 '21 09:07 bwb0101

image

红线处的两次shiftN,如果出现粘包,后面数据就读不出来了,第一次shiftn把协议头部分清掉了,下次再decode就没有协议头了

你为啥要两次shiftN?正常一次就够了,只需要最后ShiftN(msgLen)

huzhao37 avatar Jul 20 '21 09:07 huzhao37

他说的是 @nkypy 的问题,这里的 codec 这么写的确有隐患。

panjf2000 avatar Jul 20 '21 09:07 panjf2000

这可能就破案了。

einsitang avatar Feb 09 '22 10:02 einsitang