溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊(cè)×
其他方式登錄
點(diǎn)擊 登錄注冊(cè) 即表示同意《億速云用戶服務(wù)條款》

GO語(yǔ)言如何實(shí)現(xiàn)TCP服務(wù)器

發(fā)布時(shí)間:2023-03-25 11:59:37 來源:億速云 閱讀:178 作者:iii 欄目:開發(fā)技術(shù)

今天小編給大家分享一下GO語(yǔ)言如何實(shí)現(xiàn)TCP服務(wù)器的相關(guān)知識(shí)點(diǎn),內(nèi)容詳細(xì),邏輯清晰,相信大部分人都還太了解這方面的知識(shí),所以分享這篇文章給大家參考一下,希望大家閱讀完這篇文章后有所收獲,下面我們一起來了解一下吧。

interface/tcp/Handler.go

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

Handler:業(yè)務(wù)邏輯的處理接口

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:?jiǎn)?dòng)tcp服務(wù)器的配置

Address:監(jiān)聽地址

ListenAndServe:ctx是上下文,可以傳遞一些參數(shù)。死循環(huán)中接收到新連接時(shí),讓一個(gè)協(xié)程去處理連接

如果listener.Accept()出錯(cuò)了就會(huì)break跳出來,這時(shí)候需要等待已經(jīng)服務(wù)的客戶端退出。使用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在退出的時(shí)候需要關(guān)掉。如果用戶直接kill掉了程序,我們也需要關(guān)掉listener和handler,這時(shí)候要使用closeChan,一旦接收到關(guān)閉信號(hào),就執(zhí)行關(guān)閉邏輯

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
}

當(dāng)系統(tǒng)對(duì)程序發(fā)送信號(hào)時(shí),sigCh會(huì)接收到信號(hào)

tcp/echo.go

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

EchoHandler:

  • activeConn:記錄連接

  • closing:是否正在關(guān)閉,有并發(fā)競(jìng)爭(zhēng),使用atomic.Boolean

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:一個(gè)客戶端就是一個(gè)連接。Close方法關(guān)閉客戶端連接,超時(shí)時(shí)間設(shè)置為10s

func MakeHandler() *EchoHandler {
	return &EchoHandler{}
}

func (h *EchoHandler) Handle(ctx context.Context, conn net.Conn) {
   // 連接正在關(guān)閉,不接收新連接
   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
      }
      // 正在處理業(yè)務(wù),不要關(guān)掉
      client.Waiting.Add(1)
      // 將數(shù)據(jù)原封不動(dòng)寫回去,測(cè)試
      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:創(chuàng)建EchoHandler

Handle:處理客戶端的連接。

1.連接正在關(guān)閉時(shí),不接收新連接

2.存儲(chǔ)新連接,value用空結(jié)構(gòu)體

3.使用緩存區(qū)接收用戶發(fā)來的數(shù)據(jù),使用\n作為結(jié)束的標(biāo)志

Close:將所有客戶端連接關(guān)掉

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語(yǔ)言如何實(shí)現(xiàn)TCP服務(wù)器”這篇文章的所有內(nèi)容,感謝各位的閱讀!相信大家閱讀完這篇文章都有很大的收獲,小編每天都會(huì)為大家更新不同的知識(shí),如果還想學(xué)習(xí)更多的知識(shí),請(qǐng)關(guān)注億速云行業(yè)資訊頻道。

向AI問一下細(xì)節(jié)

免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如果涉及侵權(quán)請(qǐng)聯(lián)系站長(zhǎng)郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。

AI