【发布时间】:2019-12-17 03:19:18
【问题描述】:
问题:客户端需要创建tcp连接,如果由于某种原因连接断开,则重新连接,也可以随时要求断开连接。
服务器不在我手中,连接上不发送数据,只需要建立连接。
我使用 TCP keep alive 的实现,我关注了https://thenotexpert.com/golang-tcp-keepalive/
func (s *State) spawnCtrlConnection() (quit chan struct{}) {
quit = make(chan struct{}, 1)
go func(addr string) {
tcpAddr, err := net.ResolveTCPAddr("tcp", s.addr())
if err != nil {
s.HandleError(err)
return
}
conn, err := net.DialTCP("tcp", nil, tcpAddr)
if err != nil {
s.HandleError(err)
return
}
defer func() {
conn.Close()
}()
conn.SetKeepAlive(true)
conn.SetKeepAlivePeriod(time.Second * time.Duration(s.WaitInterval))
rawConn, err := conn.SyscallConn()
if err != nil {
s.HandleError(err)
return
}
rawConn.Control(
func(fdPtr uintptr) {
fd := int(fdPtr)
// Ping amount
err = syscall.SetsockoptInt(fd, syscall.IPPROTO_TCP, syscall.TCP_KEEPCNT, s.PingAmount)
if err != nil {
s.HandleError(err)
return
}
// Retry interval
err = syscall.SetsockoptInt(fd, syscall.IPPROTO_TCP, syscall.TCP_KEEPINTVL, s.RetryInterval)
if err != nil {
s.HandleError(err)
return
}
})
for {
select {
case <-quit:
return
default:
data := make([]byte, 1)
_, err = conn.Read(data)
// it blocks here forever and (<- quit will never receive anything)
// setting explicit timeout doesn't help either, as it will timeout for obvious reason
// conn.SetDeadline(time.Now().Add(time.Second * time.Duration(s.WaitInterval)))
// if err != nil {
// if err, ok := err.(net.Error); ok && err.Timeout() {
// fmt.Println("timeout", err.Error())
// } else {
// fmt.Println("I am here", err.Error())
// }
// }
// WHAT SHOULD I CODE
}
}
}()
return
}
我不明白,我应该编写什么代码来检测断开连接。
可能是我误解了 tcp keep-alive 的概念。
请帮忙。
【问题讨论】:
-
只需使用
forever 循环,它在开始时连接。让Read阻塞和超时,调整它们,当它返回连接丢失错误时,在循环开始时返回。您还可以添加一个标志来检测首次运行,从而确定它是否重新连接。如果服务器发送退出命令,不清楚,只需打破循环。 -
在任何情况下,在读取返回之前您都不能退出。
-
在超时期间读取块,我将无法在该时间间隔内断开连接
-
TCP 中不存在“断开连接”消息。您从连接中读取,直到它关闭。就是这样。
-
使用SetKeepAlive 和SetKeepAlivePeriod 设置保持活动状态。读取连接,直到 read 返回错误。仅此而已。