基于zinx的go tcp通信示例
一、zinx简介:(https://gitee.com/Aceld/zinx/)
Zinx是一个基于Golang的轻量级tcp服务框架,根据官方的定位,zinx是在游戏领域或者其他长链接的领域的轻量级企业框架,其使用简单,性能高效,能够很方便的帮助用户搭建tcp通信服务。
一个简单的zinx-tcp服务搭建只需要三步:
- 创建server服务实例
- 配置自定义路由及业务
- 启动服务
package main
import "github.com/aceld/zinx/znet"
func main() {
//1 创建一个server服务
s := znet.NewServer()
//2 配置路由
s.AddRouter(1, &PingRouter{})
//3 启动服务
s.Serve()
}
框架的基本架构:
特点:
- 路由定义
- 消息封装
- 支持多路由
- 链接管理
- 消息队列和多任务机制
二、zinx基本封装实现tcpserver,clent工具包
-
tcp-server服务端封装
-
配置注入
//zinx的配置文件的格式详见github.com/aceld/zinx/zconf,要使用server服务,需要完成配置的初始化,我们创建一个server结构体,基于zinx进行服务端封装,并接收使用者进行配置注入,如:
type TcpServer struct {
server ziface.IServer
lock sync.RWMutex
}
var instance *TcpServer
func NewServer(config *zconf.Config) *TcpServer {
// 创建 Zinx 服务器
if instance == nil {
server := znet.NewUserConfServer(config)
server.SetOnConnStart(ConnStart)
server.SetOnConnStop(ConnLost)
instance = &TcpServer{server: server}
}
return instance
}
-
服务实例化
func NewServer(config *zconf.Config) *TcpServer {
// 创建 Zinx 服务器
if instance == nil {
server := znet.NewUserConfServer(config)
server.SetOnConnStart(ConnStart)
server.SetOnConnStop(ConnLost)
instance = &TcpServer{server: server}
}
return instance
}
-
路由注册(消息收发)
// 路由注册入口
func (ts *TcpServer) RegisterRouter(uid uint32, router ziface.IRouter) {
if ts.server == nil {
return
}
//ts.restartServer()
ts.server.AddRouter(uid, router)
}
// 创建一个可收发信息的路由,便于测试
type RDuplex struct {
znet.BaseRouter
}
func (rd *RDuplex) Handle(request ziface.IRequest) {
fmt.Printf("receive from client msgID=%d, data=%s\n", request.GetMsgID(), string(request.GetData()))
err := request.GetConnection().SendMsg(2, []byte("hello zix hello Router"))
if err != nil {
fmt.Println(err)
}
}
-
服务启动/停止
func (ts *TcpServer) Start(ctx context.Context) error {
ts.server.Serve()
select {}
}
func (ts *TcpServer) Stop(ctx context.Context) error {
ts.server.Stop()
return nil
}
-
tcp-client服务端封装
-
配置注入
type (
TcpClient struct {
lock sync.RWMutex
conn net.Conn
dp ziface.IDataPack
option Option
revChan chan int
stopChan chan int
}
// 服务配置
Option struct {
ServerAddr string
Retry int
}
)
var instance *TcpClient
func NewClient(opt Option) *TcpClient {
if instance == nil {
instance = &TcpClient{
option: opt,
revChan: make(chan int, 1),
dp: zpack.Factory().NewPack(ziface.ZinxDataPack),
}
instance.initTcpClient()
}
fmt.Println("tcp client start.")
return instance
}
-
服务实例化
func NewClient(opt Option) *TcpClient {
if instance == nil {
instance = &TcpClient{
option: opt,
revChan: make(chan int, 1),
dp: zpack.Factory().NewPack(ziface.ZinxDataPack),
}
}
fmt.Println("tcp client start.")
return instance
}
-
服务监听
func (cli *TcpClient) waitRecv() {
for {
select {
case <-cli.revChan:
go cli.recv()
}
}
}
-
消息收发
func (cli *TcpClient) Send(data []byte) {
msg, _ := cli.dp.Pack(zpack.NewMsgPackage(uint32(1002), data))
_, err := cli.conn.Write(msg)
if err != nil {
fmt.Println(err.Error())
return
}
cli.revChan <- -1
}
func (cli *TcpClient) recv() {
headData := make([]byte, cli.dp.GetHeadLen())
_, err := io.ReadFull(cli.conn, headData)
if err != nil {
fmt.Println(err.Error())
}
msgHead, err := cli.dp.Unpack(headData)
if err != nil {
fmt.Println(err.Error())
}
//if msgHead.GetDataLen() == 0 {
// fmt.Println(err.Error())
//}
msg := msgHead.(*zpack.Message)
msg.Data = make([]byte, msg.GetDataLen())
_, err = io.ReadFull(cli.conn, msg.Data)
if err != nil {
fmt.Println(err.Error())
}
recvData = msg.Data
fmt.Printf("==> Client receive Msg: ID = %d, data = %s\n", msg.ID, msg.Data)
}
-
服务启停
func (cli *TcpClient) Start(ctx context.Context) error {
//go cli.loopSend()
cli.waitRecv()
return nil
}
func (cli *TcpClient) Stop(ctx context.Context) error {
//go cli.loopSend()
err := cli.conn.Close()
if err != nil {
return err
}
return nil
}
三、测试
-
测试用例
package tcp
import (
"context"
"github.com/aceld/zinx/zconf"
"github.com/go-nova/pkg/core/transport/tcp/tcp_client"
"github.com/go-nova/pkg/core/transport/tcp/tcp_server"
"testing"
"time"
)
func TestName(t *testing.T) {
var config = &zconf.Config{
Host: "0.0.0.0",
TCPPort: 8899,
}
ins := tcp_server.NewServer(config)
ins.RegisterRouter(uint32(1002), &tcp_server.RDuplex{})
ctx := context.Background()
go ins.Start(ctx)
time.Sleep(time.Second * 10)
cli := tcp_client.NewClient(tcp_client.Option{
ServerAddr: "127.0.0.1:8899",
Retry: 3,
})
go cli.Start(ctx)
go send(cli)
<-time.After(time.Second * 15)
ins.Stop(ctx)
cli.Stop(ctx)
}
func send(cli *tcp_client.TcpClient) {
for i := 0; i < 5; i++ {
cli.Send([]byte("hello server"))
}
}
-
效果