使用网络编程在golang中进行信号处理

Trying to learn golang network programming. Implemented simple server with unix domain sockets. The server has:

  1. taskChan: Unbuf channel for client connection object.
  2. sigChan: buf channel for signals
  3. srvDone: unbuf channel to indicate server done.
  4. blocks all async signals except SIGINT/SIGHUP.

Then starts signalHandler(), three taskWorker(), acceptClientReqs() in goroutines. On receiving the signal (send the signal to this process with kill -SIGINT ), the signal handler sets global variable serverShutdown. My intention is - all the goroutines to read serverShutdown and exit cleanly. However, some times all the worker threads exiting and other times they don't. Do you see any bug in my code? Or am I missing some thing?

package main

import "encoding/json"
import "fmt"
import "net"
import "os"
import "os/signal"
import "syscall"
import "time"

var srvShutdown = false

type netServer struct {
    listener     *net.UnixListener // unix local sock
    taskChan     chan net.Conn     // unbuf channel for client connections
    sigChan      chan os.Signal    // buffered channel
    srvDone      chan bool
    numOfWorkers int
}

// initialize signals.
func initSignals() {
    // block all async signals to this server except SIGINT/SIGHUP
    signal.Ignore()
}

// initializes server:
//   1) creates listener.
//   2) creates task channel - client connections accepted on this channel.
//   3) initializes few other things such as signal channel, num of workers etc.
func initServer() (*netServer, error) {
    sockAddr, err := net.ResolveUnixAddr("unix", "/tmp/unix.socket")
    if err != nil {
        fmt.Println("initServer() failed on ResolveUnixAddr(): ", err)
        return nil, err
    }
    srvlistener, err := net.ListenUnix("unix", sockAddr)
    if err != nil {
        fmt.Println("initServer() failed on ListenUnix(): ", err)
        return nil, err
    }
    netSrv := &netServer{
        listener:     srvlistener,
        taskChan:     make(chan net.Conn),
        sigChan:      make(chan os.Signal, 1),
        srvDone:      make(chan bool),
        numOfWorkers: 3,
    }
    initSignals()
    return netSrv, nil
}

// Accept client connections. Each connection is directed onto
// a channel. Worker threads pickup and work on these connections.
func (netSrv *netServer) acceptClientReqs() {
    fmt.Println("Listening on /tmp/unix.socket")
    for srvShutdown != true {
        netSrv.listener.SetDeadline(time.Now().Add(time.Second))
        conn, err := netSrv.listener.Accept()
        if err != nil {
            fmt.Println("Accept failed : ", err)
            continue
        }
        netSrv.taskChan <- conn
    }
    netSrv.srvDone <- true
    fmt.Println("Exiting server")
}

// Pickup a client connection, work on it and close connection.
func (netSrv *netServer) taskWorker(workerId int) {
    fmt.Println("taskWorker(): starting worker-", workerId)
    // for conn := range netSrv.taskChan {
    for srvShutdown != true {
        conn := <-netSrv.taskChan
        if conn == nil {
            fmt.Println("taskWorker(): worker-", workerId, " continue")
            continue
        }
        fmt.Println("taskWorker(): worker-", workerId, " processing client connection: ", conn)
        dec := json.NewDecoder(conn)
        var Cmd string
        dec.Decode(&Cmd)
        fmt.Println(" cmd: ", Cmd)

        // reply to client
        _, err := conn.Write([]byte("AUTH SUCCESS"))
        if err != nil {
            return
        }
        conn.Close()
        fmt.Println("taskWorker(): worker-", workerId, " processed client connection: ", conn)
    }
    fmt.Println("taskWorker(): exiting worker-", workerId)
}

// Handle SIGINT and SIGHUP for now.
func (netSrv *netServer) signalHandler() {
    signal.Notify(netSrv.sigChan, syscall.SIGINT, syscall.SIGHUP)
    sig := <-netSrv.sigChan
    fmt.Println("signalHandler() received signal", sig)
    srvShutdown = true
}

func (netSrv *netServer) cleanUp() {
    close(netSrv.taskChan)
    netSrv.listener.Close()
    close(netSrv.srvDone)
}

func main() {
    srv, err := initServer()
    if err != nil {
        fmt.Println("initServer() failed - ", err)
        return
    }
    go srv.signalHandler()

    for i := 1; i <= srv.numOfWorkers; i++ {
        go srv.taskWorker(i)
    }
    go srv.acceptClientReqs()

    // wait server to complete
    <-srv.srvDone
    srv.cleanUp()
}