user1206899
user1206899

Reputation: 1910

How to close a reading TCP connection from another goroutine?

My server senario is like this: One io thread is doing read on a tcp connection all the time. After a while the control thread may decide to close it due to low activity or some other reason. If c.Close() is called the io thread will report an error like this: read tcp xxx->xxx: use of closed network connection.

The code is like this:

func recv(c net.Conn) {
    input := bufio.NewScanner(c)
    for input.Scan() {
        msg <- input.Text()
        ...
    }
}

//main
conn, err := s.Accept()
...
go recv(conn)
for {
    select {
    case m := <-msg:
         ...
    case <-timeout:
        conn.Close() // oops!
    }
}

I could ignore the error perhaps but I wonder whether there is a better way.

Upvotes: 3

Views: 7166

Answers (3)

Ankit Deshpande
Ankit Deshpande

Reputation: 3604

func recv(c *net.Conn) {
    if c == nil {
       return
    }
    input := bufio.NewScanner(*c)
    for input.Scan() {
        msg <- input.Text()
        ...
    }
}

//main
conn, err := s.Accept()
c := &conn
...
go recv(&c)
for {
    select {
    case m := <-msg:
         ...
    case <-timeout:
        conn.Close()
        c = nil
    }
}

Not sure if this is the best approach. When you close the conn, you can set it to nil and not read from a nil conn value.

Upvotes: 0

Thundercat
Thundercat

Reputation: 120941

The options are to close the connection or set the read deadline to a time in the past. Either way, read on the connection will return an error.

The simplest approach for handling these errors is to handle all errors returned from read on the network connection uniformly: close the connection, clean up the resources associated with the connection and move on. It's OK to close the connection twice.

func recv(c net.Conn, msg chan string) {
    defer close(msg) // Notify main goroutine that recv is done.
    defer c.Close()  // Release resources.

    input := bufio.NewScanner(c)

    // Loop reading lines until read on c returns an error.
    // These errors include io.EOF (normal close by the peer),
    // the error caused by the main goroutine closing the connection
    // and other networking errors.
    for input.Scan() {
        msg <- input.Text()
    }
}

// main

conn, err := s.Accept()
if err != nil {
    // handle error
}

msg := make(chan string)
go recv(conn, msg)

for {
    select {
    case m, ok := <-msg:
        if !ok {
            // The recv goroutine closed the channel and the connection.
            return
        }
        fmt.Println(m)
    case <-timeout:
        // Closing the connection causes the recv goroutine to break
        // out of the loop. If the recv goroutine received a line of 
        // text and has yet sent the text to the msg channel, then 
        // a return from main at this point will cause the recv goroutine
        // to block forever. To avoid this, continue looping until until
        // the recv goroutine signals that it's done by closing the msg
        // channel.
        conn.Close()
    }
}

}

The application can record that it's shutting down the connection and handle read errors after that point in a special way, but only do that if the application needs to do something special in this situation.

Upvotes: 4

fwhez
fwhez

Reputation: 581

There're some errors that suggests handle alone like:

EOF: a long received message has finished read, everything is normal,so continue.

"An existing connection was forcibly closed by the remote host": client closes app.It's nomal and talking finished, so return.

else: some loginc or server error, need to be log and fix

handler(c net.Conn){
    defer c.Close()
    for {
       ...
       if e!=nil {
            if e == io.EOF{
                continue
            }
            if strings.Contains(e.Error(), "An existing connection was forcibly closed by the remote host.") {
                return
            }
            log.Println(e.Error())
            return
        }
    }
}

On your case, you don't want to handle the error contains 'use of closed network connection'.It's ok ignore it and remember to close the loop.Otherwise some memory will leak and a routine is hung on.There might be a hidden error which you ignore throwing over and over again.

Upvotes: 0

Related Questions