go语言处理TCP拆包/粘包的具体实现

 更新时间:2021年12月09日 15:43:09   作者:熊纪元  
TCP的拆包/粘包也算是网络编程中一个比较基础的问题了,本文主要介绍了go语言处理TCP拆包/粘包,文中通过示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下

part 1

最近在学习go自带的rpc,看完了一遍想着自己实现一个codec,也就是自定义消息的序列化和反序列化。消息的序列化和反序列化涉及到两步:

1、从网络中读取数据和将数据写到网络中;

2、根据拿到的二进制数据反序列化以及把现有的对象序列化成二进制数据。而这个过程中就需要处理TCP的拆包粘包了。

TCP的拆包/粘包也算是网络编程中一个比较基础的问题了,具体的问题含义和解决方式也不再详细描述了。虽然作为实现应用层逻辑的程序员可能根本不需要关心这个,作为中间件的开发,同时也出于学习go语言的目的,还是稍微进行一下练习。

part 2

TCP拆包粘包的解决思路:在读取数据时,将读取到的二进制数据在正确的位置进行分割即可。这里直接使用head+body的方式,也就是在发送数据时先将整个数据的大小附加在数据前,就像这样:

+++++++++++++++++++++++++++++++++++++
size (2 bytes)  | body (size bytes)
+++++++++++++++++++++++++++++++++++++

这里将数据包的size作为head
注:这里举的例子里,size占2个byte;size具体占用的byte数可以根据实际情况决定

part 3

server具体实现:

func doConn(conn net.Conn) {
    var (
        buffer = bytes.NewBuffer(make([]byte, 0, BUF_SIZE)) //buffer用来缓存读取到的数据
        readBytes = make([]byte, BUF_SIZE) //readBytes用来接收每次读取的数据,每次读取完成之后将readBytes添加到buffer中
        isHead = true //用来标识当前的状态:正在处理size部分还是body部分
        bodyLen = 0 //表示body的长度
    )

    for {
        //首先读取数据
        readByteNum, err := conn.Read(readBytes)
        if err != nil {
            log.Fatal(err)
            return
        }
        buffer.Write(readBytes[0:readByteNum])//将读取到的数据放到buffer中
        
        // 然后处理数据
        for {
            if isHead {
                if buffer.Len() >= HEAD_SIZE {
                    isHead = false
                    head := make([]byte, HEAD_SIZE)
                    _, err = buffer.Read(head)
                    if err != nil {
                        log.Fatal(err)
                        return
                    }
                    bodyLen = int(binary.BigEndian.Uint16(head))
                } else {
                    break;
                }
            }

            if !isHead {
                if buffer.Len() >= bodyLen {
                    body := make([]byte, bodyLen)
                    _, err = buffer.Read(body[:bodyLen])
                    if err != nil {
                        log.Fatal(err)
                        return
                    }
                    fmt.Println("received body: " + string(body[:bodyLen]))
                    isHead = true
                } else {
                    break;
                }
            }
        }
    }
    
    func HandleTcp() {
 listener, err := net.Listen("tcp", ":1234")
 if err != nil {
  log.Fatal(err)
  return
 }
 log.Println("start listening on 1234")
 for {
  conn, err := listener.Accept()
  if err != nil {
   log.Fatal(err)
   return
  }
  go doConn(conn)
 }
}

client具体实现:

func SendStringwithTcp(arg string) error {
 conn, err := net.Dial("tcp", ":1234")
 if err != nil {
  log.Fatal(err)
  return err
 }

 head := make([]byte, server.HEAD_SIZE)
 content := []byte(arg)
 headSize := len(content)
 binary.BigEndian.PutUint16(head, uint16(headSize))

    //先写入head部分,再写入body部分
 _, err = conn.Write(head)
 if err != nil {
  log.Fatal(err)
  return err
 }
 _, err = conn.Write(content)
 if err != nil {
  log.Fatal(err)
  return err
 }
 return nil
}

到此这篇关于go语言处理TCP拆包/粘包的文章就介绍到这了,更多相关go语言 TCP拆包/粘包内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

  • go语言分布式id生成器及分布式锁介绍

    go语言分布式id生成器及分布式锁介绍

    这篇文章主要为大家介绍了go语言分布式id生成器及分布式锁介绍,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪
    2023-04-04
  • Go语言中的字符串处理方法示例详解

    Go语言中的字符串处理方法示例详解

    Go语言的字符串是使用UTF-8编码的。UTF-8是Unicode的实现方式之一。这篇文章主要介绍了Go语言中的字符串处理方法,需要的朋友可以参考下
    2018-10-10
  • 浅谈golang中的&^位清空操作

    浅谈golang中的&^位清空操作

    这篇文章主要介绍了浅谈golang中的&^位清空操作,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧
    2020-12-12
  • go语言入门环境搭建及GoLand安装教程详解

    go语言入门环境搭建及GoLand安装教程详解

    这篇文章主要介绍了go语言入门环境搭建及GoLand安装教程详解,需要的朋友可以参考下
    2020-12-12
  • golang time常用方法详解

    golang time常用方法详解

    这篇文章主要介绍了golang time常用方法详解,本篇文章通过简要的案例,讲解了该项技术的了解与使用,以下就是详细内容,需要的朋友可以参考下
    2021-08-08
  • 一文了解Go语言中的函数与方法的用法

    一文了解Go语言中的函数与方法的用法

    与大部分语言一致,Go语言中的函数与方法定义与其他语言基本一致,但也有一定的差别。本文将通过示例详细讲讲Go语言中函数与方法的用法,感兴趣的可以学习一下
    2022-07-07
  • 深入理解Go语言的容器包

    深入理解Go语言的容器包

    Go语言的container标准包包含了堆(heap)、链表(list)和环(ring)三种数据结构,本文就来详细的介绍一下这三种的使用,感兴趣的可以了解一下
    2024-10-10
  • Go语言学习之new函数的用法详解

    Go语言学习之new函数的用法详解

    这篇文章主要为大家详细介绍了Go语言中new()函数的相关知识以及具体用法,文中的示例代码讲解详细,具有一定的学习价值,感兴趣的小伙伴可以了解一下
    2023-05-05
  • Redis BloomFilter布隆过滤器原理与实现

    Redis BloomFilter布隆过滤器原理与实现

    你在开发或者面试过程中,有没有遇到过 海量数据需要查重,缓存穿透怎么避免等等这样的问题呢?下面这个东西超棒,好好了解下,面试过关斩将,凸显你的不一样
    2022-10-10
  • Golang控制通道实现协程等待详解

    Golang控制通道实现协程等待详解

    这篇文章主要介绍了Golang控制通道实现协程等待,通道是Go语言程序的并发体goroutine是它们之间的通信机制。一个通道是一个通信机制,它可以让一个goroutine通过它给另一个goroutine发送值信息。每个通道都有一个特殊的类型,也就是channels可发送数据的类型
    2022-11-11

最新评论