在TCP连接的客户端,我试图尽可能地重用已建立的连接,以避免每次需要连接时拨号的开销。从根本上说,它是连接池,虽然从技术上讲,我的池大小恰好是一个。
我遇到了一个问题,即如果连接闲置的时间足够长,另一端会断开连接。我尝试过使用以下内容来保持连接活着:
err = conn.(*net.TCPConn).SetKeepAlive(true)
if err != nil {
fmt.Println(err)
return
}
err = conn.(*net.TCPConn).SetKeepAlivePeriod(30*time.Second)
if err != nil {
fmt.Println(err)
return
}
但这并没有帮助。事实上,它导致我的关系更快关闭。我很确定这是因为(在Mac上)这意味着30秒后探测连接健康状况,然后以30秒的间隔探测8次。 服务器端不得支持keepalive ,因此在4分30秒后,客户端正在断开连接。
我可以做任何事情来保持空闲连接无限期地存活,如果有一些方法让我至少检测连接已经关闭,那么这绝对是可以的我可以用一个新的无缝替换它。唉,即使在阅读了所有文档并搜索博客圈寻求帮助之后,我也无法找到任何方法来查询TCP连接的状态。
一定有办法。有没有人对如何实现这一点有任何见解?非常感谢任何一个人!
修改
理想情况下,我想学习如何处理这个问题,而不使用第三方库来实现这一目标。当然,如果有一些图书馆这样做,我不介意指向它的方向,所以我可以看到他们是如何做到的。
答案 0 :(得分:3)
套接字api不允许您访问连接状态。您可以从内核(例如Linux上的/proc/net/tcp[6]
)以各种方式查询当前状态,但这并不能保证进一步的发送会成功。
我在这里有点困惑。我的客户端只发送数据。除了执行数据包之外,服务器不会发回任何内容。阅读似乎不是确定连接状态的合适方法,因为需要阅读。
定义套接字API,以便通过读取返回0字节来检测已关闭的连接。这就是它的工作方式。在Go中,这被转换为Read return io.EOF
。这通常是检测断开连接的最快方法。
所以我应该发送并采取行动,发生任何错误?如果是这样,这是一个问题,因为我观察到在尝试发送破坏的管道时通常没有任何错误 - 这似乎是完全错误的
如果仔细观察TCP的工作原理,这就是预期的行为。如果远程端关闭连接,则第一次发送将从服务器触发RST,完全关闭本地连接。您需要从连接中读取以检测关闭,或者如果您尝试再次发送,您将收到错误(假设您已等待足够长的时间以使数据包进行往返),例如"破管#34;在linux上。
澄清......我可以拨打,拔掉以太网线,然后STILL无错误地发送。显然,这些消息无法通过,但我没有收到任何错误
如果连接实际上已断开,或者服务器完全没有响应,那么您就无法将数据包发送到任何地方。 TCP堆栈无法区分真正慢,数据包丢失,拥塞或连接断开的数据包之间的区别。系统需要等待重传超时,并在失败前多次重试该数据包。单独重试的标准配置可能需要13到30分钟才能触发错误。
您可以在代码中执行的操作
如果您不希望连接中有任何数据,那么在Go中检查关闭的连接非常容易;发送goroutine以从连接中读取,直到出现错误。
notify := make(chan error)
go func() {
buf := make([]byte, 1024)
for {
n, err := conn.Read(buf)
if err != nil {
notify <- err
return
}
if n > 0 {
fmt.Println("unexpected data: %s", buf[:n])
}
}
}()
答案 1 :(得分:2)
根据设计,没有“TCP连接状态”这样的东西。只有当你发送东西时会发生什么。在硅片的任何级别都没有TCP API,它会告诉您TCP连接的当前状态。你必须尝试使用它。
如果您要发送keepalive探针,服务器除了做出适当的响应之外别无选择。服务器甚至不知道他们是keepalive。他们不是。它们只是重复的ACK。支持keepalive只是意味着支持发送 Keepalive。