分享
  1. 首页
  2. 文章

golang 如何使用tcp以及tcp框架tcpx

赖皮的小萌 · · 4251 次点击 · · 开始浏览
这是一个创建于 的文章,其中的信息可能已经有所发展或是发生改变。

前言

tcp 是服务端和客户端的常见交互协议,是全双工的,即客户端和服务端可以随时对发消息。那么golang要如何使用tcp服务呢?

使用

实例地址 : golang使用tcp官方库示例
这个示例,展示了客户端和服务端对发序列,在使用官方提供的tcp库时,需要考虑以下问题:

  • 拆包
    当客户端在短时间内,或者是并发下,发送几条序列时[0 1 2 3] [2 8 7] [9 88],在服务端有可能接收到的是[0 1 2 3 2 8 7 9 88],但很显然,这是3条消息,需要在服务端进行拆包,分离成三条有效的消息才可以。

  • 心跳
    当客户端和服务端连接后,客户端因为某些原因崩溃了,没有走正常的离线操作,那么这时,服务端无法感知到客户端的结束,会继续把持着连接,耗费资源,所以需要设定心跳。由客户端每隔一段时间发送一条消息,让服务端知道他还活着。

  • 消息分离
    所有的客户端消息,都从同一个conn中留出,而不同的消息有不同的处理方法,请求参数,需要对拆分出来的消息进行分离,适用予不同的handler。

  • 维护成本
    维护成本体现在两点。第一,从官方的库示例可以看出,底层的写法很多问题没有考虑到,仅仅是涉及到连接的建立和对发,在开发时,不同的使用者写法群魔乱舞,很难做到统一,人数越多越难维护。第二,仅使用基本包在需求迭代时,很容易让代码变得越来越臃肿,比如临时给你一个需求,要求像http那样,支持某一个handler的中间件。

TCPX

TCPX 是纯golang开发的tcp框架,具备上述所有问题的解决方案,并且速度很快。
仓库地址: https://github.com/fwhezfwhez/tcpx

TCPX仿制http框架gin,几乎具备了大多数gin的原生api,使用起来极其简便。可以感受一下!

发起tcp服务

package main
import (
 "github.com/fwhezfwhez/tcpx"
)
func main() {
 srv := tcpx.NewTcpX(nil)
 srv.ListenAndServe("tcp", ":8102")
}

直接针对协议和端口号就可以启动了,太简单了。

中间件

中间件模式是tcpx最大的特色,它自带传输序列,内置messageID,可以根据messageID如同http请求一般,使用中间件,比如统计服务被请求的次数:

package main
import (
 "fmt"
 "github.com/fwhezfwhez/tcpx"
 "sync/atomic"
)
var requestTimes int32
func main() {
 srv := tcpx.NewTcpX(nil)
 srv.UseGlobal(countRequestTime)
 srv.AddHandler(3, getRequestTime)
 fmt.Println("tcp listen on :8080")
 if e := srv.ListenAndServe("tcp", ":8080"); e != nil {
 panic(e)
 }
}
func countRequestTime(c *tcpx.Context) {
 atomic.AddInt32(&requestTimes, 1)
}
func getRequestTime(c *tcpx.Context) {
 c.JSON(4, tcpx.H{"message": "success", "request_times": requestTimes})
}

上述代码制定了一个messageID为3的路由,所有客户端发送的messageID为3的消息,都会经过该路由处理,并且,它确保每个消息都会经过全局中间件countRequestTime

关于中间件,tcpx支持了以下三种

  • 锚类型
 srv.Use("before-login", beforeLogin)
 srv.AddHandler(1, login)

调用Use()后,添加的路由会经过该中间件处理。

  • 全局类型
 srv.UseGlobal(countRequestTime)
 srv.AddHandler(3, getRequestTime)

所有路由,都会经过全局中间件处理。

  • 路由类型
srv.AddHandler(3, beforeLogin, login)

仅仅该路由会经过路由中间件处理。

优雅关闭,重启

