我在macOS本地主机上运行用Go(1.12)编写的客户端和套接字服务器。
服务器在net.TCPConn上设置SetKeepAlive和SetKeepAlivePeriod。
客户端发送数据包,然后关闭连接(FIN
)或客户端突然终止。
Tcpdump显示,即使客户端关闭连接后,服务器仍会继续发送保持活动的探测。
它不应该检测到对等方“死”并关闭连接吗?
这个问题很笼统,如果我缺少一些基础知识,请随时澄清。
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)
}
}
答案 0 :(得分:1)
对该问题的回答:
仅当您需要打开但空闲的连接时才需要发送keepalive。在这种情况下,存在断开连接的风险,因此保持活动状态将尝试检测断开的连接。
如果您使用适当的con.Close()在服务器端关闭了连接,则将不会触发保持活动状态(您已将其推迟到主函数的末尾)。
如果测试服务器代码,它将在设置的超时时间后开始发送保持活动状态。
您注意到,只有在所有保持活动状态的证明(内核中的默认值9)和两次证明之间的时间间隔(8x)之后,服务器端才会出现io.EOF错误。服务器停止发送)!
当前,GO实现在Linux和OSX上是相同的,并且将TCP_KEEPINTVL
和TCP_KEEPIDLE
都设置为传递给setKeepAlivePeriod
函数的值,因此,行为取决于内核版本。
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)
}
自2014年以来,有一个针对provide a way to set keepalive time and interval separately的请求。
一些参考文献: