基于zinx的go tcp通信案例

news2025/1/24 2:18:07

基于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\n", 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\n", 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"))
    	}
    }
    
    
  • 效果

    在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/632489.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Baumer工业相机堡盟工业相机如何使用BGAPISDK的相机图像时间戳计算运行时间以及时间差(C#)

Baumer工业相机堡盟工业相机如何使用BGAPISDK的相机图像时间戳计算运行时间以及时间差&#xff08;C#&#xff09; Baumer工业相机Baumer工业相机BGAPI SDK和图像时间戳的技术背景Baumer工业相机使用BGAPISDK控制相机数据流的方式1.引用合适的类文件2.使用BGAPISDK获取时间戳的…

Vue中如何进行条件渲染

Vue中如何进行条件渲染 Vue是一款流行的前端框架&#xff0c;它提供了许多方便的功能来处理数据和视图。其中一个非常有用的功能是条件渲染。条件渲染可以根据条件来控制视图的显示和隐藏。在本文中&#xff0c;我们将介绍Vue中如何进行条件渲染&#xff0c;并提供一些示例代码…

异常数据检测 | Python实现k-means时间序列异常数据检测

文章目录 文章概述模型描述源码分享学习小结文章概述 异常数据检测 | Python实现k-means时间序列异常数据检测 模型描述 k-means是一种广泛使用的聚类算法。它创建了k个具有相似特性的数据组。不属于这些组的数据实例可能会被标记为异常。在我们开始k-means聚类之前,我们使用e…

spring.expression 随笔0 概述

0. 我只是个普通码农&#xff0c;不值得挽留 Spring SpEL表达式的使用 常见的应用场景:分布式锁的切面借助SpEL来构建key 比较另类的的应用场景:动态校验 个人感觉可以用作控制程序的走向&#xff0c;除此之外&#xff0c;spring的一些模块的自动配置类&#xff0c;也会在Cond…

chatgpt赋能python:Python如何判断输入数据类型

Python如何判断输入数据类型 Python是一种动态类型语言&#xff0c;它可以在运行时自动识别数据的类型。但是&#xff0c;有时候我们需要在代码中判断输入数据的类型&#xff0c;以便进行相应的操作。 判断数据类型的内置函数 Python有一些内置函数可以用于判断数据类型&…

仿滴滴打车百度地图定位查找附近出租车或门店信息

前端vue仿滴滴打车百度地图定位查找附近出租车或门店信息, 下载完整代码请访问uni-app插件市场地址:https://ext.dcloud.net.cn/plugin?id12982 效果图如下: # #### 使用方法 使用方法 #安装vue-baidu-map插件 npm install vue-baidu-map --save <!-- center: 地图中…

蓝牙耳机可以戴着游泳吗?推荐四款可以游泳的游泳耳机

今年的夏天格外炎热&#xff0c;热衷于户外运动的人们也开始转换健身方式&#xff0c;游泳作为一项锻炼全身又祛暑清凉的运动&#xff0c;自然成为了最佳选择&#xff0c;相信大多数人跟我一样在运动时都离不开耳机&#xff0c;而游泳需要和水接触&#xff0c;这也导致了我所有…

SpringBoot+Mybatis+Thymeleaf实现的物资管理系统

本系统具体使用的技术是&#xff1a;后端使用SpringBootMybatis&#xff0c;前端使用了Thymeleaf框架&#xff0c;数据库使用的是MySql 8.0。开发工具使用的是IDEA。 本系统前端是使用了前辈的管理系统模板&#xff0c;具体的系统模块功能如下图所示&#xff1a; 一、系统首页…

chatgpt赋能python:Python如何删除之前的内容

Python如何删除之前的内容 在Python编程中&#xff0c;删除之前输入或者生成的内容是一个常见的需求。本文将介绍如何在Python中删除之前的内容以及相关的技巧和方法。 为什么需要删除之前的内容&#xff1f; 在Python编程中&#xff0c;我们有时需要重新输入命令或代码段&a…

OpenGl光照之材质

文章目录 设置材质光的属性完整代码 在现实世界里&#xff0c;每个物体会对光产生不同的反应。比如&#xff0c;钢制物体看起来通常会比陶土花瓶更闪闪发光&#xff0c;一个木头箱子也不会与一个钢制箱子反射同样程度的光。有些物体反射光的时候不会有太多的散射(Scatter)&…

6月10日,今日信息差

1、中国科学家实现含氯废塑料高效无害升级回收。近日&#xff0c;中国科学院上海硅酸盐研究所首席研究员黄富强团队采用新型常温脱氯法&#xff0c;将含氯废塑料直接转化成多种高附加值新材料&#xff0c;成功实现高效无害升级回收&#xff0c;可广泛应用于绿色环保、新型储能、…

Linux5.2 LVS+keepalived高可用群集

文章目录 计算机系统5G云计算第三章 LINUX LVSKeepalived群集一、Keepalived 概述1. Keepalived 作用2.Keepalived 实现原理剖析3.VRRP协议&#xff08;虚拟路由冗余协议&#xff09;4.Keepalived 主要模块及其作用5.健康检查方式&#xff08;学名&#xff1a;探针&#xff09;…

chatgpt赋能python:Python怎么删库:谨慎使用

Python怎么删库&#xff1a;谨慎使用 Python是一种强大的编程语言&#xff0c;它被广泛用于各种项目中&#xff0c;不仅仅是数据科学和机器学习。但它也可以被用来执行危险的任务&#xff0c;比如删库。当你需要在Python中进行数据库操作时&#xff0c;一定要特别小心。在这篇…

Stable-Diffusion|文生图 拍立得纪实风格的Lora 图例(三)

上篇【Stable-Diffusion|入门怎么下载与使用civitai网站的模型&#xff08;二&#xff09;】介绍了如何使用c站进行文生图&#xff0c;尤其一些Lora可能随时会下架&#xff0c;所以及时测试&#xff0c;及时保存很关键&#xff0c;更新一些笔者目前尝试比较有意思的Lora。 本篇…

【python】【excel】在UI中加载EXCEL并修改

界面 代码 import tkinter as tk from tkinter import filedialog from pandastable import Table import pandas as pd import pyperclipclass ExcelEditor(tk.Frame):def __init__(self, parentNone):tk.Frame.__init__(self, parent)self.parent parentself.grid()self.cr…

chatgpt赋能python:如何在Python中添加空行?

如何在Python中添加空行&#xff1f; 如果你是一个有经验的Python工程师&#xff0c;在编写代码时你可能会遇到需要添加空行的情况。但是有几种方法可以实现这一点&#xff0c;你应该用哪种方法呢&#xff1f;在本文中&#xff0c;我们将探讨如何在Python中添加空行以及各种添…

TypeScript 5.1发布,新功能更新

文章目录 1&#xff1a;返回类型增加undefined2&#xff1a;getter可以设置和 setter 的不相关类型3&#xff1a;对 JSX 元素和 JSX 标签的异步支持4&#xff1a;支持命名空间属性名称 JSX5&#xff1a;typeRoots在模块更新6&#xff1a;JSX 标签的链接游标7&#xff1a;param …

Python中对文件的基本操作

文章目录 文件和目录路径文件的读取、写入、复制、删除、变更位置及修改名称解压缩zip格式的文件剪切板的应用使用python-docx处理Word文档使用openpyxl处理Excel文档示例&#xff1a;获取Excel文档中的数据生成Word文档 文件和目录路径 os库是Python内置的标准库&#xff0c;…

张天禹移动端学习

文章目录 相关概念&#xff08;一&#xff09;屏幕相关1. 屏幕大小2. 屏幕分辨率3. 屏幕密度 &#xff08;二&#xff09;像素相关1.物理像素2. css 像素3. 设备独立像素4.像素比5.像素之间的关系 &#xff08;三&#xff09;图片高清显示位图像素图片的高清显示&#xff08;媒…

chatgpt赋能python:Python下如何给网页添加背景图片

Python下如何给网页添加背景图片 随着现代互联网的快速发展&#xff0c;人们对于网页设计的要求越来越高&#xff0c;其中非常重要的一项就是背景图。在Python编程中&#xff0c;我们也可以很容易的为网页添加背景图片。 HTML中的background属性 要给网页加上背景图&#xf…