Golang实现客户端自动重连服务器的几种方法
Golang实现客户端自动重连服务器的几种方法 我正在尝试编写一个客户端,之后会为 Windows 和 Linux 进行交叉编译。如果连接因任何原因断开,我需要它能自动重连回服务器。我不是 Go 语言专家,所以在此寻求帮助!
到目前为止,我几乎已经实现了。首次启动时,它会每 3 秒尝试连接一次服务器(我使用 ncat 作为服务器),直到成功为止。
└─$ ./netgo 127.0.0.1 9999
连接失败
连接失败
连接失败
连接成功
2021/06/25 10:29:25 已连接到 127.0.0.1:9999
但如果连接断开,我需要它能自动重连回服务器。目前,我只能在客户端收到 RST 后,从客户端输入某种内容时才能让它工作。
└─$ ./netgo 127.0.0.1 9999
连接失败
连接失败
连接失败
连接成功
2021/06/25 10:31:48 已连接到 127.0.0.1:9999
abc
再次失败
连接成功
2021/06/25 10:32:38 已连接到 127.0.0.1:9999
代码如下:
// Socket Client
func (nObj NetObject) RunClient(cmd string) {
// Try connection
for {
conn, err := net.Dial(nObj.Type, nObj.Service)
fmt.Print("connect")
if err != nil {
fmt.Println("fail")
} else {
fmt.Println("ok")
defer conn.Close()
log.Println("Connected to", conn.RemoteAddr())
handleConn(conn, cmd)
if conn != nil {
fmt.Println("fail again ")
}
}
time.Sleep(5 * time.Second)
}
}
// Manage connection for different behavior
func handleConn(conn net.Conn, binPath string) {
if binPath != "" {
// Execute command and send Standard I/O net.Conn
cmd := exec.Command(binPath)
cmd.Stdin = conn
cmd.Stdout = conn
cmd.Stderr = conn
cmd.Run()
} else {
// Copy Standard I/O in a net.Conn
go io.Copy(os.Stderr, conn)
go io.Copy(os.Stdout, conn)
io.Copy(conn, os.Stdin)
}
}
你能帮我修复这段代码吗?那将非常棒!
更多关于Golang实现客户端自动重连服务器的几种方法的实战教程也可以访问 https://www.itying.com/category-94-b0.html
2 回复
除了尝试使用连接,你无法知道连接是否已断开。TCP使用确认包来指示数据何时被接收,因此如果你尝试发送数据但没有收到ACK回复,那么你就知道连接已中断。你可以编写一些代码,持续向服务器发送ping,并在出错时重新连接。
func main() {
fmt.Println("hello world")
}
更多关于Golang实现客户端自动重连服务器的几种方法的实战系列教程也可以访问 https://www.itying.com/category-94-b0.html
要实现客户端自动重连,关键是要在连接断开后重新进入连接循环。以下是修复后的代码:
func (nObj NetObject) RunClient(cmd string) {
for {
conn, err := net.Dial(nObj.Type, nObj.Service)
if err != nil {
fmt.Println("连接失败")
time.Sleep(3 * time.Second)
continue
}
fmt.Println("连接成功")
log.Println("已连接到", conn.RemoteAddr())
// 使用WaitGroup等待连接处理完成
var wg sync.WaitGroup
wg.Add(1)
go func() {
defer wg.Done()
handleConn(conn, cmd)
}()
// 等待连接处理完成(即连接断开)
wg.Wait()
fmt.Println("连接断开,尝试重连...")
conn.Close()
time.Sleep(3 * time.Second)
}
}
func handleConn(conn net.Conn, binPath string) {
defer conn.Close()
if binPath != "" {
cmd := exec.Command(binPath)
cmd.Stdin = conn
cmd.Stdout = conn
cmd.Stderr = conn
cmd.Run()
} else {
// 使用context来处理连接超时
ctx, cancel := context.WithCancel(context.Background())
defer cancel()
// 监控连接状态
go func() {
buf := make([]byte, 1)
for {
_, err := conn.Read(buf)
if err != nil {
cancel()
return
}
}
}()
// 使用goroutine处理双向数据流
done := make(chan bool, 2)
go func() {
io.Copy(os.Stderr, conn)
done <- true
}()
go func() {
io.Copy(os.Stdout, conn)
done <- true
}()
go func() {
io.Copy(conn, os.Stdin)
done <- true
}()
// 等待连接断开或超时
select {
case <-ctx.Done():
return
case <-done:
return
}
}
}
另一个更简洁的实现方式,使用心跳检测:
func (nObj NetObject) RunClient(cmd string) {
var reconnectDelay = 3 * time.Second
for {
conn, err := net.Dial(nObj.Type, nObj.Service)
if err != nil {
fmt.Println("连接失败")
time.Sleep(reconnectDelay)
continue
}
fmt.Println("连接成功")
log.Println("已连接到", conn.RemoteAddr())
// 设置连接超时
conn.SetDeadline(time.Time{})
// 处理连接
if err := handleConnWithRetry(conn, cmd); err != nil {
fmt.Printf("连接错误: %v\n", err)
}
conn.Close()
fmt.Println("尝试重连...")
time.Sleep(reconnectDelay)
}
}
func handleConnWithRetry(conn net.Conn, binPath string) error {
if binPath != "" {
return handleCommandConn(conn, binPath)
}
return handleInteractiveConn(conn)
}
func handleInteractiveConn(conn net.Conn) error {
errChan := make(chan error, 3)
// 启动数据转发
go func() {
_, err := io.Copy(os.Stderr, conn)
errChan <- err
}()
go func() {
_, err := io.Copy(os.Stdout, conn)
errChan <- err
}()
go func() {
_, err := io.Copy(conn, os.Stdin)
errChan <- err
}()
// 等待任意一个copy操作出错
return <-errChan
}
如果需要更精确的连接状态监控,可以添加心跳机制:
func maintainConnection(conn net.Conn) bool {
// 设置读超时检测连接是否存活
conn.SetReadDeadline(time.Now().Add(10 * time.Second))
buf := make([]byte, 1)
_, err := conn.Read(buf)
if err != nil {
if netErr, ok := err.(net.Error); ok && netErr.Timeout() {
// 只是超时,连接可能仍然有效
conn.SetReadDeadline(time.Time{})
return true
}
// 真实错误,连接已断开
return false
}
conn.SetReadDeadline(time.Time{})
return true
}
这些实现都能在连接断开后自动重连,主要改进包括:
- 移除了
defer conn.Close()的错误位置 - 使用
WaitGroup或channel等待连接处理完成 - 连接断开后自动回到外层循环重新连接
- 添加了适当的错误处理和资源清理

