How to set timeout while doing a net.DialTCP in golang? - go

As net.DialTCP seems like the only way to get net.TCPConn, I'm not sure how to set timeouts while doing the DialTCP. https://golang.org/pkg/net/#DialTCP
func connectAddress(addr *net.TCPAddr, wg *sync.WaitGroup) error {
start := time.Now()
conn, err := net.DialTCP("tcp", nil, addr)
if err != nil {
log.Printf("Dial failed for address: %s, err: %s", addr.String(), err.Error())
return err
}
elasped := time.Since(start)
log.Printf("Connected to address: %s in %dms", addr.String(), elasped.Nanoseconds()/1000000)
conn.Close()
wg.Done()
return nil
}

Use net.Dialer with either the Timeout or Deadline fields set.
d := net.Dialer{Timeout: timeout}
conn, err := d.Dial("tcp", addr)
if err != nil {
// handle error
}
A variation is to call Dialer.DialContext with a deadline or timeout applied to the context.
Type assert to *net.TCPConn if you specifically need that type instead of a net.Conn:
tcpConn, ok := conn.(*net.TCPConn)

One can use net.DialTimeout:
func DialTimeout(network, address string, timeout time.Duration) (Conn, error)
DialTimeout acts like Dial but takes a timeout.
The timeout includes name resolution, if required. When using TCP, and the
host in the address parameter resolves to multiple IP addresses, the timeout
is spread over each consecutive dial, such that each is given an appropriate
fraction of the time to connect.
See func Dial for a description of the network and address parameters.

Related

How to prevent SetReadDeadline from timing out connection between reads in go?

I'm writing a simple TLV style service using TCP in go, that should be able to handle multiple connections, and allow for multiple messages to be sent on the same connection.
I need to be able to ensure that a message with incorrect length doesn't block the connection indefinitely, but I need to ensure that subsequent messages can be sent on the same connection without timing out.
I am using io.ReadFull to read in a fixed number of bytes that contain the type and length, and then when I receive the length, I am calling io.ReadFull again and reading in length number of bytes.
If I am reading in 4 bytes for the type and length, but the client only sends 3 for whatever reason, io.ReadFull will hang. Similarly, if the client sends 300 for the length, but the length should only be 200, io.ReadFull will hang, blocking all communication on that channel.
I've tried using conn.SetReadDeadline() and setting it to 5 seconds for this example. This causes the connection to timeout if the improper length is sent, which is great. The issue is that the connection will timeout if the next request isn't sent until after >5 seconds.
// ...
for {
conn, err := lis.Accept()
if err != nil {
fmt.Println(err)
continue
}
fmt.Println("Connected")
go handleC(conn)
}
func handleC(conn net.Conn) {
for {
err := conn.SetReadDeadline(time.Now().Add(5 * time.Second))
if err != nil {
fmt.Println(err)
break
}
l, err := readTL(conn)
if err != nil {
if err, ok := err.(net.Error); ok && err.Timeout() {
fmt.Println("Timeout", err)
break
}
fmt.Println("Other error"), err
break
}
v, err := readV(conn, l)
if err != nil {
if err, ok := err.(net.Error); ok && err.Timeout() {
fmt.Println("Timeout", err)
break
}
fmt.Println("Other error"), err
break
}
// go doStuffWithv()
}
}
func readTL(conn net.Conn) (l int, err error) {
buf := make([]byte, 4)
n, err := io.ReadFull(conn, buf)
if err != nil {
return l, err
}
fmt.Printf("Read %d bytes\n", n)
// getLengthFromTL()
return l, err
}
func readV(conn net.Conn, l int) (v []byte, err error) {
buf := make([]byte, l)
n, err := io.ReadFull(conn, buf)
if err != nil {
return v, err
}
fmt.Printf("Read %d bytes\n", n)
return v, err
}
If a client sends one request with the proper TL, things work as intended.
However, if the same client doesn't send a second message for 10 seconds, the connection will timeout before then, with the error tls: use of closed connection
Is there a way to ensure that this doesn't occur?
One thing I've tried doing is in the event of a timeout, it simply continues, rather than breaking.
I added in another error check to see if it is EOF, and break if it is.
My first impression is that this works, but I'm not sure if there are instances where a connection timeout can mean that the connection is dead and shouldn't be used anymore or not, or if that would always return an EOF error.

