您现在的位置是:首页 >技术杂谈 >基于zinx的go tcp通信案例网站首页技术杂谈

基于zinx的go tcp通信案例

玉言心 2024-10-02 00:01:04
简介基于zinx的go tcp通信案例

基于zinx的go tcp通信示例

一、zinx简介:(https://gitee.com/Aceld/zinx/)

Zinx是一个基于Golang的轻量级tcp服务框架,根据官方的定位,zinx是在游戏领域或者其他长链接的领域的轻量级企业框架,其使用简单,性能高效,能够很方便的帮助用户搭建tcp通信服务。

一个简单的zinx-tcp服务搭建只需要三步:

  1. 创建server服务实例
  2. 配置自定义路由及业务
  3. 启动服务
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工具包

  1. tcp-server服务端封装

    1. 配置注入

      //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
      }
      
      
    2. 服务实例化

      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
      }
      
    3. 路由注册(消息收发)

      // 路由注册入口
      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
      ", request.GetMsgID(), string(request.GetData()))
      	err := request.GetConnection().SendMsg(2, []byte("hello zix hello Router"))
      	if err != nil {
      		fmt.Println(err)
      	}
      }
      
      
    4. 服务启动/停止

      
      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
      }
      
  2. tcp-client服务端封装

    1. 配置注入

      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
      }
      
    2. 服务实例化

      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
      }
      
    3. 服务监听

      func (cli *TcpClient) waitRecv() {
      	for {
      		select {
      		case <-cli.revChan:
      			go cli.recv()
      		}
      	}
      }
      
    4. 消息收发

      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
      ", msg.ID, msg.Data)
      }
      
      
    5. 服务启停

      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"))
    	}
    }
    
    
  • 效果

    在这里插入图片描述

风语者!平时喜欢研究各种技术,目前在从事后端开发工作,热爱生活、热爱工作。