如何使TCP连接在每秒未收到响应时超时?

huangapple go评论74阅读模式
英文:

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 (
&quot;crypto/rand&quot;
&quot;log&quot;
&quot;net&quot;
&quot;time&quot;
)
func main() {
listener, err := net.Listen(&quot;tcp&quot;, &quot;localhost:8000&quot;)
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&lt;&lt;19) // 512 KB
for {
_, err := conn.Read(buf)
if err != nil {
log.Print(err)
break
}
}
}(conn)
}
}()
payload := make([]byte, 1&lt;&lt;20)
_, err = rand.Read(payload) // generate a random payload
if err != nil {
log.Print(err)
}
conn, err := net.Dial(&quot;tcp&quot;, listener.Addr().String())
if err != nil {
log.Fatal(err)
}
log.Println(&quot;Connected to server.&quot;)
time.Sleep(5 * time.Second)
_, err = conn.Write(payload)
if err != nil {
log.Print(err)
}
listener.Close()
}

huangapple
  • 本文由 发表于 2022年4月23日 18:40:23
  • 转载请务必保留本文链接:https://go.coder-hub.com/71978906.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定