Keep-alive: dead peers detection

I run client and socket server written in Go (1.12) on macOS localhost.
Server sets SetKeepAlive and SetKeepAlivePeriod on net.TCPConn.
Client sends a packet and then closes connection (FIN) or client abruptly terminated.
Tcpdump shows that even after client closes the connection, server keeps sending keep-alive probes.
Shouldn't it detect that peer is "dead" and close the connection?
The question is generic, feel free to clarify if I'm missing some basics.
package main
import (
"flag"
"fmt"
"net"
"os"
"time"
)
func main() {
var client bool
flag.BoolVar(&client, "client", false, "")
flag.Parse()
if client {
fmt.Println("Client mode")
conn, err := net.Dial("tcp", "127.0.0.1:12345")
checkErr("Dial", err)
written, err := conn.Write([]byte("howdy"))
checkErr("Write", err)
fmt.Printf("Written: %v\n", written)
fmt.Println("Holding conn")
time.Sleep(60 * time.Second)
err = conn.Close()
checkErr("Close", err)
fmt.Println("Closed conn")
return
}
fmt.Println("Server mode")
l, err := net.Listen("tcp", "127.0.0.1:12345")
checkErr("listen", err)
defer l.Close()
for {
c, err := l.Accept()
checkErr("accept", err)
defer c.Close()
tcpConn := c.(*net.TCPConn)
err = tcpConn.SetKeepAlive(true)
checkErr("SetKeepAlive", err)
err = tcpConn.SetKeepAlivePeriod(5 * time.Second)
checkErr("SetKeepAlivePeriod", err)
b := make([]byte, 1024)
n, err := c.Read(b)
checkErr("read", err)
fmt.Printf("Received: %v\n", string(b[:n]))
}
}
func checkErr(location string, err error) {
if err != nil {
fmt.Printf("%v: %v\n", location, err)
os.Exit(-1)
}
}
The response to that question:
Sending keepalives is only necessary when you need the connection opened but idle. In that cases there is a risk that the connection is broken, so keep alive will try to detect broken connections.
If you had close the connection at server side with a proper con.Close() the keep alive would not be triggered (you did defer it to the end of the main function).
If you test your server code, it will start sending the keep alive after the timeout you set.
You notice that only after all keep alive proves (default 9 from kernel) and the time between the proves (8x), you get an io.EOF error on the server side Read (yes, the server stop sending)!
Currently the GO implementation is the same at Linux and OSX and it set both TCP_KEEPINTVL and TCP_KEEPIDLE to the value you pass to the setKeepAlivePeriod function, so, the behavior will depend of the kernel version.
func setKeepAlivePeriod(fd *netFD, d time.Duration) error {
// The kernel expects seconds so round to next highest second.
d += (time.Second - time.Nanosecond)
secs := int(d.Seconds())
if err := fd.pfd.SetsockoptInt(syscall.IPPROTO_TCP, syscall.TCP_KEEPINTVL, secs); err != nil {
return wrapSyscallError("setsockopt", err)
}
err := fd.pfd.SetsockoptInt(syscall.IPPROTO_TCP, syscall.TCP_KEEPIDLE, secs)
runtime.KeepAlive(fd)
return wrapSyscallError("setsockopt", err)
}
There is a request opened since 2014 to provide a way to set keepalive time and interval separately.
Some references:
rfc1122
net: enable TCP keepalive on new connections from net.Dial
net: enable TCP keepalives by default
TCP keep-alive to determine if client disconnected in netty
Using TCP keepalive with Go

Using net.DialTimeout with specific local port

