前段时间用golang在linux下用socket写一个server.发现部分线程无法退出,最后排查是net.Conn.read() 一直阻塞,实际上客户端已经断开了(但是网络异常引起,导致服务器tcp4次挥手异常),直接调用syscall.Shutdown 去关闭socket都没有用,最后修改了下程序设置接收超时来解决
type RichConn struct {
net.Conn
Stoped bool
timeout time.Duration
}
func (conn *RichConn) Read(b []byte) (n int, err error) {
for {
if conn.timeout > 0 {
conn.SetReadDeadline(time.Now().Add(conn.timeout)) // 1秒
}
n, err = conn.Conn.Read(b)
if conn.Stoped {
return 0, io.EOF
}
if nerr, ok := err.(net.Error); ok && nerr.Timeout() {
//检测停止标志 配合超时设置 防止网络异常时socket recv一直阻塞
//fmt.Printf("uupstimeout=%d,%v\n", n, nerr)
continue
}
return n, err
}
}
func (conn *RichConn) Write(b []byte) (n int, err error) {
for {
if conn.timeout > 0 {
conn.SetWriteDeadline(time.Now().Add(conn.timeout)) // 1秒
}
n, err = conn.Conn.Write(b)
if conn.Stoped {
return 0, errors.New("CLOSE")
}
if nerr, ok := err.(net.Error); ok && nerr.Timeout() {
//检测停止标志 配合超时设置 防止网络异常时socket write一直阻塞
//fmt.Printf("uupstimeout=%d,%v\n", n, nerr)
continue
}
return n, err
}
}
func main(){
code start
..........
...........
//用RichConn 代替conn
//RichConn中设置超时为1秒
if tcpConn, ok := conn.(*net.TCPConn); ok {
tcpConn.SetKeepAlive(false)
tcpConn.SetLinger(0)
}
timeoutTCPConn := &RichConn{conn,false,1000*time.Millisecond}
//后面的程序我是两个线程一个是接收一个是发送,发送异常时把RichConn.Stoped设置为true 一秒超时后调用RichConn.Read() 返回的会是io.EOF 自然就结束了
timeoutTCPConn.Stoped=true
}