文章详情

短信预约-IT技能 免费直播动态提醒

请输入下面的图形验证码

提交验证

短信预约提醒成功

GO语言如何实现TCP服务器

2023-07-05 15:44

关注

今天小编给大家分享一下GO语言如何实现TCP服务器的相关知识点,内容详细,逻辑清晰,相信大部分人都还太了解这方面的知识,所以分享这篇文章给大家参考一下,希望大家阅读完这篇文章后有所收获,下面我们一起来了解一下吧。

interface/tcp/Handler.go

type Handler interface {   Handle(ctx context.Context, conn net.Conn)   Close() error}

Handler:业务逻辑的处理接口

Handle(ctx context.Context, conn net.Conn) 处理连接

tcp/server.go

type Config struct {    Address string}func ListenAndServeWithSignal(cfg *Config, handler tcp.Handler) error {    closeChan := make(chan struct{})    listen, err := net.Listen("tcp", cfg.Address)    if err != nil {       return err   }    logger.Info("start listen")    ListenAndServe(listen, handler, closeChan)    return nil}func ListenAndServe(listener net.Listener,                    handler tcp.Handler,                    closeChan <-chan struct{}) {    ctx := context.Background()    var waitDone sync.WaitGroup    for true {        conn, err := listener.Accept()        if err != nil {            break        }        logger.Info("accept link")        waitDone.Add(1)        go func() {            defer func() {                waitDone.Done()            }()            handler.Handler(ctx, conn)        }()    }    waitDone.Wait()}

Config:启动tcp服务器的配置

Address:监听地址

ListenAndServe:ctx是上下文,可以传递一些参数。死循环中接收到新连接时,让一个协程去处理连接

如果listener.Accept()出错了就会break跳出来,这时候需要等待已经服务的客户端退出。使用WaitGroup等待客服端退出

func ListenAndServe(listener net.Listener,                    handler tcp.Handler,                    closeChan <-chan struct{}) {    go func() {       <-closeChan       logger.Info("shutting down...")       _ = listener.Close()       _ = handler.Close()   }()    defer func() {       _ = listener.Close()       _ = handler.Close()   }()    ......}

listener和handler在退出的时候需要关掉。如果用户直接kill掉了程序,我们也需要关掉listener和handler,这时候要使用closeChan,一旦接收到关闭信号,就执行关闭逻辑

func ListenAndServeWithSignal(cfg *Config, handler tcp.Handler) error {    closeChan := make(chan struct{})    sigCh := make(chan os.Signal)    signal.Notify(sigCh, syscall.SIGHUP, syscall.SIGQUIT, syscall.SIGTERM, syscall.SIGINT)    go func() {       sig := <-sigCh       switch sig {          case syscall.SIGHUP, syscall.SIGQUIT, syscall.SIGTERM, syscall.SIGINT:          closeChan <- struct{}{}      }   }()    listen, err := net.Listen("tcp", cfg.Address)    if err != nil {       return err   }    logger.Info("start listen")    ListenAndServe(listen, handler, closeChan)    return nil}

当系统对程序发送信号时,sigCh会接收到信号

tcp/echo.go

type EchoHandler struct {   activeConn sync.Map   closing    atomic.Boolean}

EchoHandler:

type EchoClient struct {   Conn    net.Conn   Waiting wait.Wait}func (c *EchoClient) Close() error {c.Waiting.WaitWithTimeout(10 * time.Second)_ = c.Conn.Close()return nil}

EchoClient:一个客户端就是一个连接。Close方法关闭客户端连接,超时时间设置为10s

func MakeHandler() *EchoHandler {return &EchoHandler{}}func (h *EchoHandler) Handle(ctx context.Context, conn net.Conn) {   // 连接正在关闭,不接收新连接   if h.closing.Get() {      _ = conn.Close()   }   client := &EchoClient{      Conn: conn,   }   h.activeConn.Store(client, struct{}{})   reader := bufio.NewReader(conn)   for {      msg, err := reader.ReadString('\n')      if err != nil {         if err == io.EOF {            logger.Info("connection close")            h.activeConn.Delete(client)         } else {            logger.Warn(err)         }         return      }      // 正在处理业务,不要关掉      client.Waiting.Add(1)      // 将数据原封不动写回去,测试      b := []byte(msg)      _, _ = conn.Write(b)      client.Waiting.Done()   }}func (h *EchoHandler) Close() error {   logger.Info("handler shutting down...")   h.closing.Set(true)   h.activeConn.Range(func(key interface{}, val interface{}) bool {      client := key.(*EchoClient)      _ = client.Close()      return true   })   return nil}

MakeEchoHandler:创建EchoHandler

Handle:处理客户端的连接。

连接正在关闭时,不接收新连接

存储新连接,value用空结构体

使用缓存区接收用户发来的数据,使用\n作为结束的标志

Close:将所有客户端连接关掉

main.go

const configFile string = "redis.conf"var defaultProperties = &config.ServerProperties{   Bind: "0.0.0.0",   Port: 6379,}func fileExists(filename string) bool {   info, err := os.Stat(filename)   return err == nil && !info.IsDir()}func main() {   logger.Setup(&logger.Settings{      Path:       "logs",      Name:       "godis",      Ext:        "log",      TimeFormat: "2022-02-02",   })   if fileExists(configFile) {      config.SetupConfig(configFile)   } else {      config.Properties = defaultProperties   }   err := tcp.ListenAndServeWithSignal(      &tcp.Config{         Address: fmt.Sprintf("%s:%d",            config.Properties.Bind,            config.Properties.Port),      },      EchoHandler.MakeHandler())   if err != nil {      logger.Error(err)   }}

以上就是“GO语言如何实现TCP服务器”这篇文章的所有内容,感谢各位的阅读!相信大家阅读完这篇文章都有很大的收获,小编每天都会为大家更新不同的知识,如果还想学习更多的知识,请关注编程网行业资讯频道。

阅读原文内容投诉

免责声明:

① 本站未注明“稿件来源”的信息均来自网络整理。其文字、图片和音视频稿件的所属权归原作者所有。本站收集整理出于非商业性的教育和科研之目的,并不意味着本站赞同其观点或证实其内容的真实性。仅作为临时的测试数据,供内部测试之用。本站并未授权任何人以任何方式主动获取本站任何信息。

② 本站未注明“稿件来源”的临时测试数据将在测试完成后最终做删除处理。有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341

软考中级精品资料免费领

  • 历年真题答案解析
  • 备考技巧名师总结
  • 高频考点精准押题
  • 2024年上半年信息系统项目管理师第二批次真题及答案解析(完整版)

    难度     813人已做
    查看
  • 【考后总结】2024年5月26日信息系统项目管理师第2批次考情分析

    难度     354人已做
    查看
  • 【考后总结】2024年5月25日信息系统项目管理师第1批次考情分析

    难度     318人已做
    查看
  • 2024年上半年软考高项第一、二批次真题考点汇总(完整版)

    难度     435人已做
    查看
  • 2024年上半年系统架构设计师考试综合知识真题

    难度     224人已做
    查看

相关文章

发现更多好内容

猜你喜欢

AI推送时光机
位置:首页-资讯-服务器
咦!没有更多了?去看看其它编程学习网 内容吧
首页课程
资料下载
问答资讯