即使刚刚打开,新的TCP连接也会引发“使用封闭的网络连接”

When opening new connections, I sometimes get the "use of closed network connection" error at client.Socket.Read(message) in the receive function.

This is odd enough, but what's really weird is that I then get an error for trying to close a closed channel at close(connection.Data), when the client is passed through manager.unregister.

I'm clearly missing something here, because all of my logging suggests that these are entirely new and unused channels and sockets.

Any ideas?

I'm using Golang version 1.10.

type ClientManager struct {
    clients map[*types.Client]bool
    broadcast chan []byte
    register chan *types.Client
    unregister chan *types.Client
}

func CloseSocket(client *types.Client) {
    client.M.Lock()
    defer client.M.Unlock()

    if !client.Closed {
        client.Socket.Close()
        client.Closed = true
    }
}

func (manager *ClientManager) receive(client *types.Client) {
    for {
            message := make([]byte, 4096)
            fmt.Println("client listening:", client)
            length, err := client.Socket.Read(message)

            if err != nil {
                    fmt.Println(err)
                    manager.unregister <- client
                    CloseSocket(client)
                    break
            }
            if length > 0 {
                    request := util.DecodeGob(message)
                    HandleRequest(request, client)
            }
    }
}

func (manager *ClientManager) start() {
    fmt.Println("Listening for TCP connections...")
    for {
            select {
            case connection := <-manager.register:
                    manager.clients[connection] = true
                    fmt.Println("Added new connection!")
            case connection := <-manager.unregister:
                    if _, ok := manager.clients[connection]; ok {
                            close(connection.Data)
                            delete(manager.clients, connection)
                            fmt.Println("manager.clients:", manager.clients)
                            fmt.Println("A connection has terminated!")
                    }
    }
}

func Run(port string) {
    fmt.Println("Starting server...")

    listener, error := net.Listen("tcp", port)

    if error != nil {
            fmt.Println(error)
    }

    manager := ClientManager{
            clients: make(map[*types.Client]bool),
            broadcast: make(chan []byte),
            register: make(chan *types.Client),
            unregister: make(chan *types.Client),
    }

    go manager.start()

    fmt.Println("Server running on port " + port + "!")

    for {
            connection, _ := listener.Accept()
            if error != nil {
                    fmt.Println(error)
            }

            fmt.Println("
Remote address:", connection.RemoteAddr())

            client := &types.Client{Socket: connection, Data: make(chan []byte)}

            manager.register <- client
            go manager.receive(client)
            go manager.send(client)
    }
}

And the logging:

Starting server...
Server running on port :9000!
Listening for TCP connections...

Remote address: [::1]:54857
Added new connection!
client listening: &{0xc42000e528 0xc42007e240 false}
EOF
manager.clients: map[]
A connection has terminated!

Remote address: [::1]:54860
Added new connection!
client listening: &{0xc4200e2000 0xc4200e6000 false}
EOF
manager.clients: map[]
A connection has terminated!

Remote address: [::1]:54863
Added new connection!
client listening: &{0xc420110000 0xc420116000 false}
read tcp [::1]:9000->[::1]:54863: use of closed network connection
panic: close of closed channel

goroutine 5 [running]:
go_gomoku/server.(*ClientManager).start(0xc42000c920)
    /Users/themainframe/go/src/go_gomoku/server/server.go:397 +0x35c
created by go_gomoku/server.Run
    /Users/themainframe/go/src/go_gomoku/server/server.go:434 +0x242

Edit: added mutex to client, no more race output. Bug still present.

Okay, solved. I need to implement backoff for the first write to the channel, because sometimes it failed on the first attempt. Now it gracefully waits a moment and connects perfectly. Here's what it looks like:

func SendBackoff(data []byte, client *types.Client, i int) {
    select {
    case client.Data <- data:
        return
    default:
        time.Sleep(500 * time.Millisecond)

        fmt.Println("Trying again!", i)
        if (i > 5) {
            return
        }
        SendBackoff(data, client, i + 1)
    }
}

func SendToClient(request types.Request, client *types.Client) {
    data, err := util.GobToBytes(request)

    if err != nil {
            fmt.Println(err)
            return
    }

    fmt.Println("Trying to send:", data)

    SendBackoff(data, client, 1)
}