通信开发进阶
使用示例
我们通过一个完成的示例来说明一下如何在程序中实现异步全双工通信,完成示例代码位于:https://github.com/gogf/gf/tree/master/geg/net/gtcp/pkg_operations/common
types/types.go
考虑到简化测试代码复杂度,因此这里使用
JSON
数据格式来传递数据。在一些对于消息包大小比较严格的场景中,数据
字段可以自行按照二进制进行封装解析设计。此外,需要注意的是,即使使用JSON
数据格式,其中的Act
字段往往定义常量来实现,大部分场景中使用uint8
类型即可,以减小消息包大小,这里偷一下懒,直接使用字符串,以便演示。funcs/funcs.go
自定义数据格式的发送/获取定义,便于数据结构编码/解析。
package funcs
import (
"encoding/json"
"fmt"
"github.com/gogf/gf/g/net/gtcp"
"github.com/gogf/gf/geg/net/gtcp/pkg_operations/common/types"
)
// 自定义格式发送消息包
func SendPkg(conn *gtcp.Conn, act string, data...string) error {
s := ""
if len(data) > 0 {
s = data[0]
}
msg, err := json.Marshal(types.Msg{
Act : act,
Data : s,
})
if err != nil {
panic(err)
}
return conn.SendPkg(msg)
}
func RecvPkg(conn *gtcp.Conn) (msg *types.Msg, err error) {
if data, err := conn.RecvPkg(); err != nil {
return nil, err
} else {
msg = &types.Msg{}
err = json.Unmarshal(data, msg)
return nil, fmt.Errorf("invalid package structure: %s", err.Error())
}
return msg, err
}
}
-
通信服务端。在该示例中,服务端并不主动断开连接,而是在
10
秒后向客户端发送doexit
消息,通知客户端主动断开连接,以结束示例。 gtcp_common_client.go
通信客户端,可以看到代码结构和服务端差不多,数据获取独立处于
for
循环中,每个业务逻辑发送消息包时直接使用SendPkg
方法进行发送。客户端连接
10
秒后,服务端会给客户端发送doexit
消息,客户端收到该消息后便主动断开连接,长连接结束。package main
import (
"github.com/gogf/gf/g/net/gtcp"
"github.com/gogf/gf/g/os/glog"
"github.com/gogf/gf/g/os/gtimer"
"github.com/gogf/gf/geg/net/gtcp/pkg_operations/common/funcs"
"github.com/gogf/gf/geg/net/gtcp/pkg_operations/common/types"
"time"
)
func main() {
conn, err := gtcp.NewConn("127.0.0.1:8999")
if err != nil {
panic(err)
}
defer conn.Close()
gtimer.SetInterval(time.Second, func() {
if err := funcs.SendPkg(conn, "heartbeat"); err != nil {
panic(err)
}
})
// 测试消息, 3秒后向服务端发送hello消息
gtimer.SetTimeout(3*time.Second, func() {
if err := funcs.SendPkg(conn, "hello", "My name's John!"); err != nil {
panic(err)
}
for {
msg, err := funcs.RecvPkg(conn)
if err != nil {
if err.Error() == "EOF" {
glog.Println("server closed")
}
break
}
switch msg.Act {
case "hello": onServerHello(conn, msg)
case "doexit": onServerDoExit(conn, msg)
case "heartbeat": onServerHeartBeat(conn, msg)
default:
glog.Errorfln("invalid message: %v", msg)
break
}
}
}
func onServerHello(conn *gtcp.Conn, msg *types.Msg) {
glog.Printfln("hello response message from [%s]: %s", conn.RemoteAddr().String(), msg.Data)
}
func onServerHeartBeat(conn *gtcp.Conn, msg *types.Msg) {
glog.Printfln("heartbeat from [%s]", conn.RemoteAddr().String())
}
func onServerDoExit(conn *gtcp.Conn, msg *types.Msg) {
glog.Printfln("exit command from [%s]", conn.RemoteAddr().String())
conn.Close()
}
- 执行后
- 服务端输出结果
- 客户端输出结果