package main
import (
 "fmt"
 "runtime/debug"
 "time"
 "github.com/fwhezfwhez/tcpx"
 "log"
 //"tcpx"
)
func main() {
 srv := tcpx.NewTcpX(nil)
 // start server
 go func() {
 fmt.Println("tcp listen on :8080")
 srv.ListenAndServe("tcp", ":8080")
 }()
 // after 10 seconds and stop it
 go func() {
 time.Sleep(10 * time.Second)
 if e := srv.Stop(false); e != nil {
 log.Println(fmt.Sprintf("%s \n %s", e.Error(), debug.Stack()))
 return
 }
 // operate between stop and start
 // do something
 fmt.Println("before start, print ok")
 // after 10 seconds start again
 time.Sleep(10 * time.Second)
 if e := srv.Start(); e != nil {
 log.Println(fmt.Sprintf("%s \n %s", e.Error(), debug.Stack()))
 return
 }
 // or call `Restart()` equals to above `Close` and `Start`
 //if e := srv.Restart(false, func() {
 // fmt.Println("before start, print ok")
 //}); e != nil {
 // fmt.Println(e.Error())
 //}
 }()
 select {}
}

自带心跳

package main
import (
 "github.com/fwhezfwhez/tcpx"
 //"tcpx"
 "time"
)
func main() {
 srv := tcpx.NewTcpX(nil)
 srv.HeartBeatModeDetail(true, 10*time.Second, false, tcpx.DEFAULT_HEARTBEAT_MESSAGEID)
 //srv.RewriteHeartBeatHandler(1300, func(c *tcpx.Context) {
 // fmt.Println("rewrite heartbeat handler")
 // c.RecvHeartBeat()
 //})
 tcpx.SetLogMode(tcpx.DEBUG)
 srv.ListenAndServe("tcp", ":8101")
}

自带用户池,与上线下线,池内用户对发

package main
import (
 "fmt"
 "github.com/fwhezfwhez/errorx"
 "github.com/fwhezfwhez/tcpx"
 //"tcpx"
)
func main() {
 srv := tcpx.NewTcpX(tcpx.JsonMarshaller{})
 srv.WithBuiltInPool(true)
 srv.AddHandler(1, online)
 srv.AddHandler(3, offline)
 srv.ListenAndServe("tcp", ":8102")
}
func online(c *tcpx.Context) {
 type Login struct {
 Username string `json:"username"`
 }
 var login Login
 if _, e := c.Bind(&login); e != nil {
 fmt.Println(errorx.Wrap(e).Error())
 return
 }
 c.Online(login.Username)
 fmt.Println("online success")
}
func offline(c *tcpx.Context) {
 fmt.Println("offline success")
 c.Offline()
}

有疑问加站长微信联系(非本文作者)

本文来自:简书

感谢作者:赖皮的小萌

查看原文:golang 如何使用tcp以及tcp框架tcpx

入群交流(和以上内容无关):加入Go大咖交流群,或添加微信:liuxiaoyan-s 备注:入群;或加QQ群:692541889

关注微信
4251 次点击
暂无回复
添加一条新回复 (您需要 后才能回复 没有账号 ?)
  • 请尽量让自己的回复能够对别人有帮助
  • 支持 Markdown 格式, **粗体**、~~删除线~~、`单行代码`
  • 支持 @ 本站用户;支持表情(输入 : 提示),见 Emoji cheat sheet
  • 图片支持拖拽、截图粘贴等方式上传

用户登录

没有账号?注册
(追記) (追記ここまで)

今日阅读排行

    加载中
(追記) (追記ここまで)

一周阅读排行

    加载中

关注我

  • 扫码关注领全套学习资料 关注微信公众号
  • 加入 QQ 群:
    • 192706294(已满)
    • 731990104(已满)
    • 798786647(已满)
    • 729884609(已满)
    • 977810755(已满)
    • 815126783(已满)
    • 812540095(已满)
    • 1006366459(已满)
    • 692541889

  • 关注微信公众号
  • 加入微信群:liuxiaoyan-s,备注入群
  • 也欢迎加入知识星球 Go粉丝们(免费)

给该专栏投稿 写篇新文章

每篇文章有总共有 5 次投稿机会

收入到我管理的专栏 新建专栏