TCP服务器在后台监听而不阻止其他操作

时间:2017-12-13 00:58:47

标签: go tcp goroutine

我正在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()(我想独立地将数据包发送到其他服务器)就不会被触发。我试图改变主要功能并使用goroutine:

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

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

3 个答案:

答案 0 :(得分:1)

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

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

或者使用waitGroups等待代码结束,然后退出。

答案 1 :(得分:0)

以下是使用频道实现此目标的更简洁方法。

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 "))
}

答案 2 :(得分:0)

添加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 "))
}