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

在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))
		}
	}
}


  • 2
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
QT是一个跨平台的应用程序开发框架,支持网络编程,并提供了一套自定义协议的功能。 在QT中,我们可以使用QT的网络模块来实现网络通信。网络模块提供了基于TCP和UDP协议的Socket类,可以用于进行网络连接、数据发送和接收等操作。 对于自定义协议,QT提供了QTcpSocket和QUdpSocket两个类,我们可以通过继承这两个类来实现自己的网络协议。 对于TCP协议,我们可以通过继承QTcpSocket类,并重其中的虚函数来实现自定义协议。通过重其中的数据接收函数,我们可以解析接收到的数据,并根据自定义协议进行相应的处理。同时,我们也可以重数据发送函数,将数据按照自定义协议组织成数据包并发送出去。 对于UDP协议,我们可以通过继承QUdpSocket类,并重其中的虚函数来实现自定义协议。通过重其中的数据接收函数,我们可以解析接收到的数据,并根据自定义协议进行相应的处理。同时,我们也可以重数据发送函数,将数据按照自定义协议组织成数据包并发送出去。 除了继承socket类外,QT还提供了信号与槽机制,我们可以通过信号与槽来处理自定义协议的通信。即在数据接收端,我们可以通过接收到特定的信号来触发槽函数进行数据处理;在数据发送端,我们可以通过槽函数来触发信号发送数据。 总的来说,QT网络模块提供了丰富的功能和工具来实现自定义的网络协议。我们可以根据自己的需求,使用QT的网络模块和自定义协议的功能来进行网络通信和数据处理。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值