网络通信中,为了防止长时间无响应的情况,经常会用到网络连接超时、读写超时的设置。
本文结合例子简介golang的连接超时和读写超时设置。
1.超时设置
1.1 连接超时
func DialTimeout(network, address string, timeout time.Duration) (Conn, error)
第三个参数timeout
可以用来设置连接超时设置。
如果超过timeout
的指定的时间,连接没有完成,会返回超时错误。
1.2 读写超时
在Conn
定义中,包括读写的超时时间设置。
type Conn interface {
// SetDeadline sets the read and write deadlines associated
// with the connection. It is equivalent to calling both
// SetReadDeadline and SetWriteDeadline.
//
... ...
SetDeadline(t time.Time) error
// SetReadDeadline sets the deadline for future Read calls
// and any currently-blocked Read call.
// A zero value for t means Read will not time out.
SetReadDeadline(t time.Time) error
// SetWriteDeadline sets the deadline for future Write calls
// and any currently-blocked Write call.
// Even if write times out, it may return n > 0, indicating that
// some of the data was successfully written.
// A zero value for t means Write will not time out.
SetWriteDeadline(t time.Time) error
}
通过上面的函数说明,可以得知,这里的参数t
是一个未来的时间点,所以每次读或写之前,都要调用SetXXX
重新设置超时时间,
如果只设置一次,就会出现总是超时的问题。
2.例子
2.1 server
server端监听连接,如果收到连接请求,就是创建一个goroutine负责这个连接的数据收发。
为了测试超时,我们在写操作之前,sleep 3s。
package main
import (
"net"
"log"
"time"
)
func main() {
addr := "0.0.0.0:8080"
tcpAddr, err := net.ResolveTCPAddr("tcp",addr)
if err != nil {
log.Fatalf("net.ResovleTCPAddr fail:%s", addr)
}
listener, err := net.ListenTCP("tcp", tcpAddr)
if err != nil {
log.Fatalf("listen %s fail: %s", addr, err)
} else {
log.Println("listening", addr)
}
for {
conn, err := listener.Accept()
if err != nil {
log.Println("listener.Accept error:", err)
continue
}
go handleConnection(conn)
}
}
func handleConnection(conn net.Conn) {
defer conn.Close()
var buffer []byte = []byte("You are welcome. I'm server.")
for {
time.Sleep(3*time.Second)// sleep 3s
n, err := conn.Write(buffer)
if err != nil {
log.Println("Write error:", err)
break
}
log.Println("send:", n)
}
log.Println("connetion end")
}
2.2 client
client建立连接时,使用的超时时间是3s。
创建连接成功后,设置连接的读超时。
每次读之前,都重新设置超时时间。
package main
import (
"log"
"net"
"os"
"time"
)
func main() {
connTimeout := 3*time.Second
conn, err := net.DialTimeout("tcp", "127.0.0.1:8080", connTimeout) // 3s timeout
if err != nil {
log.Println("dial failed:", err)
os.Exit(1)
}
defer conn.Close()
readTimeout := 2*time.Second
buffer := make([]byte, 512)
for {
err = conn.SetReadDeadline(time.Now().Add(readTimeout)) // timeout
if err != nil {
log.Println("setReadDeadline failed:", err)
}
n, err := conn.Read(buffer)
if err != nil {
log.Println("Read failed:", err)
//break
}
log.Println("count:", n, "msg:", string(buffer))
}
}
输出结果
2019/05/12 16:18:19 Read failed: read tcp 127.0.0.1:51718->127.0.0.1:8080: i/o timeout
2019/05/12 16:18:19 count: 0 msg:
2019/05/12 16:18:20 count: 28 msg: You are welcome. I'm server.
2019/05/12 16:18:22 Read failed: read tcp 127.0.0.1:51718->127.0.0.1:8080: i/o timeout
2019/05/12 16:18:22 count: 0 msg: You are welcome. I'm server.
2019/05/12 16:18:23 count: 28 msg: You are welcome. I'm server.
2019/05/12 16:18:25 Read failed: read tcp 127.0.0.1:51718->127.0.0.1:8080: i/o timeout
2019/05/12 16:18:25 count: 0 msg: You are welcome. I'm server.
2019/05/12 16:18:26 count: 28 msg: You are welcome. I'm server.
Just try, don't shy.