从零开始写Go网络通信框架(4)——全双工收发消息

经过上面三篇文章,我们已经写了一个简单的C/S框架,可以实现客户端与服务端进行发送消息。但这是一个单向通讯的。我们要想让客户端接收服务端发来的命令,并执行响应的动作,就需要写成全双工收发消息。即客户端也能接收服务端发来的指令。

下面我在客户端定义一个方法,用来接收服务端的消息。

//接收服务端发来的消息
func ReadMsg(conn net.Conn)  {

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

	//将信息解包
	n,_:=conn.Read(buf)
	tmpbuf = protocol.Depack(append(tmpbuf,buf[:n]...))
	msg:=string(tmpbuf)
	fmt.Println("server say:",msg)
}

客户端完整代码如下:

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)
	ch:=make(chan int,100)

	ticker := time.NewTicker(time.Second)
	defer ticker.Stop()

	for{
		select {
		case <-ticker.C:
			ch<-1
			go ClientMsgHandler(conn,ch)
		case <-time.After(time.Second*10):
			defer conn.Close()
			fmt.Println("timeout")
		}
	}

}

//客户端消息处理
func ClientMsgHandler(conn net.Conn,ch chan int)  {

	<-ch
	//获取当前时间
	msg:=time.Now().String()
	go SendMsg(conn,msg)
	go ReadMsg(conn)

}

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

//接收服务端发来的消息
func ReadMsg(conn net.Conn)  {

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

	//将信息解包
	n,_:=conn.Read(buf)
	tmpbuf = protocol.Depack(append(tmpbuf,buf[:n]...))
	msg:=string(tmpbuf)
	fmt.Println("server say:",msg)
}

//向服务端发送消息
func SendMsg(conn net.Conn,msg string)  {

session:=GetSession()

	words := "{\"Session\":"+session +",\"Meta\":\"Monitor\",\"Message\":\""+msg+"\"}"
	//将信息封包
	smsg:=protocol.Enpack([]byte(words))
	conn.Write(smsg)

}

在服务端,我们也做了响应的调整。定义一个方法,专门向客户端发送消息。

//服务端向客户端发送消息
func WriteMsgToClient(conn net.Conn)  {

	talk:="wordpress"
	//将信息封包
	smsg:=protocol.Enpack([]byte(talk))
	conn.Write(smsg)
}

服务端完整代码如下:

package main

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


//定义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 ServerMsgHandler(new_conn)
	}

	
}

//服务端消息处理
func ServerMsgHandler(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))

		Msg:=tmpbuf

		//向客户端发送消息
		go WriteMsgToClient(conn)

		beatch :=make(chan byte)
		//心跳计时,默认30秒
		go HeartBeat(conn,beatch,30)
		//检测每次Client是否有数据传来
		go HeartChanHandler(Msg,beatch)

	}

}

//处理心跳,根据HeartChanHandler判断Client是否在设定时间内发来信息
func HeartBeat(conn net.Conn,heartChan chan byte,timeout int)  {
	select {
	case hc:=<-heartChan:
		Log("<-heartChan:",string(hc))
		conn.SetDeadline(time.Now().Add(time.Duration(timeout)*time.Second))
		break
	case <-time.After(time.Second*30):
		Log("timeout")
		conn.Close()
	}
}

//服务端向客户端发送消息
func WriteMsgToClient(conn net.Conn)  {

	talk:="wordpress"
	//将信息封包
	smsg:=protocol.Enpack([]byte(talk))
	conn.Write(smsg)
}

//处理心跳channel
func HeartChanHandler( n [] byte,beatch chan byte)  {
	for _,v:=range n{
		beatch<-v
	}
	close(beatch)
}

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

	for{
		select {
		case data:=<-readchan:
			Log(string(data))
		}
	}
}

现在配上一张图,Server端不断给Client端发送消息"wordpress",Client不断向Server端汇报当前时间。


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

MXi4oyu

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值