分享
  1. 首页
  2. 文章

自定义协议和Golang实现

黄淑宁 · · 2185 次点击 · · 开始浏览
这是一个创建于 的文章,其中的信息可能已经有所发展或是发生改变。

自定义协议和Golang实现

写在这里一是做一下总结在忘的时候方便查看,二是如果文章有错误请各位大佬喷我哈哈,欢迎指正。如果能帮到别人也挺好的。

协议

所谓协议就是指定一系列规则,这些规则使想要交流的双方或多方可以正常通信交流。如我们说的汉语就是协议,如果不按照汉语规则说话,交流的人就听不懂对方说什么,汉语和英语就是不同的协议,用汉语和只会英语的人交流,人家也听不懂你说啥。在计算机中,入参出参是协议,最简单的服务端rest接口是协议,系统实现一层层的网络协议使计算机可以互相通信。

自定义协议

自定义协议就是在现有协议满足不了我们需求时,在现有协议之上构建的满足我们自己程序的通信需求的协议。目前的操作系统,会把我们应用需要的底层网络协议实现好,这对我们来说是透明的,但是在我们写rest时,是不是需要定义接口呢,这接口就是我们自定义的一种协议,通过定义请求参数,响应参数,让客户端和我们写好的服务端程序进行交互,这就是自定义的协议。

定义自定义协议

在TCP基础上自定义协议,主要就是通信双方规定一个头,头后面是传递的数据。头中规定一些参数,比如传递的消息前10个字节是头,头最前面2个字节是个固定魔数,让你知道我是根据这个协议来通信的,如果不支持这个协议的话,就可以直接关掉这个连接了。跟着魔数的4个字节是整个包中数据的长度,这个长度不包括头,需要长度的原因是TCP协议面向的是流,会把应用原本一个一个发送的包一股脑的扔给对方,如果不用长度字段,我们就很有可能拿到了多个包的数据。最后4个字节是个校验码,校验收到的包的数据是否有错误,主要是防止通信时被篡改。
此时我们就完成了我们自定义协议的定义。只需要发送端在将数据写入TCP前,在数据前面加入这个头,接收端接收到数据时,按照这个头的定义拿取数据,就可以完成双方的通信了。

GO中定义协议

自定义了一个Conn结构体,结构体中包含连接,元数据,scan是用来从真正的socket中获取数据,通过socket连接new一个scan,然后通过scan的split方法注册一个分割数据的函数,这里包装在了RegisterSplitFunc()函数中,scan会通过我们注册函数中的分割方式来分割数据,通过Scan()方法分割一次数据,获取返回数据使用scan.Bytes()

type Conn struct {
 c net.Conn
 meta metadata.Metadata
 scan *bufio.Scanner
}
func (c *Conn) Read() (b []byte, err error) {
 if c.c == nil {
 return nil, errors.New("conn is nil")
 }
 if c.scan == nil {
 //包装过的函数,做了一个scan.Split(splitFunc)的操作
 c.RegisterSplitFunc()
 }
 if c.scan.Scan() {
 b = c.scan.Bytes()
 } else {
 c.c.Close()
 err = errors.New("conn scan error")
 }
 return
}
//write方法中的头由模数0x0102, 长度len(data)+4组成
//头后面跟着数据
func (c *Conn) Write(data []byte) (err error) {
 if len(data) > math.MaxUint16 {
 return errors.New("data too big")
 }
 buf := bytes.Buffer{}
 binary.Write(&buf, binary.BigEndian, []byte{0x01,0x02})
 binary.Write(&buf, binary.BigEndian, uint16(len(data)+4))
 binary.Write(&buf, binary.BigEndian, data)
 _, err = c.c.Write(buf.Bytes())
 return
}

分割函数

func MySplitFunc(data []byte, atEOF bool) (advance int, token []byte, err error) {
 //判断长度,魔数
 if len(data) < 4 || data[0] != 0x01 || data[1] != 0x02 {
 err = errors.New("protocol error")
 return
 }
 var l uint16
 //获取header
 err = binary.Read(bytes.NewReader(data[2:4]), binary.BigEndian, &l)
 if err != nil {
 logs.Error("binary.Read error(%v)", err)
 return
 }
 //通过长度读取数据,advance为读取的长度,包括头和数据,data是读取的数据
 if int(l) <= len(data) {
 advance, token, err = int(l), data[:int(l)], nil
 }
 if atEOF {
 err = errors.New("EOF")
 }
 return
}

总结

以上为在golang中自定义简单通信协议的方式。


有疑问加站长微信联系(非本文作者)

本文来自:Segmentfault

感谢作者:黄淑宁

查看原文:自定义协议和Golang实现

入群交流(和以上内容无关):加入Go大咖交流群,或添加微信:liuxiaoyan-s 备注:入群;或加QQ群:692541889

关注微信
2185 次点击
暂无回复
添加一条新回复 (您需要 后才能回复 没有账号 ?)
  • 请尽量让自己的回复能够对别人有帮助
  • 支持 Markdown 格式, **粗体**、~~删除线~~、`单行代码`
  • 支持 @ 本站用户;支持表情(输入 : 提示),见 Emoji cheat sheet
  • 图片支持拖拽、截图粘贴等方式上传

用户登录

没有账号?注册
(追記) (追記ここまで)

今日阅读排行

    加载中
(追記) (追記ここまで)

一周阅读排行

    加载中

关注我

  • 扫码关注领全套学习资料 关注微信公众号
  • 加入 QQ 群:
    • 192706294(已满)
    • 731990104(已满)
    • 798786647(已满)
    • 729884609(已满)
    • 977810755(已满)
    • 815126783(已满)
    • 812540095(已满)
    • 1006366459(已满)
    • 692541889

  • 关注微信公众号
  • 加入微信群:liuxiaoyan-s,备注入群
  • 也欢迎加入知识星球 Go粉丝们(免费)

给该专栏投稿 写篇新文章

每篇文章有总共有 5 次投稿机会

收入到我管理的专栏 新建专栏