直接上代码
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 条左右,丢数据的原因是什么,目前我怀疑是接收性能不足,在向通道传数据的过程中第二条数据就过来了,导致第二条数据直接被丢弃
1
codehz 2020-12-09 10:20:33 +08:00 via Android 2
粘包警察👮预警
|
2
misaka19000 2020-12-09 10:24:32 +08:00
你的想法是错误的,OS 会把未读的数据放在缓冲区
|
3
misaka19000 2020-12-09 10:25:51 +08:00
此外,没懂你的「 tcpdump 抓取报文有 200 条」是什么意思,建议把另一端的代码也放出来
|
4
chazyu1996 2020-12-09 10:29:55 +08:00
tcp 基于字节流,没有包的概念吧
|
6
zunceng 2020-12-09 10:33:08 +08:00
err == EOF n != 0 的情况处理了么
|
7
no1xsyzy 2020-12-09 10:34:13 +08:00
请提供完整可复现的代码
|
8
rochek 2020-12-09 10:35:40 +08:00
你的 buffer 是 1448 。
所以有可能一个 tcp 段由 2 个 ip 片承载。 建议先了解一下基本网络原理,再考虑抓包之类的事情。 写网络的化建议先从简单的问题开始。 |
9
BingoXuan 2020-12-09 10:39:47 +08:00 1
不要看包數量,看字節數,tcp 每一個數據包大小都不是固定的。
用的都是同一段 buf 接收數據並通過 chan 轉發我記得是有問題的。 |
10
server 2020-12-09 10:40:22 +08:00
用 gnet 吧 https://github.com/panjf2000/gnet
|
11
icexin 2020-12-09 10:40:39 +08:00 1
你复用了 buffer,新数据过来之后老的 buffer 就被覆盖了。拷贝一份 buffer 发送到 channel 里面。
|
12
labulaka521 2020-12-09 10:45:28 +08:00
@icexin 这个不需要的 复用 buffer 是可以的
|
13
icexin 2020-12-09 10:49:37 +08:00 1
@labulaka521 在一个 goroutine 里面复用是没问题的,但楼主把数据发送到 channel 里面,在另外一个 goroutine 里面处理,两者会发生不同步,就会产生消费 goroutine 没处理完,新的数据又写入到 buffer 里面了。即使不考虑 buffer 覆盖问题,两个 goroutine 同时访问一块内存也会出现数据竞争。
|
14
JackieChoi 2020-12-09 10:56:57 +08:00
不懂 go,但是 tcpdump 的数据 200 条,是在网卡抓取的数据,和应用层的数据并不是完全对应的。
程序收到的是协议栈处理完全后的程序,而 tcpdump 里可能有未被丢弃的超时重传包、未被合并的分包等等其他数据包。 建议算字节数,应用层接收的字节数,理论上等于最后一个 tcp 的 seq - 第一个 tcp 包的 seq + 最后一个 tcp 的 datalen |
15
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 |
17
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) ``` |
18
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 里面了。 |
19
djoiwhud 2020-12-09 11:36:22 +08:00
dataChanel <- buffer[:n]
这行代码,看起来,dataChanel 里面保存的 buffer 地址指向的数据会被后面的数据覆盖。你需要重新 new 一个切片再丢进 channel 。大半年没写 go 了,有些遗忘,最好测试一下我这个说法。 ps ,你的 chanel 拼写是错的。 功底有点差。是学生? |
21
Peakday OP 感谢大家的回复!我再去学习学习
|
22
djoiwhud 2020-12-09 11:58:44 +08:00
你对 tcpdump 使用的也有问题。tcpdump 这类工具不是给你数报文条数的。
你应该逐 bit 分析通信协议代码是否有 bug,tcpdump 保存数据到 wireshark 分析,或者直接用 wireshark 。发收方的 tcpdump 的数据串是一致的,通常有一方代码有 bug,应用层处理出错了。 |
23
namaketa 2020-12-09 13:27:05 +08:00
也在学习 go 语言。
目前来看 lz 主要的问题是直接把数据缓存完之后直接传了个 slice 。 而 slice 本质就是一个指针,后续还进行了并发操作,导致指针引用的对象变化了。 可以了解一下深拷贝浅拷贝的概念。 |
24
gesse 2020-12-09 15:50:24 +08:00
tcp 是基于字节流、而不是基于消息包的协议。比如 tcpdump 抓到两个 IP 包,包含两个 tcp 数据,但是这两个数据可以在运输层被一次性 copy 到应用层。
|