Ziva
Ziva

Reputation: 3501

TCP Server listening in the background without blocking other operations

I'm writing a TCP Server and Client in Go, just as a working example to get familiar with this language. I want to write a server, let's call it MyServer, which does the following - it has a backend TCP Server, which listens for incoming messages, but it also has a Client which allows him to send other messages, independently on the received once. However, I don't know how to tell MyServer to listen "in the background", without blocking the main thread. Here is the code for my 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()
}

Function startListener() blocks the main function, so the function doOtherThins() (which I want to independently send packets to other servers) is never triggered as long as the server is listening. I tried to change the main function and use the goroutine:

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

But then the server is not listening for the incoming packets (it just triggers doOtherThins() and ends main()). Is it possible to spin the listener in the background, to allow the main thread do also other operations?

Upvotes: 0

Views: 2282

Answers (3)

user4466350
user4466350

Reputation:

adding another variation of Anuruddha answer

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

Upvotes: -1

Anuruddha
Anuruddha

Reputation: 3245

Here is a cleaner way of achieving this using channels.

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

Upvotes: -1

Topo
Topo

Reputation: 5002

Your last example should do what you want, the issue is that the main thread ends before you can do anything. There's 2 solutions start doOtherThins() on another goroutine and then call startListener() which blocks but the other goroutine is already running:

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

Or use waitGroups to wait until the code ends before exiting.

Upvotes: 1

Related Questions