go 处理 tcp 长连接丢失数据原因是什么

2020-12-09 10:17:18 +08:00
 Peakday

直接上代码

listen, err := net.Listen("tcp", cfg.Network.IpP)
if err != nil {
	panic("can't listen port!")
}

for {
	conn, err := listen.Accept()
	if err != nil {
		fmt.Println(err)
		continue
	}

	go func(cn net.Conn) {
		buffer := make([]byte, 1448)
		for {
			n, err := cn.Read(buffer)
			if err != nil {
				fmt.Println("tcp read error\t", err)
				continue
			}
			//丢数据问题待解决
			dataChanel <- buffer[:n]
		}
	}(conn)

使用 tcpdump 抓取报文有 200 条,但是程序只收到 190 条左右,丢数据的原因是什么,目前我怀疑是接收性能不足,在向通道传数据的过程中第二条数据就过来了,导致第二条数据直接被丢弃

2901 次点击
所在节点    Go 编程语言
24 条回复
codehz
2020-12-09 10:20:33 +08:00
粘包警察👮预警
misaka19000
2020-12-09 10:24:32 +08:00
你的想法是错误的,OS 会把未读的数据放在缓冲区
misaka19000
2020-12-09 10:25:51 +08:00
此外,没懂你的「 tcpdump 抓取报文有 200 条」是什么意思,建议把另一端的代码也放出来
chazyu1996
2020-12-09 10:29:55 +08:00
tcp 基于字节流,没有包的概念吧
nadoo
2020-12-09 10:32:54 +08:00
应该看发送了多少字节数据,接收了多少字节数据
zunceng
2020-12-09 10:33:08 +08:00
err == EOF n != 0 的情况处理了么
no1xsyzy
2020-12-09 10:34:13 +08:00
请提供完整可复现的代码
rochek
2020-12-09 10:35:40 +08:00
你的 buffer 是 1448 。
所以有可能一个 tcp 段由 2 个 ip 片承载。

建议先了解一下基本网络原理,再考虑抓包之类的事情。
写网络的化建议先从简单的问题开始。
BingoXuan
2020-12-09 10:39:47 +08:00
不要看包數量,看字節數,tcp 每一個數據包大小都不是固定的。

用的都是同一段 buf 接收數據並通過 chan 轉發我記得是有問題的。
server
2020-12-09 10:40:22 +08:00
icexin
2020-12-09 10:40:39 +08:00
你复用了 buffer,新数据过来之后老的 buffer 就被覆盖了。拷贝一份 buffer 发送到 channel 里面。
labulaka521
2020-12-09 10:45:28 +08:00
@icexin 这个不需要的 复用 buffer 是可以的
icexin
2020-12-09 10:49:37 +08:00
@labulaka521 在一个 goroutine 里面复用是没问题的,但楼主把数据发送到 channel 里面,在另外一个 goroutine 里面处理,两者会发生不同步,就会产生消费 goroutine 没处理完,新的数据又写入到 buffer 里面了。即使不考虑 buffer 覆盖问题,两个 goroutine 同时访问一块内存也会出现数据竞争。
JackieChoi
2020-12-09 10:56:57 +08:00
不懂 go,但是 tcpdump 的数据 200 条,是在网卡抓取的数据,和应用层的数据并不是完全对应的。
程序收到的是协议栈处理完全后的程序,而 tcpdump 里可能有未被丢弃的超时重传包、未被合并的分包等等其他数据包。
建议算字节数,应用层接收的字节数,理论上等于最后一个 tcp 的 seq - 第一个 tcp 包的 seq + 最后一个 tcp 的 datalen
hasdream
2020-12-09 10:58:40 +08:00
如果是在一个局域网的话:
1. 网卡 mtu 是多少
2. 发送的数据是多少 (mtu 一般为 1500 tcp payload 一般为 66byte 后 payload 大于(mtu - (66-12) = payload 最大值) 如果超过 payload 最大值就会自动分片为下一个 packet)
如果就在本机测试那没有 1500 的限制,lo MTU 为 65535
keepeye
2020-12-09 11:00:35 +08:00
@icexin +1 特别需要注意这点
gamexg
2020-12-09 11:05:49 +08:00
buffer 的内容被覆盖了
改成这样:

```
go func(cn net.Conn) {
for {
buffer := make([]byte, 1448)
n, err := cn.Read(buffer)
if err != nil {
fmt.Println("tcp read error\t", err)
continue
}
//丢数据问题待解决
dataChanel <- buffer[:n]
}
}(conn)

```
djoiwhud
2020-12-09 11:29:09 +08:00
不知道是练手的 demo 还是给企业用。这代码不全,而且看起来问题很多。

tcpdump 检测的是 os 层级的接收写入到 tcp rec buffer window 的操作,实际上 cn.Read(buffer) 作为应用层代码,这种代码是从 tcp rec buffer window 读取数据。两者不一致是可能的。

“目前我怀疑是接收性能不足,在向通道传数据的过程中第二条数据就过来了,导致第二条数据直接被丢弃”

你这个说法表明你没有理解网络。个人建议你先看看计算机网络卷 1

发送方可以在一秒钟内发 10 次数据,总共发几千字节,而你这边可以用 cn.Read(buffer) 一次全部读出来,只要 buffer 足够大,你不读,数据也在 tcp rec buffer window 里面了。
djoiwhud
2020-12-09 11:36:22 +08:00
dataChanel <- buffer[:n]

这行代码,看起来,dataChanel 里面保存的 buffer 地址指向的数据会被后面的数据覆盖。你需要重新 new 一个切片再丢进 channel 。大半年没写 go 了,有些遗忘,最好测试一下我这个说法。

ps ,你的 chanel 拼写是错的。

功底有点差。是学生?
Peakday
2020-12-09 11:49:11 +08:00
@djoiwhud 不是学生,第一次写,感谢您的解答

这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。

https://www.v2ex.com/t/733626

V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。

V2EX is a community of developers, designers and creative people.

© 2021 V2EX