I'm trying to use specific source IP port, but net.DialTimeout does not have laddr parameter
my_dial, err := net.DialTimeout("tcp", addr, 3*time.Second)
conn := tls.Client(my_dial, tlsconf)
Then I checked the document, the only method that support laddr is:
func DialIP(network string, laddr, raddr *IPAddr) (*IPConn, error)
But it returns net.IPConn instead of net.Conn.
Any ideas?
Dial and DialTimeout are just helpers around the net.Dialer type, which has all the available options documented.
You can set a local address and the timeout. The Dialer also has a DialContext method to use a context directly rather than a timeout if desired.
d := net.Dialer{
LocalAddr: lAddr,
Timeout: timeout,
}
conn, err := d.Dial(network, address)
...

Can TCP clients receive data after a read timeout?

I create a tcp connection pool in golang and set every connection to keep alive, when I get a connection from pool, I will set a 10 seconds timeout by SetDeadline function. Now I want to know, if a timeout error occur after reading from a connection and server send message to me after this point, will the message be put into my connection receive buffer? Will I get the message in next reading? If so, how should I handle the timeout error? Close the connection and create a new one?
If a timeout error occur after reading from a connection and server send message to me after this point, will the message be put into my connection receive buffer?
If you don't close the connection, yes.
Will I get the message in next reading?
Yes, if you reset the deadline. Although this does beg the question why you set a timeout in the first place.
package main
import (
"fmt"
"io"
"log"
"net"
"time"
)
func main() {
l, err := net.Listen("tcp", "127.0.0.1:3001")
check(err)
go slowServer(l)
conn, err := net.Dial("tcp", "127.0.0.1:3001")
check(err)
conn.SetDeadline(time.Now().Add(time.Second))
b := make([]byte, 512)
n, err := conn.Read(b)
fmt.Printf("%q, %v\n", b[:n], err) // "", i/o timeout
// Reset deadline
conn.SetDeadline(time.Now().Add(2 * time.Second))
n, err = conn.Read(b)
fmt.Printf("%q, %v\n", b[:n], err) // "hello world", <nil>
}
func slowServer(l net.Listener) {
conn, err := l.Accept()
check(err)
time.Sleep(2 * time.Second)
io.WriteString(conn, "hello world")
conn.Close()
}
func check(err error) {
if err != nil {
log.Fatal(err)
}
}
// "", read tcp 127.0.0.1:50488->127.0.0.1:3001: i/o timeout
// "hello world", <nil>
Try it on the playground: https://play.golang.org/p/Id60hHK7tKF

ListenUDP, a one way street?

When I run this code an incoming UDP packet gets read in, however no packet gets sent back out. Why is this? (I verified this fact with wireshark). I want to be able to communicate two ways over a UDP connection, how do I achieve this with golang?
//Node 1
func main() {
addr := net.UDPAddr{
Port: 7000,
IP: net.ParseIP("127.0.0.1"),
}
conn, err := net.ListenUDP("udp", &addr)
defer conn.Close()
if err != nil {
panic(err)
}
for {
b := make([]byte, 10)
conn.Read(b)
fmt.Println(string(b[:]))
conn.Write([]byte("sending back"))
}
}
func main() {
sock, _ := net.Dial("udp", "127.0.0.1:7000")
buf := make([]byte, 10)
sock.Write([]byte("first send"))
sock.Read(buf)
fmt.Println(string(buf[:]))
}
Remember, UDP is connection-less. When you call conn.Write, your listener doesn't know where to send the packet. In your server code, you should be using UDPConn.ReadFromUDP and UDPConn.WriteToUDP to obtain and specify the client address, as mentioned in the documentation:
The returned connection's ReadFrom and WriteTo methods can be used to receive and send UDP packets with per-packet addressing.
Your modified Node 1 loop could then look something like the following:
for {
b := make([]byte, 10)
n, clientAddr, _ := conn.ReadFromUDP(b) // TODO: error check
fmt.Println(string(b[:n]))
conn.WriteToUDP([]byte("sending back"), clientAddr)
}

Resources