英文:
How do I make it such that a TCP connection will timeout if the connection doesn't receive a response every second?
问题
我正在尝试创建一个TCP服务器,如果客户端在每秒的时间段内没有响应,服务器将超时。
我尝试了以下代码:
func main() {
    listener, err := net.Listen("tcp", "localhost:8000")
    if err != nil {
        log.Fatal(err)
    }
    for {
        conn, err := listener.Accept()
        conn.SetDeadline(time.Now().Add(timeout))
        if err != nil {
            log.Print(err)
        }
        go handleConn(conn)
    }
}
其中timeout是一秒钟,但是服务器立即断开连接,甚至不等待回复。
英文:
I'm trying to create a TCP server that will timeout if the client does not respond within the span of every second.
I tried:
func main() {
	listener, err := net.Listen("tcp", "localhost:8000")
	if err != nil {
		log.Fatal(err)
	}
	for {
		conn, err := listener.Accept()
		conn.SetDeadline(time.Now().Add(timeout))
		if err != nil {
			log.Print(err)
		}
		go handleConn(conn)
	}
}
where the timeout is a single second but the disconnects immediately, not even waiting for a reply.
答案1
得分: 1
你想要的可以通过在监听器上设置套接字选项来实现。根据你的需求调整这些值。
请注意,这是自己的KeepAlive,不依赖于应用程序的传入/传出数据。
以下是在循环之前在监听器上调用此函数的更多选项:
func main() {
    listener, err := net.Listen("tcp", "localhost:8000")
    if err != nil {
        log.Fatal(err)
    }
    err = enableTCPKeepAlive(listener)
	if err != nil {
		log.Fatal(err)
	}
    for {
        conn, err := listener.Accept()
        conn.SetDeadline(time.Now().Add(timeout))
        if err != nil {
            log.Print(err)
        }
        go handleConn(conn)
    }
}
除了我上面提到的选项之外,还有更多可用的选项。
英文:
What you want can be achieved by setting socket options on your listener. Tweak the values as per your needs
> Note that this is its own KeepAlive and does not depend on incoming/outgoing data by application
func enableTCPKeepAlive(listener *net.TCPListener) error {
	rawConn, err := listener.SyscallConn()
	if err != nil {
		return err
	}
	cfg := config.TLSServerConfig()
	rawConn.Control(
		func(fdPtr uintptr) {
			// got socket file descriptor. Setting parameters.
			fd := int(fdPtr)
			//Idle time before sending probe.
			err = syscall.SetsockoptInt(fd, syscall.IPPROTO_TCP, syscall.TCP_KEEPIDLE, cfg.TCPKAIdleTime)
			if err != nil {
				return err
			}
			//Number of probes.
			err := syscall.SetsockoptInt(fd, syscall.IPPROTO_TCP, syscall.TCP_KEEPCNT, cfg.TCPKANumProbes)
			if err != nil {
				return err
			}
			//Wait time after an unsuccessful probe.
			err = syscall.SetsockoptInt(fd, syscall.IPPROTO_TCP, syscall.TCP_KEEPINTVL, cfg.TCPKAInterval)
			if err != nil {
				return err
			}
			// go syscall doesn't have the constant 0x12 (18) for TCP_USER_TIMEOUT.
			// 0x12 value referenced from linux kernel source code header:
			// include/uapi/linux/tcp.h
			err = syscall.SetsockoptInt(fd, syscall.IPPROTO_TCP, 0x12, cfg.TCPKAUserTimeout)
			if err != nil {
				return err
			}
		})
	return nil
}
There are more options available than the ones I have mentioned above.
Call this function on your listener before the for loop.
func main() {
    listener, err := net.Listen("tcp", "localhost:8000")
    if err != nil {
        log.Fatal(err)
    }
    err = enableTCPKeepAlive(listener)
	if err != nil {
		log.Fatal(err)
	}
    for {
        conn, err := listener.Accept()
        conn.SetDeadline(time.Now().Add(timeout))
        if err != nil {
            log.Print(err)
        }
        go handleConn(conn)
    }
}
答案2
得分: 0
问题几乎总是出现在这里未发布的代码中。这个函数显然运行得很好:
package main
import (
	"crypto/rand"
	"log"
	"net"
	"time"
)
func main() {
	listener, err := net.Listen("tcp", "localhost:8000")
	if err != nil {
		log.Fatal(err)
	}
	go func() {
		for {
			conn, err := listener.Accept()
			if err != nil {
				log.Print(err)
				return
			}
			go func(c net.Conn) {
				defer c.Close()
				conn.SetDeadline(time.Now().Add(2 * time.Second))
				if err != nil {
					log.Print(err)
					return
				}
				buf := make([]byte, 1<<19) // 512 KB
				for {
					_, err := conn.Read(buf)
					if err != nil {
						log.Print(err)
						break
					}
				}
			}(conn)
		}
	}()
	payload := make([]byte, 1<<20)
	_, err = rand.Read(payload) // 生成一个随机负载
	if err != nil {
		log.Print(err)
	}
	conn, err := net.Dial("tcp", listener.Addr().String())
	if err != nil {
		log.Fatal(err)
	}
	log.Println("已连接到服务器。")
	time.Sleep(5 * time.Second)
	_, err = conn.Write(payload)
	if err != nil {
		log.Print(err)
	}
	listener.Close()
}
英文:
The problem is almost always in code that is not posted here. The function obviously works like a charme:
package main
import (
"crypto/rand"
"log"
"net"
"time"
)
func main() {
listener, err := net.Listen("tcp", "localhost:8000")
if err != nil {
log.Fatal(err)
}
go func() {
for {
conn, err := listener.Accept()
if err != nil {
log.Print(err)
return
}
go func(c net.Conn) {
defer c.Close()
conn.SetDeadline(time.Now().Add(2 * time.Second))
if err != nil {
log.Print(err)
return
}
buf := make([]byte, 1<<19) // 512 KB
for {
_, err := conn.Read(buf)
if err != nil {
log.Print(err)
break
}
}
}(conn)
}
}()
payload := make([]byte, 1<<20)
_, err = rand.Read(payload) // generate a random payload
if err != nil {
log.Print(err)
}
conn, err := net.Dial("tcp", listener.Addr().String())
if err != nil {
log.Fatal(err)
}
log.Println("Connected to server.")
time.Sleep(5 * time.Second)
_, err = conn.Write(payload)
if err != nil {
log.Print(err)
}
listener.Close()
}
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。


评论