This is actually a follow up on this question: How to know TCP connection is closed in Golang net package? which doesn't work in my implementation, and I'm trying to figure out why.
The idea is, I have a server that accepts TCP connections and waits for the client to close them. It waits for 5 seconds, and every second it tries to read from the TCP connection, if this gives an EOF error, that would indicate that the client has closed the connection.
This is the servers code:
func server(done chan bool) {
l, _ := net.Listen("tcp", serverAddress)
// Listen for incomming TCP connections
conn, err := l.Accept()
if err != nil {
log.Fatalf("l.Accept(): got error on accept: %v", err)
}
defer conn.Close()
// Read periodically from the TCP connection to see if we can find
// out when the client has closed his end.
go func() {
for {
conn.SetReadDeadline(time.Now().Add(10 * time.Millisecond))
b, err := conn.Read([]byte{})
fmt.Printf("Read %v bytes with err %v \n", b, err)
if err == io.EOF {
conn.Close()
conn = nil
break
} else {
var zero time.Time
conn.SetReadDeadline(zero)
}
time.Sleep(1 * time.Second)
}
done <- true
}()
// Do this for 5 seconds, than quit
time.Sleep(5 * time.Second)
done <- true
}
So the client sets up a connection, waits for two seconds and then closes it. The clients code is as follows:
func main() {
done := make(chan bool, 1)
go server(done)
// Give the listener some time to set up
time.Sleep(200 * time.Millisecond)
// Dial the server
conn, err := net.Dial("tcp", serverAddress)
if err != nil {
log.Fatalf("net.Dial(): got %v, want no error", err)
}
defer conn.Close()
go func() {
// After two seconds, close the TCP connection to the server
time.Sleep(2 * time.Second)
fmt.Printf("Client closes conn now with err %v\n", conn.Close())
}()
// Wait untill server is done
<-done
}
I would expect that the server would get an EOF error when reading, after the client has closed the connection, however the output I get is:
Read 0 bytes with err <nil>
Read 0 bytes with err <nil>
Client closes conn now with err <nil>
Read 0 bytes with err <nil>
Read 0 bytes with err <nil>
Read 0 bytes with err <nil>
I have the wireshark output here. It actually shows the FIN was send by the client (package 11), but only 3 seconds later the server sends it's own FIN:
Wireshark output
So basically I can't figure out why I'm not getting any error on the read on the servers side.
Related
I would like to know if there's any way of making a Go HTTP server aware of a timeout in the client, and immediately terminate the processing of the ongoing request. Currently, I've tried setting timeouts on the client side that actually work as expected on their side and the request finishes with context deadline exceeded (Client.Timeout exceeded while awaiting headers) after the timeout is reached.
req, err := http.NewRequest(http.MethodGet, URL, nil)
if err != nil {
log.Fatal(err)
}
client := http.Client{Timeout: time.Second}
_, err = client.Do(req)
if err != nil {
log.Fatal(err)
}
I've also tried with different versions of the client code, like using a request with context, and got the same result, which is ok for the client side.
However, when it comes to detect the timeout on the server side, it turns out that the processing of the request continues until the server finishes its work, regardless of the timeout in the client, and what I would like to happen (I don't know if it's even possible) is to immediately terminate and abort the processing once the client has timed out.
The sever side code would be something like this (just for the sake of the example, in production code it would be something more sophisticated):
func handler(w http.ResponseWriter, r *http.Request) {
fmt.Println("before sleep")
time.Sleep(3 * time.Second)
fmt.Println("after sleep")
fmt.Fprintf(w, "Done!")
}
func main() {
http.HandleFunc("/", handler)
log.Fatal(http.ListenAndServe(":8080", nil))
}
When the previous code is run, and a request hits the HTTP server, the following sequence of events occurs:
Server prints before sleep
Server falls asleep
Client times out and terminates with error context deadline exceeded (Client.Timeout exceeded while awaiting headers)
Server wakes up and prints after sleep
But what I would like to happen is to terminate the process at step 3.
Thank being said, I'd like to know your thoughts about it, and whether you think what I want to do is feasible or not.
There are a few different ideas at play here. First, to confirm what you are asking for, it looks like you want to make a client disconnection trigger the whole server to be shut down. To do this you can do the following:
Add a context.WithCancel or a channel to use to propagate a shutdown event
Watch for a disconnect in your http handler and cancel the context
Add a goroutine that shuts down your server when the channel is closed
Here is a complete sample program that produces the following output:
go run ./main.go
2021/03/04 17:56:44 client: starting request
2021/03/04 17:56:44 server: handler started
2021/03/04 17:56:45 client: deadline exceeded
2021/03/04 17:56:45 server: client request canceled
2021/03/04 17:56:45 server: performing server shutdown
2021/03/04 17:56:45 waiting for goroutines to finish
2021/03/04 17:56:45 All exited!
// main.go
package main
import (
"context"
"errors"
"fmt"
"io/ioutil"
"log"
"net/http"
"os"
"sync"
"time"
)
func main() {
wg := &sync.WaitGroup{}
srvContext, srvCancel := context.WithCancel(context.Background())
defer srvCancel()
srv := http.Server{
Addr: ":8000",
Handler: http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
log.Printf("server: handler started")
select {
case <-time.After(2 * time.Second):
log.Printf("server: completed long request")
w.WriteHeader(http.StatusOK)
w.Write([]byte("OK"))
case <-r.Context().Done():
log.Printf("server: client request canceled")
srvCancel()
return
}
}),
}
// add a goroutine that watches for the server context to be canceled
// as a signal that it is time to stop the HTTP server.
wg.Add(1)
go func() {
defer wg.Done()
<-srvContext.Done()
log.Printf("server: performing server shutdown")
// optionally add a deadline context to avoid waiting too long
if err := srv.Shutdown(context.TODO()); err != nil {
log.Printf("server: shutdown failed with context")
}
}()
// just simulate making the request after a brief delay
wg.Add(1)
go makeClientRequest(wg)
if err := srv.ListenAndServe(); err != nil && err != http.ErrServerClosed {
fmt.Fprintf(os.Stderr, "Server failed listening with error: %v\n", err)
return
}
log.Printf("waiting for goroutines to finish")
wg.Wait()
log.Printf("All exited!")
}
func makeClientRequest(wg *sync.WaitGroup) {
defer wg.Done()
// delay client request
time.Sleep(500 * time.Millisecond)
log.Printf("client: starting request")
ctx, cancel := context.WithTimeout(context.Background(), 1*time.Second)
defer cancel()
req, err := http.NewRequestWithContext(ctx, http.MethodGet, "http://127.0.0.1:8000", http.NoBody)
if err != nil {
log.Fatalf("failed making client request")
}
resp, err := http.DefaultClient.Do(req)
if err != nil {
if errors.Is(err, context.DeadlineExceeded) {
log.Printf("client: deadline exceeded")
} else {
log.Printf("client: request error: %v", err)
}
return
}
// got a non-error response
defer resp.Body.Close()
body, _ := ioutil.ReadAll(resp.Body)
log.Printf("client: got response %d %s", resp.StatusCode, string(body))
}
I'm trying to send an HTTP request over a net.Conn TCP connection and read the subsequent response, but I never seem to receive an EOF from the connection when reading. This makes functions like io.Copy or ioutil.ReadAll useless, as they block forever.
Code:
client.go
const requestString = "GET /test HTTP/1.1\r\n" + "Host: 127.0.0.1:8080\r\n\r\n"
func main() {
dialer := net.Dialer{}
conn, err := dialer.Dial("tcp", "127.0.0.1:8080")
if err != nil {
log.Fatalln(err)
}
defer conn.Close()
_, err = conn.Write([]byte(requestString))
if err != nil {
log.Fatalln(err)
}
buf := make([]byte, 1024)
data := make([]byte, 0)
length := 0
for {
n, err := conn.Read(buf)
if err != nil {
if err != io.EOF {
fmt.Printf("Read error: %v\n", err)
}
break
}
data = append(data, buf[:n]...)
length += n
fmt.Printf("Partial read:\n%s\n", string(buf[:n]))
}
fmt.Println("Response:")
fmt.Println(string(data))
}
server.go
func main() {
http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
fmt.Fprintf(w, "Hello, %s", r.URL.Path[1:])
})
http.ListenAndServe(":8080", nil)
}
Output of running client.go with server.go already running:
Partial read:
HTTP/1.1 200 OK
Date: Wed, 25 Nov 2020 04:09:32 GMT
Content-Length: 11
Content-Type: text/plain; charset=utf-8
Hello, test
The first call to Read() returns the expected response, but lacks an EOF. The subsequent call to Read() hangs forever, and I'm not sure how to determine when the connection has finished. If I interrupt the server process, the client connection closes properly and the response is complete.
How can I either a) receive an EOF or b) determine when the response is complete? All examples I've seen online have something close to my code working, so I'm not sure what I'm doing wrong.
Adding Connection: close to the request headers makes sure that the connection... well, closes.
I wrote a golang program which run well in the past several months in ubuntu 12.04 LTS until I upgraded it to 14.04 LTS
My program is focused on sending HTTP requests which send about 2-10 HTTP requests per second. The HTTP request address vary.
When the problem occurs, first, some of the requests shows read tcp [ip]:[port]: i/o timeout, then after several minutes all requests show read tcp [ip]:[port]: i/o timeout, not any request can be sent.
I restart the program, everything become right again.
All of our servers(2 server) have such problem after upgraded from 12.04 to 14.04
I create new goroutine for every request
the problem does not occur in the same interval, sometimes it won't occur one or two day, sometimes It occur twice in an hour
Bellow is my code requesting HTTP Address:
t := &http.Transport{
Dial: timeoutDial(data.Timeout),
TLSClientConfig: &tls.Config{InsecureSkipVerify: true},
}
//req := s.ParseReq(data)
req := data.convert2Request()
if req == nil {
return
}
var resp *http.Response
if data.Redirect {
c := &http.Client{
Transport: t,
}
resp, err = c.Do(req)
} else {
resp, err = t.RoundTrip(req)
}
data.updateTry()
r := s.ParseResp(data, resp, err)
updateTry:
func (d *SendData) updateTry() {
d.Try++
d.LastSend = time.Now()
}
timeoutDial:
func timeoutDial(timeout int) func(netw, addr string) (net.Conn, error) {
if timeout <= 0 {
timeout = 10
}
return func(netw, addr string) (net.Conn, error) {
deadline := time.Now().Add(time.Duration(timeout) * time.Second)
c, err := net.DialTimeout(netw, addr, time.Second*time.Duration(timeout+5))
if err != nil {
return nil, err
}
c.SetDeadline(deadline)
return c, nil
}
}
and My dealing with response is:
func (s *Sender) ParseResp(data SendData, resp *http.Response, err error) (r Resp) {
r = Resp{URL: data.URL}
if err != nil {
r.Err = err.Error()
} else {
r.HttpCode = resp.StatusCode
r.Header = resp.Header
r.URL = resp.Request.URL.String()
defer resp.Body.Close()
// we just read part of response and log it.
reader := bufio.NewReader(resp.Body)
buf := make([]byte, bytes.MinRead) // 512 byte
for len(r.Body) < 1024 { // max 1k
var n int
if n, _ = reader.Read(buf); n == 0 {
break
}
r.Body += string(buf[:n])
}
}
return
}
I also found setting in /etc/sysctl.conf which can make the problem happen less frequently:
net.core.somaxconn = 65535
net.netfilter.nf_conntrack_max = 655350
net.netfilter.nf_conntrack_tcp_timeout_established = 1200
I need help for solving this problem.
It seems like this but I don't see any solution https://bugs.launchpad.net/juju-core/+bug/1307434
To more explicitly state what Not_a_Golfer and OneOfOne have said, when you're done with the response, you need to close the connection which has been left open (through the Body field which is an io.ReadCloser). So basically, one simple though would be to change the code pertaining to making an http request to:
var resp *http.Response
if data.Redirect {
c := &http.Client{
Transport: t,
}
resp, err = c.Do(req)
} else {
resp, err = t.RoundTrip(req)
}
if err == nil {
defer resp.Body.Close() // we need to close the connection
}
Without seeing the code to timeoutDial, my wild guess is that you don't close the connection when you're done with it.
I work on a tcp server in golang ,recently I found a problem, when the client connected to the server, use netstat -nat|grep -i "55555" to check , it tells me the connection has established and Recv-Q has message , but Accept() need take about several seconds to return . I'm confused about it, could anyone tell me why or give any advice how to find the problem?
Here is my main code
tcpAddr, err := net.ResolveTCPAddr("tcp4", server.Host)
if err != nil {
log.Error(err.Error())
os.Exit(-1)
}
listener, err := net.ListenTCP("tcp", tcpAddr)
if err != nil {
log.Error(err.Error())
os.Exit(-1)
}
for {
conn, err := listener.Accept()
if err != nil {
continue
}
log.Debug("Accept a new connection")
go handleClient(conn)//deal the connection
}
P.S.
In handleClient, it's too long, I can't paste it here, only one more thing, I set tcp keep alive to the connection , use this package github.com/felixge/tcpkeepalive
kaConn, err := tcpkeepalive.EnableKeepAlive(connection)
if err != nil {
log.Debug("EnableKeepAlive err ", err)
}else{
kaConn.SetKeepAliveIdle(30*time.Second)
kaConn.SetKeepAliveCount(4)
kaConn.SetKeepAliveInterval(5*time.Second)
}
Update :
When I stop tcp keep alive, it works and Accept return instantly .
But even if not stop tcp keep alive, Accept return instantly when I use a script to test.Is it to do with the client?
I'm trying to detect sending failures by inspecting the error returned by golang TCPConn.Write, but it's nil. I also tried using TCPConn.SetWriteDeadline without success.
That's how things happen:
the server starts
a client connects
the server sends a message and the client receives it
the client shuts down
the server sends one more message: no error
the server sends the third message: only now the error appears
Question: why only the second message to a non-existing client results in an error? How should the case be handled properly?
The code follows:
package main
import (
"net"
"os"
"bufio"
"fmt"
"time"
)
func AcceptConnections(listener net.Listener, console <- chan string) {
msg := ""
for {
conn, err := listener.Accept()
if err != nil {
panic(err)
}
fmt.Printf("client connected\n")
for {
if msg == "" {
msg = <- console
fmt.Printf("read from console: %s", msg)
}
err = conn.SetWriteDeadline(time.Now().Add(time.Second))
if err != nil {
fmt.Printf("SetWriteDeadline failed: %v\n", err)
}
_, err = conn.Write([]byte(msg))
if err != nil {
// expecting an error after sending a message
// to a non-existing client endpoint
fmt.Printf("failed sending a message to network: %v\n", err)
break
} else {
fmt.Printf("msg sent: %s", msg)
msg = ""
}
}
}
}
func ReadConsole(network chan <- string) {
console := bufio.NewReader(os.Stdin)
for {
line, err := console.ReadString('\n')
if err != nil {
panic(err)
} else {
network <- line
}
}
}
func main() {
listener, err := net.Listen("tcp", "localhost:6666")
if err != nil {
panic(err)
}
println("listening on " + listener.Addr().String())
consoleToNetwork := make(chan string)
go AcceptConnections(listener, consoleToNetwork)
ReadConsole(consoleToNetwork)
}
The server console looks like this:
listening on 127.0.0.1:6666
client connected
hi there!
read from console: hi there!
msg sent: hi there!
this one should fail
read from console: this one should fail
msg sent: this one should fail
this one actually fails
read from console: this one actually fails
failed sending a message to network: write tcp 127.0.0.1:51194: broken pipe
The client looks like this:
package main
import (
"net"
"os"
"io"
//"bufio"
//"fmt"
)
func cp(dst io.Writer, src io.Reader, errc chan<- error) {
// -reads from src and writes to dst
// -blocks until EOF
// -EOF is not an error
_, err := io.Copy(dst, src)
// push err to the channel when io.Copy returns
errc <- err
}
func StartCommunication(conn net.Conn) {
//create a channel for errors
errc := make(chan error)
//read connection and print to console
go cp(os.Stdout, conn, errc)
//read user input and write to connection
go cp(conn, os.Stdin, errc)
//wait until nil or an error arrives
err := <- errc
if err != nil {
println("cp error: ", err.Error())
}
}
func main() {
servAddr := "localhost:6666"
tcpAddr, err := net.ResolveTCPAddr("tcp", servAddr)
if err != nil {
println("ResolveTCPAddr failed:", err.Error())
os.Exit(1)
}
conn, err := net.DialTCP("tcp", nil, tcpAddr)
if err != nil {
println("net.DialTCP failed:", err.Error())
os.Exit(1)
}
defer conn.Close()
StartCommunication(conn)
}
EDIT: Following JimB's suggestion I came up with a working example. Messages don't get lost any more and are re-sent in a new connection. I'm not quite sure though how safe is it to use a shared variable (connWrap.IsFaulted) between different go routines.
package main
import (
"net"
"os"
"bufio"
"fmt"
)
type Connection struct {
IsFaulted bool
Conn net.Conn
}
func StartWritingToNetwork(connWrap * Connection, errChannel chan <- error, msgStack chan string) {
for {
msg := <- msgStack
if connWrap.IsFaulted {
//put it back for another connection
msgStack <- msg
return
}
_, err := connWrap.Conn.Write([]byte(msg))
if err != nil {
fmt.Printf("failed sending a message to network: %v\n", err)
connWrap.IsFaulted = true
msgStack <- msg
errChannel <- err
return
} else {
fmt.Printf("msg sent: %s", msg)
}
}
}
func StartReadingFromNetwork(connWrap * Connection, errChannel chan <- error){
network := bufio.NewReader(connWrap.Conn)
for (!connWrap.IsFaulted) {
line, err := network.ReadString('\n')
if err != nil {
fmt.Printf("failed reading from network: %v\n", err)
connWrap.IsFaulted = true
errChannel <- err
} else {
fmt.Printf("%s", line)
}
}
}
func AcceptConnections(listener net.Listener, console chan string) {
errChannel := make(chan error)
for {
conn, err := listener.Accept()
if err != nil {
panic(err)
}
fmt.Printf("client connected\n")
connWrap := Connection{false, conn}
go StartReadingFromNetwork(&connWrap, errChannel)
go StartWritingToNetwork(&connWrap, errChannel, console)
//block until an error occurs
<- errChannel
}
}
func ReadConsole(network chan <- string) {
console := bufio.NewReader(os.Stdin)
for {
line, err := console.ReadString('\n')
if err != nil {
panic(err)
} else {
network <- line
}
}
}
func main() {
listener, err := net.Listen("tcp", "localhost:6666")
if err != nil {
panic(err)
}
println("listening on " + listener.Addr().String())
consoleToNetwork := make(chan string)
go AcceptConnections(listener, consoleToNetwork)
ReadConsole(consoleToNetwork)
}
This isn't Go specific, and is a artifact of the underlying TCP socket showing through.
A decent diagram of the TCP termination steps is at the bottom of this page:
http://www.tcpipguide.com/free/t_TCPConnectionTermination-2.htm
The simple version is that when the client closes its socket, it sends a FIN, and receives an ACK from the server. It then waits for the server to do the same. Instead of sending a FIN though, you're sending more data, which is discarded, and the client socket now assumes that any more data coming from you is invalid, so the next time you send you get an RST, which is what bubbles up into the error you see.
Going back to your program, you need to handle this somehow. Generally you can think of whomever is in charge of initiating a send, is also in charge of initiating termination, hence your server should assume that it can continue to send until it closes the connection, or encounters an error. If you need to more reliably detect the client closing, you need to have some sort of client response in the protocol. That way recv can be called on the socket and return 0, which alerts you to the closed connection.
In go, this will return an EOF error from the connection's Read method (or from within the Copy in your case). SetWriteDeadline doesn't work because a small write will go though and get dropped silently, or the client will eventually respond with an RST, giving you an error.