您的位置:首页 > 理论基础 > 计算机网络

从零开始写Go网络通信框架(2)——自定义通讯协议

2017-06-19 18:57 411 查看
在Server和Client通讯过程中,难免有网络波动,Client有可能无法将信息一次性完整发送而采用分片传送,最终到Server端可能就是多个数据段,我们要拼接这些数据端才能最终还原成原始数据。那么问题来了,怎样才能知道分段数据中的开头和结尾呢?这时候我们就需要自定义通讯协议来解决这个问题。

参考:http://blog.csdn.net/ahlxt123/article/details/47396509 (完全照抄)

在Server和Client通讯时,我们规定一种数据格式,让两者按照这种数据格式封装信息,以此来解决分包问题。

下面代码为我们自定义通讯协议,其中Enpack为封包方法,Depack为解包方法。Enpack用于将Client端数据封装后传送给Server端,Depack方法用于Server端解析数据。

package protocol

import (

"bytes"
"encoding/binary"
)

const (
MsgHeader = "MoonSocket"
HeaderLength = 10
DataLength = 4
)

//封包
//封包信息由 header + 信息长度 + 信息内容组成
func Enpack(msg [] byte) [] byte {

return append(append([]byte(MsgHeader),IntToBytes(len(msg))...),msg...)
}

//解包

func Depack(buffer [] byte) [] byte {

length:=len(buffer)
data:=make([] byte,64)
var i int

for i=0;i<length;i++{

if length<i+HeaderLength+DataLength{
//解包完毕
break
}

//如果解析到头部,则解析包信息到data
if string(buffer[i:i+HeaderLength]) == MsgHeader{

//将msg的长度转换为int
msgLength := BytesToInt(buffer[i+HeaderLength:i+HeaderLength+DataLength])

if length<i+HeaderLength+DataLength+msgLength{
//解包完毕
break
}

//解析包信息到data
data=buffer[i+HeaderLength+DataLength:i+HeaderLength+DataLength+msgLength]
}
}

if i==length{

return make([] byte,0)
}

return data
}

//整型转换为字节
func IntToBytes(n int) [] byte {
data:=int32(n)

bytesBuffer:=bytes.NewBuffer([] byte{})
//将data参数里面包含的数据写入到bytesBuffer中
//
binary.Write(bytesBuffer,binary.BigEndian,data)

return bytesBuffer.Bytes()
}

//字节转换成整型
func BytesToInt(b [] byte) int {

bytesBuffer:=bytes.NewBuffer(b)

var data int32
binary.Read(bytesBuffer,binary.BigEndian,&data)

return int(data)
}
下面是Client端代码,采用自定义协议对数据进行封包:

package main

import (
"fmt"
"os"
"net"
"strconv"
"time"
"github.com/mxi4oyu/MoonSocket/protocol"
)

//定义CheckError方法,避免写太多到 if err!=nil
func CheckError(err error) {

if err!=nil{
fmt.Fprintf(os.Stderr,"Fatal error:%s",err.Error())

os.Exit(1)
}

}

func main() {

if len(os.Args) !=2 {

fmt.Fprintf(os.Stderr,"Usage:%s IP:Port\n",os.Args[0])

os.Exit(1)
}

//动态传入服务端IP和端口号
service:=os.Args[1]

tcpAddr,err:=net.ResolveTCPAddr("tcp4",service)

CheckError(err)

conn,err:=net.DialTCP("tcp",nil,tcpAddr)

CheckError(err)

msg:="测试自定义协议"
SendMsg(conn,msg)

}

func GetSession() string{
gs1:=time.Now().Unix()
gs2:=strconv.FormatInt(gs1,10)
return gs2
}

func SendMsg(conn net.Conn,msg string) {

for i:=0;i<100;i++{
session:=GetSession()

words := "{\"ID\":"+ strconv.Itoa(i) +"\",\"Session\":"+session +",\"Meta\":\"golang\",\"Message\":\""+msg+"\"}"
conn.Write([] byte(words))
protocol.Enpack([]byte(words))
conn.Write(protocol.Enpack([]byte(words)))
}

fmt.Println("send over")

defer conn.Close()
}

下面是Server端代码,采用自定义协议来解包Client端传来的数据:
package main

import (
"fmt"
"os"
"net"
"log"
"github.com/mxi4oyu/MoonSocket/protocol"
)

//定义CheckError方法,避免写太多到 if err!=nil
func CheckError(err error) {

if err!=nil{
fmt.Fprintf(os.Stderr,"Fatal error:%s",err.Error())

os.Exit(1)
}

}

//自定义log
func Log(v... interface{}) {

log.Println(v...)
}

func main() {

server_listener,err:=net.Listen("tcp","localhost:8848")

CheckError(err)

defer server_listener.Close()

Log("Waiting for clients connect")

for{
new_conn,err:=server_listener.Accept()

CheckError(err)

go MsgHandler(new_conn)
}

}

//处理业务逻辑

func MsgHandler(conn net.Conn) {

//存储被截断的数据
tmpbuf:=make([] byte,0)
buf:=make([] byte,1024)

defer conn.Close()

//接收解包
readchan:=make(chan [] byte,16)
go ReadChan(readchan)

for{
n,err:=conn.Read(buf)

if err!=nil{

fmt.Println("connection close")
return
}

//解包
tmpbuf = protocol.Depack(append(tmpbuf,buf[:n]...))
fmt.Println("client say:",string(tmpbuf))

clientIp:=conn.RemoteAddr()

Log(clientIp)

conn.Write([] byte("hello:"+clientIp.String()+"\n"))

}

}

//从channell中读取数据
func ReadChan(readchan chan [] byte) {

for{
select {
case data:=<-readchan:
Log(string(data))
}
}
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: 
相关文章推荐