【问题标题】:TCP Server listening in the background without blocking other operationsTCP Server 后台监听,不阻塞其他操作
【发布时间】:2018-05-26 19:24:45
【问题描述】:

我正在用 Go 编写 TCP 服务器和客户端,作为熟悉这种语言的工作示例。我想编写一个服务器,我们称之为 MyServer,它执行以下操作 - 它有一个后端 TCP 服务器,它侦听传入的消息,但它也有一个客户端,它允许他发送其他消息,独立于接收到的一次。但是,我不知道如何告诉 MyServer 在不阻塞主线程的情况下“在后台”监听。这是我的 TCPServer 的代码:

package main

import (
  "fmt"
  "net"
  "os"
)

func main() {
  startListener()
  doOtherThins()
}

func startListener() {
  listener, err := net.Listen("tcp", "localhost:9998")
  if err != nil {
      fmt.Println("Error listening:", err.Error())
      os.Exit(1)
  }
  defer listener.Close()
  fmt.Println("Listening on " + "localhost:9998")
  for {
      // Listen for an incoming connection.
      conn, err := listener.Accept()
      if err != nil {
          fmt.Println("Error during accepting: ", err.Error())
          os.Exit(1)
      }
      go handleConnection(conn)
  }
}

func handleConnection(conn net.Conn) {
  buf := make([]byte, 1024)

  _, err := conn.Read(buf)
  if err != nil {
    fmt.Println("Error reading:", err.Error())
  }

  conn.Write([]byte("Message correctly received."))
  conn.Close()
}

函数startListener()阻塞了主函数,所以函数doOtherThins()(我想独立发送数据包到其他服务器)永远不会被触发,只要服务器正在监听。我试图改变 main 函数并使用 goroutine:

func main() {
  go startListener()
  doOtherThins()
}

但是服务器没有监听传入的数据包(它只是触发doOtherThins()并结束main())。 是否可以在后台旋转监听器,让主线程也可以执行其他操作?

【问题讨论】:

    标签: go tcp goroutine


    【解决方案1】:

    你的最后一个例子应该做你想做的事,问题是主线程在你做任何事情之前就结束了。有 2 个解决方案在另一个 goroutine 上启动 doOtherThins(),然后调用 startListener() 会阻塞但另一个 goroutine 已经在运行:

    func main() {
      go doOtherThins()
      startListener()
    }
    

    或者使用waitGroups等到代码结束再退出。

    【讨论】:

      【解决方案2】:

      这是使用通道实现此目的的更简洁的方法。

      package main
      
      import (
          "net/http"
          "fmt"
      )
      
      func main() {
          // Create a channel to synchronize goroutines
          finish := make(chan bool)
      
          server8001 := http.NewServeMux()
          server8001.HandleFunc("/foo", foo8001)
          server8001.HandleFunc("/bar", bar8001)
      
          go func() {
              http.ListenAndServe(":8001", server8001)
          }()
      
          go func() {
              //do other things in a separate routine
              fmt.Println("doing some work")
              // you can also start a new server on a different port here
          }()
      
          // do other things in the main routine
      
          <-finish //wait for all the routines to finish
      }
      
      func foo8001(w http.ResponseWriter, r *http.Request) {
          w.Write([]byte("Listening on 8001: foo "))
      }
      
      func bar8001(w http.ResponseWriter, r *http.Request) {
          w.Write([]byte("Listening on 8001: bar "))
      }
      

      【讨论】:

      • 我宁愿阻止监听操作,而不是添加新的频道。无论你接下来做什么,在调用客户端之前你都需要睡一会儿。你应该有类似 Listen, check err ||稍等,开始新的例程消费客户端。
      【解决方案3】:

      添加另一个Anuruddha答案的变体

      package main
      
      import (
          "io"
          "net/http"
          "os"
          "time"
      )
      
      func main() {
          server8001 := http.NewServeMux()
          server8001.HandleFunc("/foo", foo8001)
          server8001.HandleFunc("/bar", bar8001)
      
          unblock(func() error {
              return http.ListenAndServe(":8001", server8001)
          })//forgot err check, must be done!
      
          res, err := http.Get("http://0.0.0.0:8001/foo")
          if err != nil {
              panic(err)
          }
          defer res.Body.Close()
          io.Copy(os.Stdout, res.Body)
          os.Exit(0)
      }
      
      func unblock(h func() error) error {
          w := make(chan error)
          go func() {
              w <- h()
          }()
          select {
          case err := <-w:
              return err
          case <-time.After(time.Millisecond * 50):
              return nil
          }
      }
      
      func foo8001(w http.ResponseWriter, r *http.Request) {
          w.Write([]byte("Listening on 8001: foo "))
      }
      
      func bar8001(w http.ResponseWriter, r *http.Request) {
          w.Write([]byte("Listening on 8001: bar "))
      }
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2014-03-28
        • 1970-01-01
        • 1970-01-01
        • 2016-08-26
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多