## TCP服务器 目录 - 配置文件 - 一个基本的消息收发例子 - 注册路由 - 拦截器 - 服务认证 - 更多 > HotGo基于GF框架的TCP服务器组件,提供了一个简单而灵活的方式快速搭建基于TCP的服务应用。集成了许多常用功能,如长连接、服务认证、路由分发、RPC消息、拦截器和数据绑定等,大大简化和规范了服务器开发流程。 ### 配置文件 - 配置文件:server/manifest/config/config.yaml ```yaml tcp: # 服务器 server: address: ":8099" # 客户端 client: # 定时任务 cron: group: "cron" # 分组名称 name: "cron1" # 客户端名称 address: "127.0.0.1:8099" # 服务器地址 appId: "1002" # 应用名称 secretKey: "hotgo" # 密钥 # 系统授权 auth: group: "auth" # 分组名称 name: "auth1" # 客户端名称 address: "127.0.0.1:8099" # 服务器地址 appId: "mengshuai" # 应用名称 secretKey: "123456" # 密钥 ``` - 可以看到,除了服务器配置外,还有两个客户端配置`cron` 和`auth` - `cron`是HotGo内置的定时任务服务,和http服务通过RPC通讯以实现和后台交互,使其可以独立、集群部署。 - `auth`可以为第三方平台提供授权服务。如果你需要他,可以将它部署在第三方程序中,在重要的位置进行授权验证。 ### 一个基本的消息收发测试用例 - 文件路径:server/internal/library/network/tcp/tcp_example_test.go ```go package tcp_test import ( "context" "fmt" "github.com/gogf/gf/v2/os/gctx" "github.com/gogf/gf/v2/test/gtest" "hotgo/internal/library/network/tcp" "testing" "time" ) var T *testing.T // 声明一个全局的 *testing.T 变量 type TestMsgReq struct { Name string `json:"name"` } type TestMsgRes struct { tcp.ServerRes } type TestRPCMsgReq struct { Name string `json:"name"` } type TestRPCMsgRes struct { tcp.ServerRes } func onTestMsg(ctx context.Context, req *TestMsgReq) { fmt.Printf("服务器收到消息 ==> onTestMsg:%+v\n", req) conn := tcp.ConnFromCtx(ctx) gtest.C(T, func(t *gtest.T) { t.AssertNE(conn, nil) }) res := new(TestMsgRes) res.Message = fmt.Sprintf("你的名字:%v", req.Name) conn.Send(ctx, res) } func onResponseTestMsg(ctx context.Context, req *TestMsgRes) { fmt.Printf("客户端收到响应消息 ==> TestMsgRes:%+v\n", req) err := req.GetError() gtest.C(T, func(t *gtest.T) { t.AssertNil(err) }) } func onTestRPCMsg(ctx context.Context, req *TestRPCMsgReq) (res *TestRPCMsgRes, err error) { fmt.Printf("服务器收到消息 ==> onTestRPCMsg:%+v\n", req) res = new(TestRPCMsgRes) res.Message = fmt.Sprintf("你的名字:%v", req.Name) return } func startTCPServer() { serv := tcp.NewServer(&tcp.ServerConfig{ Name: "hotgo", Addr: ":8002", }) // 注册路由 serv.RegisterRouter( onTestMsg, ) // 注册RPC路由 serv.RegisterRPCRouter( onTestRPCMsg, ) // 服务监听 err := serv.Listen() gtest.C(T, func(t *gtest.T) { t.AssertNil(err) }) } // 一个基本的消息收发 func TestSendMsg(t *testing.T) { T = t go startTCPServer() ctx := gctx.New() client := tcp.NewClient(&tcp.ClientConfig{ Addr: "127.0.0.1:8002", }) // 注册路由 client.RegisterRouter( onResponseTestMsg, ) go func() { err := client.Start() gtest.C(T, func(t *gtest.T) { t.AssertNil(err) }) }() // 确保服务都启动完成 time.Sleep(time.Second * 1) // 拿到客户端的连接 conn := client.Conn() gtest.C(T, func(t *gtest.T) { t.AssertNE(conn, nil) }) // 向服务器发送tcp消息,不会阻塞程序执行 err := conn.Send(ctx, &TestMsgReq{Name: "Tom"}) gtest.C(T, func(t *gtest.T) { t.AssertNil(err) }) // 向服务器发送rpc消息,会等待服务器响应结果,直到拿到结果或响应超时才会继续 var res TestRPCMsgRes if err = conn.RequestScan(ctx, &TestRPCMsgReq{Name: "Tony"}, &res); err != nil { gtest.C(T, func(t *gtest.T) { t.AssertNil(err) }) } fmt.Printf("客户端收到RPC消息响应 ==> TestRPCMsgRes:%+v\n", res) time.Sleep(time.Second * 1) } ``` ### 注册路由 - 从上面的例子可以看到,不管是普通TCP消息和RPC消息的请求/响应结构体都采用类似GF框架的规范路由的结构,请求`XxxRes`/响应`XxxRes`的格式,是不是很亲切? ### 拦截器 - 不管是服务端还是客户端,在初始化时都可以注册多个拦截器来满足更多场景的服务开发,下面是一个使用例子: ```go package main import ( "context" "github.com/gogf/gf/v2/frame/g" "hotgo/internal/library/network/tcp" ) func main() { serv = tcp.NewServer(&tcp.ServerConfig{ Name: "hotgo", Addr: ":8002", }) // 注册拦截器 // 执行顺序是从前到后,即Interceptor -> Interceptor2 -> Interceptor3。如果中间有任意一个抛出错误,则会中断后续处理 serv.RegisterInterceptor(Interceptor, Interceptor2, Interceptor3) // 服务监听 if err := serv.Listen(); err != nil { if !serv.IsClose() { g.Log().Warningf(ctx, "TCPServer Listen err:%v", err) } } } func Interceptor(ctx context.Context, msg *tcp.Message) (err error) { // 可以在拦截器中通过上下文拿到连接 conn := tcp.ConnFromCtx(ctx) // 拿到原始请求消息 g.Dump(msg) // 如果想要中断后续处理只需返回一个错误即可,但注意两种情况 // tcp消息:如果你还想对该消息进行回复应在拦截器中进行处理,例如:conn.Send(ctx, 回复消息内容) // rpc消息:返回一个错误后系统会将错误自动回复到rpc响应中,无需单独处理 return } func Interceptor2(ctx context.Context, msg *tcp.Message) (err error) { // ... return } func Interceptor3(ctx context.Context, msg *tcp.Message) (err error) { // ... return } ``` ### 服务认证 - 一般情况下,建议客户端连接到服务器时都通过`授权许可证`的方式进行登录认证,当初始化客户端配置认证数据时,连接成功后会自动进行登录认证。 ```go // 创建客户端配置 clientConfig := &tcp.ClientConfig{ Addr: "127.0.0.1:8002", AutoReconnect: true, // 认证数据 // 认证数据可以在后台-系统监控-在线服务-许可证列表中添加,同一个授权支持多个服务使用,但多个服务不能使用相同的名称进行连接 Auth: &tcp.AuthMeta{ Name: "服务名称", Group: "服务分组", AppId: "APPID", SecretKey: "SecretKey", }, } // 初始化客户端 client = tcp.NewClient(clientConfig) ``` ### 更多 TCP服务器源码路径:server/internal/library/network/tcp 更多文档请参考:https://goframe.org/pages/viewpage.action?pageId=1114625