全站年SVIP
全站1000+试题无限查看
什么是 TCP 粘包
粘包问题是指当发送两条消息时,比如发送了 ABC 和 DEF,但另一端接收到的却是 ABCD,像这种一次性读取了两条数据的情况就叫做粘包(正常情况应该是一条一条读取的), 正确读取 ABC 和 DEF 两条信息。
当发送的消息是 ABC 时,另一端却接收到的是 AB 和 C 两条信息,像这种情况就叫做半包。
为什么会有粘包和半包?
这是因为 TCP 是面向连接的传输协议,TCP 传输的数据是以流的形式,而流数据是没有明确的开始结尾边界,所以 TCP 也没办法判断哪一段流属于一个消息。
造成粘包的主要原因
发送方每次写入数据 < 套接字(Socket)缓冲区大小
接收方读取套接字(Socket)缓冲区数据不够及时。
造成半包的主要原因
发送方每次写入数据 > 套接字(Socket)缓冲区大小
发送的数据大于协议的 MTU (Maximum Transmission Unit,最大传输单元),因此必须拆包。
怎么处理粘包?
fix length 处理粘包
package frame_decoder import ( "fmt" "math/rand" "net" "week9/protocol" ) // ClientTcpFrameDecoder length field based frame decoder func ClientTcpFrameDecoder(conn net.Conn) { Log("client, length field based frame decoder") for i := 0; i < 10; i++ { userName := randStringRunes(6) words := "{\"Name\":\"" + userName + "20211217\",\"Meta\":\"golang\",\"Content\":\"message\"}" _, err := conn.Write(protocol.Packet([]byte(words))) if err != nil { fmt.Println(err, ",写入字符串错误 index=", i) return } fmt.Println(words) // 打印发送出去的信息 } Log("send over") } var letterRunes = []rune("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ") func randStringRunes(n int) string { b := make([]rune, n) for i := range b { b[i] = letterRunes[rand.Intn(len(letterRunes))] } return string(b) }
执行程序在 client server 目录
每次发送固定缓冲区大小数据。客户端和服务器约定每次发送请求的大小。例如客户端发送 1024 个字节,服务器接受 1024 个字节。
这样虽然可以解决粘包的问题,但是如果发送的数据小于 1024 个字节,就会导致数据内存冗余和浪费;且如果发送请求大于 1024 字节,会出现半包的问题,也就是数据接收的不完整。
delimiter based 处理粘包
package fix_length import ( "fmt" "net" ) func ServerTcpFixLength(server net.Conn) { fmt.Println("server fixed length") const BYTES = 1024 for { var buf = make([]byte, BYTES) _, err := server.Read(buf) if err != nil { fmt.Println(err) return } fmt.Println("client data: ", string(buf)) } }
执行程序在 client server 目录
基于定界符来判断是不是一个请求(例如结尾’\n’). 客户端发送过来的数据,每次以 \n 结束,服务器每接受到一个 \n 就以此作为一个请求。然后对其拆分后的头部部分与前一个包的剩余部分进行合并,这样就得到了一个完整的包。这种方式的缺点在于如果数据量过大,查找定界符会消耗一些性能
length field based frame decoder 处理粘包
在 TCP 协议头里面写入每次发送请求的长度。 客户端在协议头里面带入数据长度,服务器在接收到请求后,根据协议头里面的数据长度来决定接受多少数据,只有在读取到足够长度的消息之后才算是读到了一个完整的消息。之后会按照参数指定的包长度偏移量数据对接收到的数据进行解码,从而得到目标消息体数据。
什么是 TCP 粘包
什么是 TCP 粘包
粘包问题是指当发送两条消息时,比如发送了 ABC 和 DEF,但另一端接收到的却是 ABCD,像这种一次性读取了两条数据的情况就叫做粘包(正常情况应该是一条一条读取的), 正确读取 ABC 和 DEF 两条信息。
当发送的消息是 ABC 时,另一端却接收到的是 AB 和 C 两条信息,像这种情况就叫做半包。
为什么会有粘包和半包?
这是因为 TCP 是面向连接的传输协议,TCP 传输的数据是以流的形式,而流数据是没有明确的开始结尾边界,所以 TCP 也没办法判断哪一段流属于一个消息。
造成粘包的主要原因
发送方每次写入数据 < 套接字(Socket)缓冲区大小
接收方读取套接字(Socket)缓冲区数据不够及时。
造成半包的主要原因
发送方每次写入数据 > 套接字(Socket)缓冲区大小
发送的数据大于协议的 MTU (Maximum Transmission Unit,最大传输单元),因此必须拆包。
怎么处理粘包?
fix length 处理粘包
执行程序在 client server 目录
每次发送固定缓冲区大小数据。客户端和服务器约定每次发送请求的大小。例如客户端发送 1024 个字节,服务器接受 1024 个字节。
这样虽然可以解决粘包的问题,但是如果发送的数据小于 1024 个字节,就会导致数据内存冗余和浪费;且如果发送请求大于 1024 字节,会出现半包的问题,也就是数据接收的不完整。
delimiter based 处理粘包
执行程序在 client server 目录
基于定界符来判断是不是一个请求(例如结尾’\n’). 客户端发送过来的数据,每次以 \n 结束,服务器每接受到一个 \n 就以此作为一个请求。然后对其拆分后的头部部分与前一个包的剩余部分进行合并,这样就得到了一个完整的包。这种方式的缺点在于如果数据量过大,查找定界符会消耗一些性能
length field based frame decoder 处理粘包
执行程序在 client server 目录
在 TCP 协议头里面写入每次发送请求的长度。 客户端在协议头里面带入数据长度,服务器在接收到请求后,根据协议头里面的数据长度来决定接受多少数据,只有在读取到足够长度的消息之后才算是读到了一个完整的消息。之后会按照参数指定的包长度偏移量数据对接收到的数据进行解码,从而得到目标消息体数据。