经过上面三篇文章,我们已经写了一个简单的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端汇报当前时间。