tcp粘包产生的原因这里就不说了,因为大家能搜索TCP粘包的处理方法,想必大概对TCP粘包有了一定了解,所以我们直接从处理思路开始讲起

tcp粘包现象代码重现

首先,我们来重现一下TCP粘包,然后再此基础之上解决粘包的问题,这里给出了client和server的示例代码如下

/*文件名:client.goclient客户端的示例代码(未处理粘包问题)通过无限循环无时间间隔发送数据给server服务器server将会不间断的出现TCP粘包问题
*/
package main
import ("fmt""net"
)
func main() {conn, err := net.Dial("tcp", ":9000")if err != nil {return}defer conn.Close()for {s := "Hello, Server!"n, err := conn.Write([]byte(s))if err != nil {fmt.Println("Error:", err)fmt.Println("Error N:", n)return}// 这里通过限制发送频率和时间间隔来解决TCP粘包// 虽然能够实现,但是频率被限制,效率也会被限制// time.Sleep(time.Second * 1)}
}
/*文件名:server.goserver服务端的示例代码(未处理粘包问题)服务端接收到数据后立即打印此时将会不间断的出现TCP粘包问题
*/
package main
import ("fmt""net"
)
func main() {ln, err := net.Listen("tcp", ":9000")if err != nil {return}for {conn, err := ln.Accept()if err != nil {continue}go handleConnection(conn)}
}
func handleConnection(conn net.Conn) {defer conn.Close()tmp := []byte{}for {buf := make([]byte, 1024)n, err := conn.Read(buf)if err != nil {fmt.Println("Read Error:", err)fmt.Println("Read N:", n)return}fmt.Println(string(buf))}
}

按顺序启动server.go和client.go,正常情况下每行会输出Hello, World!字样,出现TCP粘包后,将会出现类似Hello, World!Hello之类的字样,后一个包粘到前一个包了

解决TCP粘包有很多种方法,归结起来就是通过自定义通讯协议来解决,例如分隔符协议、MQTT协议、包长协议等等,而我们这里介绍的就是通过包长协议来解决问题的,当然包长协议也有很多种自定义的方法

通过演示的结果,我们可以看出来,后一个包粘到了前一个包,而且后一个包不一定是一个完整的包,也很有可能第一次收到的数据包也不是完整的数据包

tcp粘包问题处理方法

这样我们就有必要校验每次收到的数据包是否是我们期望收到的,比较直观的,客户端和服务端双方协商某种协议,例如包长协议,在客户端发送数据时,先计算一下数据的长度(假设用2字节的uint16表示),然后将计算得到的长度和实际的数据组装成一个包,最后发送给服务端;而服务端接收到数据时,先读取2字节的数据长度信息(可能不足2字节,程序需要针对这种情况设计),然后根据数据长度来读取后边的数据(可能会存在数据过剩、数据刚好、数据不足等情况,程序需要针对这些情况设计)

有了思路之后,我们就需要对发送端和接收端的数据进行处理了,因为发送端较为简单,不需要考虑其他情况,只管封装数据包发送,所以这里我们先对发送端client进行处理

/*文件名:client.go使用包长协议,封装TCP包并循环发送给server服务端
*/
package main
import ("encoding/binary""fmt""net"
)
func main() {conn, err := net.Dial("tcp", ":9000")if err != nil {return}defer conn.Close()for {s := "Hello, Server!"sbytes := make([]byte, 2+len(s))binary.BigEndian.PutUint16(sbytes, uint16(len(s)))copy(sbytes[2:], []byte(s))n, err := conn.Write(sbytes)if err != nil {fmt.Println("Error:", err)fmt.Println("Error N:", n)return}// time.Sleep(time.Second * 1)}
}

按照我们的思路,首先使用len()函数计算出待发送字符串的长度,然后使用make()函数创建一个[]byte切片作为待组装发送的数据包缓存sbyte,长度就是2字节的包头+字符串的长度,接着通过binary.BigEndian.PutUint16()函数来对数据包缓存sbyte进行操作,将字符串的长度信息写入2字节的包头中,紧接着又通过copy()完成封包组装,最后通过conn.Write()将封包发送出去,这样子发送出去的数据大概长成下面的样子

[0][14][H][e][l][l][o][,][ ][S][e][r][v][e][r][!]

其中,封包整体长16bytes,Hello, Server!则长14bytes

好了,至此数据将会循环不简短的发送给服务端,接下来我们就要对服务端server.go进行处理了,先上代码

/*文件名:server.go使用包长协议,处理接收到的封包数据收到的封包数据,可能存在几种情况:1、封包总长度不足2字节(这种情况不能完整获取包头),缓存起来与下次获取的数据拼接2、封包总长度刚好2字节,数据长度信息读出来是0,这种情况可以正常处理并清空缓存3、封包总长度大于2字节,数据长度信息大于封包数据实际长度,表示数据包不完整,需要等到下一次读取再拼接起来4、封包总长度大于2字节,数据长度信息等于封包数据实际长度,这种情况(理想情况)可以正常处理并清空缓存5、封包总长度大于2字节,数据长度信息小于封包实际长度,表示数据包发生TCP粘包了,读取实际数据后,将剩余部分缓存起来等待下次拼接PS:这里只总结出了这几种情况,其他未发现的情况还需另外处理
*/
package main
import ("encoding/binary""fmt""net"
)
func main() {ln, err := net.Listen("tcp", ":9000")if err != nil {return}for {conn, err := ln.Accept()if err != nil {continue}go handleConnection(conn)}
}
func handleConnection(conn net.Conn) {defer conn.Close()tmp := []byte{}for {buf := make([]byte, 1024)// fmt.Println("len:", len(buf), " cap:", cap(buf))n, err := conn.Read(buf)if err != nil {if e, ok := err.(*net.OpError); ok {fmt.Println(e.Source, e.Addr, e.Net, e.Op, e.Err)if e.Timeout() {fmt.Println("Timeout Error")}}fmt.Println("Read Error:", err)fmt.Println("Read N:", n)return}if n == 0 {fmt.Println("Read N:", n)return}tmp = append(tmp, buf[:n]...)length := len(tmp)if length < 2 {continue}if length >= 2 {head := make([]byte, 2)copy(head, tmp[:2])dataLength := binary.BigEndian.Uint16(head)data := make([]byte, dataLength)copy(data, tmp[2:dataLength+2])fmt.Println(string(data)) // 得到数据if uint16(length) == 2+dataLength {tmp = []byte{}} else if uint16(length) > 2+dataLength {tmp = tmp[dataLength+2:]}}// fmt.Println(string(buf))}
}

ps:这里的示例代码不能直接用于生产环境,只是提供tcp粘包处理的思路过程,代码还是存在一些问题的,例如server.go服务端还没有对第3种情况进行处理,封包总长度大于2字节,数据长度信息大于封包数据实际长度,表示数据包不完整,需要等到下一次读取再拼接起来

Golang通过包长协议处理TCP粘包相关推荐

  1. Netty(二)——TCP粘包/拆包

    转载请注明出处:http://www.cnblogs.com/Joanna-Yan/p/7814644.html 前面讲到:Netty(一)--Netty入门程序 主要内容: TCP粘包/拆包的基础知 ...

  2. TCP粘包和拆包原因

    最近研究Netty网络编程,以前项目中页遇到过数据接收过程中数据质量太差问题,很可能是TCP传输过程中问题,特此记录. 问题产生 一个完整的业务可能会被TCP拆分成多个包进行发送,也有可能把多个小的包 ...

  3. 【Netty入门】TCP 粘包/拆包问题产生原因

    TCP粘包/分包问题的由来 因为TCP是以流的方式来处理数据,一个完整的包可能会被TCP拆分成多个包进行发送,也可能把小的封装成一个大的数据包发送. 这样说可能比较抽象,下面举例来说明TCP拆包/粘包 ...

  4. Netty学习总结(5)——Netty之TCP粘包/拆包问题的解决之道

    无论是服务端还是客户端,读取或者发送消息的时候,都需要考虑TCP底层的粘包/拆包机制. TCP粘包/拆包 TCP是个"流"协议. 流:没有界限的一串数据.如同河里的流水,它们是连成 ...

  5. TCP——粘包/拆包

    TCP粘包/拆包 TCP是个"流"协议,所谓流,就是没有界限的一串数据.大家可以想想河里的流水,它们是连成一片的,其间并没有分界线.TCP底层并不了解上层业务数据的具体含义,它会根 ...

  6. TCP粘包/拆包问题

    目录 TCP粘包/拆包 TCP粘包/拆包问题说明 TCP粘包/拆包发生的原因 粘包问题的解决策略 未考虑TCP粘包导致功能异常案例  TimeServer的改造 TimeClient的改造 利用Lin ...

  7. TCP粘包、半包原理及解决方案

    引言:TCP协议是网络通信协议中十分重要的协议,相比于UDP协议来说,它是一个可靠的传输协议,并且是一个面向数据流的协议:所谓面向数据流,其实是指数据传输是以流式的方式传输,这些传输的数据就像一条河里 ...

  8. Netty权威指南(四)TCP粘包/拆包问题

    TCP粘包/拆包问题解决之道 上一章 一.介绍 1.1 TCP粘包/拆包问题说明 1.2 TCP粘包/拆包发生的原因 1.3 粘包问题的解决策略 二.未考虑TCP粘包导致的功能异常案例 2.1 Tim ...

  9. Netty详解(五):Netty TCP粘包 拆包

    1. 概述 无论是服务端还是客户端,我们读取或者发送消息的时候,都需要考虑TCP底层的粘包和拆包机制.下面我们来通过Netty来详解TCP底层的粘包和拆包机制. 2. TCP底层的粘包和拆包机制 TC ...

  10. 关于TCP粘包的拙见

    概述 本文主要概述TCP粘包的原因和如何解决TCP粘包的问题. TCP粘包原因 由于TCP是字节流传输协议,又没有保护边界,传输过程中为了提高传输效率,其采用了一种优化方式,将发送时间间隔小数据量小的 ...

最新文章

  1. java自动雨刷系统,安装雨量传感器实现自动大灯/自动雨刷(详细方法)多图!!
  2. spring storedProcedure 使用
  3. 《黑天鹅》读书笔记(part3)--那些声称注重过程而非结果的人并没有完全讲真话
  4. c# mysql ef框架_首页 C# EF6数据库第一-EF试图创建我的数据...
  5. 从结构到模块!华为提出最新两步搜索的目标检测SM-NAS
  6. javascript class constructor
  7. 「雅礼集训 2018 Day2」农民
  8. DHCP:(3)思科防火墙ASA上部署DHCP服务以及DHCP中继
  9. 手机功能测试主要测哪些方面?
  10. js去空格 回车 制表符 换页符
  11. 【转载】用Pwnage + Redsnow 制作完美越狱固件
  12. find函数的使用方法Matlab,matlab中find函数的使用说明——emily语法介绍
  13. 为什么可以做Shopyy独立站
  14. 【支付】银行卡支付的行为主体介绍
  15. HDU_1709 The Balence (生成函数)
  16. ios苹果机系统 的1px边框不显示
  17. 矢量数据shp七个文件介绍_读取矢量数据
  18. ipa文件包获取服务器地址,iOS获取App ipa包以及资源文件
  19. 浅谈STM32 USART串口中断配置函数USART_ITConfig()的编程思路
  20. 多个相机拍摄定位_多相机视觉系统的坐标系统标定与统一及其应用

热门文章

  1. SQL Server数据库应用与开发教程各章习题参考答案
  2. TranslateAnimation类:位置变化动画类 (类似tab切换效果)
  3. 如何用python制作五子棋
  4. ftdi usb转串口驱动
  5. Python利用结巴分词进行中文分词
  6. Lenovo y50-70 1080P 10.15.2 修改驱动 更新驱动
  7. 中国智慧建造行业投资前景分析与项目投资建议报告2021-2027年版
  8. 天九共享全方位孵化增添活力 助力独角兽企业开辟更多新大陆
  9. idea 因破解而无法打开的问题
  10. adobe illustrator 菜单中英文对照