golang tcp server client异常掉线判断

当TCP Client异常结束时,大部分TCP server都无法正确判断和处理这个问题。
常见的解决思路:

  1. NoDelay: 解决不了,只解决了发送的问题
  2. KeepAlive:解决不了,虽然这个机制会最终导致socket error然后退出,但是时间太长,没有实际意义
  3. epoll error: 这个机制不是所有的语言都支持
  4. Timeout: 这个机制对golang内置,其他语言不一定有原生实现
  5. 自定义心跳:可以,需要双端支持,不建议

这里给出一个golang 解决这个问题的代码:

server: (核心在connLoop函数)

package main

import (
	"fmt"
	"net"
	"time"
)

// var tcpServe *net.TCPListener

func main() {
    
    
	fmt.Println("Hello world")

	addr, er := net.ResolveTCPAddr("tcp", "0.0.0.0:19000")
	if er != nil {
    
    
		fmt.Println("parse addr error on ", addr.String())
		return
	}
	tcpServe, er := net.ListenTCP("tcp", addr)
	if er != nil {
    
    
		fmt.Println("Listen error on ", tcpServe.Addr().String())
		return
	}
	defer tcpServe.Close()

	fmt.Println("start listen for client...", tcpServe.Addr().String())
	for {
    
    
		conn, er := tcpServe.AcceptTCP()
		if er != nil {
    
    
			fmt.Println("tcp server accept error ", er)
			break
		}
		fmt.Println("Conn come in: ", conn.RemoteAddr().String())

		conn.SetNoDelay(true)
		conn.SetKeepAlive(true)

		go connLoop(conn)
	}
}

func connLoop(c *net.TCPConn) {
    
    
	defer c.Close()

	//fmt.Println("Conn come in: ", c.RemoteAddr().String())
	var buff = make([]byte, 1024)
	for {
    
    
		c.SetReadDeadline(time.Now().Add(time.Second * 10))
		n, er := c.Read(buff)
		if er != nil {
    
    
			switch t_er := er.(type) {
    
    
			case net.Error:
				if t_er.Timeout() {
    
       // 只有这个可以判断 read timeout 错误.
					fmt.Println("read timeout, retry...")
					continue
				}
				// 对于 io.EOF 之类,正常处理
				break
			default:
				break
			}
			fmt.Println("Conn error: ", er)
			break
		}
		fmt.Println("Read inf: ", buff[:n])
	}
	fmt.Println("Conn leave... ", c.RemoteAddr().String())
}

Client:

package main

import (
	"bufio"
	"fmt"
	"net"
	"os"
)

// var client *net.TCPConn

func main() {
    
    
	fmt.Println("Hello world client")
	client, er := net.Dial("tcp", "127.0.0.1:19000")
	if er != nil {
    
    
		fmt.Println("Dial error: ", er)
		return
	}
	defer client.Close()

	fmt.Println("Local: ", client.LocalAddr().String(), "Remote: ", client.RemoteAddr().String())

	reader := bufio.NewReader(os.Stdin)
	for {
    
    
		dt, er := reader.ReadString('\n')
		if er != nil {
    
    
			fmt.Println("read error: ", er)
			break
		}
		client.Write([]byte(dt))
	}
	fmt.Println("client disconnect...")

}

猜你喜欢

转载自blog.csdn.net/bbdxf/article/details/126970441