英文:
How to measure RTT/latency through TCP clients (created in GoLang) from a TCP server created in GoLang?
问题
所以我正在通过GoLang托管一个TCP服务器,然后我想使用多个TCP客户端连接到我的TCP服务器,并在每次连接新客户端时测量RTT。我还没有找到任何可以在Golang中测量连接到此服务器的RTT的方法(例如,我连接到localhost,但不起作用)。下面是我的TCP服务器代码。
package main
import (
"bufio"
"fmt"
"log"
"math/rand"
"net"
"os"
"strconv"
"strings"
"time"
)
var counter int
const MIN = 1
const MAX = 100
func random() int {
return rand.Intn(MAX-MIN) + MIN
}
func verifyPortNo(portNo string) bool {
conn, err := net.Listen("tcp", portNo)
if err != nil {
log.Println("Connection error: ", err)
log.Println("Cannot verify port")
return false
}
log.Println("Available")
conn.Close()
return true
}
func handleConnection(con net.Conn, counter int) {
fmt.Printf("Client %d: %s\n", counter, con.LocalAddr().String())
defer con.Close()
for {
clientRequest, err := bufio.NewReader(con).ReadString('\n')
if err != nil {
fmt.Println(err)
return
}
stop := strings.TrimSpace(clientRequest)
if stop == "STOP" {
break
}
result := strconv.Itoa(random()) + "\n"
con.Write([]byte(string(result)))
}
}
func main() {
arguments := os.Args //first element of the argument array is the program name
if len(arguments) == 1 {
fmt.Println("Please provide a port number")
return
}
PortNo := "localhost:" + arguments[1]
fmt.Println(PortNo)
if !verifyPortNo(PortNo) {
return
}
n, err := net.Listen("tcp", PortNo)
if err != nil {
fmt.Println(err)
return
}
//close the listener when the application closes
defer n.Close()
rand.Seed(time.Now().Unix())
for {
//while loop for TCP server to accept connections
conn, err := n.Accept()
if err != nil {
fmt.Println(err)
return
}
counter++
go handleConnection(conn, counter)
}
}
以下是我的TCP客户端代码。
package main
import (
"bufio"
"log"
"net"
"os"
"strings"
"time"
)
var counter int
func main() {
for {
go createTCPClient()
time.Sleep(1 * time.Second)
}
// log.Println("Available")
//netstat -anp TCP | grep 9999
}
func createTCPClient() {
PortNo := "localhost:" + os.Args[1]
conn, err := net.Dial("tcp", PortNo)
if err != nil {
log.Println("Connection error: ", err)
log.Println("Cannot verify port")
return
}
defer conn.Close()
serverReader := bufio.NewReader(conn)
for {
reply, err := serverReader.ReadString('\n')
if err != nil {
println("Write to server failed:", err.Error())
os.Exit(1)
}
println("reply from server=", strings.TrimSpace(reply))
}
}
代码是可以工作的(如下图所示),但我无法理解如何测量每个TCP客户端的RTT并显示它。
英文:
so I am hosting a TCP server through GoLang and then I want to connect to my TCP server using multiple TCP clients and measure the RTT every time a new client is connected. I haven't found anything that allows me to measure RTT to connect to this server in Golang (like do I connect to localhost, it doesn't work) Below is my code for the TCP server.
package main
import (
"bufio"
"fmt"
"log"
"math/rand"
"net"
"os"
"strconv"
"strings"
"time"
)
var counter int
const MIN = 1
const MAX = 100
func random() int {
return rand.Intn(MAX-MIN) + MIN
}
func verifyPortNo(portNo string) bool {
conn, err := net.Listen("tcp", portNo)
if err != nil {
log.Println("Connection error: ", err)
log.Println("Cannot verify port")
return false
}
log.Println("Available")
conn.Close()
return true
}
func handleConnection(con net.Conn, counter int) {
fmt.Printf("Client %d: %s\n", counter, con.LocalAddr().String())
defer con.Close()
for {
clientRequest, err := bufio.NewReader(con).ReadString('\n')
if err != nil {
fmt.Println(err)
return
}
stop := strings.TrimSpace(clientRequest)
if stop == "STOP" {
break
}
result := strconv.Itoa(random()) + "\n"
con.Write([]byte(string(result)))
}
}
func main() {
arguments := os.Args //first element of the argument array is the program name
if len(arguments) == 1 {
fmt.Println("Please provide a port number")
return
}
PortNo := "localhost:" + arguments[1]
fmt.Println(PortNo)
if !verifyPortNo(PortNo) {
return
}
n, err := net.Listen("tcp", PortNo)
if err != nil {
fmt.Println(err)
return
}
//close the listener when the application closes
defer n.Close()
rand.Seed(time.Now().Unix())
for {
//while loop for TCP server to accept connections
conn, err := n.Accept()
if err != nil {
fmt.Println(err)
return
}
counter++
go handleConnection(conn, counter)
}
}
Below is my code for the TCP clients.
package main
import (
"bufio"
"log"
"net"
"os"
"strings"
"time"
)
var counter int
func main() {
for {
go createTCPClient()
time.Sleep(1 * time.Second)
}
// log.Println("Available")
//netstat -anp TCP | grep 9999
}
func createTCPClient() {
PortNo := "localhost:" + os.Args[1]
conn, err := net.Dial("tcp", PortNo)
if err != nil {
log.Println("Connection error: ", err)
log.Println("Cannot verify port")
return
}
defer conn.Close()
serverReader := bufio.NewReader(conn)
for {
reply, err := serverReader.ReadString('\n')
if err != nil {
println("Write to server failed:", err.Error())
os.Exit(1)
}
println("reply from server=", strings.TrimSpace(reply))
}
}
The code works (see figure below) but I cannot wrap my head around measuring the RTT for each TCP client and displaying it.
答案1
得分: 5
唯一的便携解决方案是使用/设计一个应用程序协议,让你能够确定往返时间(RTT)。例如,计算请求/响应之间的时间差。
另外,操作系统内核通常记录TCP连接的延迟。然而:
- 没有一种便携的方法来获取TCP的RTT
- TCP的RTT在所有平台上都不可用。
下面是一个简化的示例,演示如何在Linux下读取包含TCP RTT的TCPInfo:
//go:build linux
package main
import (
"fmt"
"net"
"time"
"golang.org/x/sys/unix"
)
func main() {
listener, err := net.Listen("tcp", ":0")
check(err)
fmt.Println("Listening on", listener.Addr())
for {
conn, err := listener.Accept()
check(err)
go func(conn *net.TCPConn) {
defer conn.Close()
info, err := tcpInfo(conn)
check(err)
rtt := time.Duration(info.Rtt) * time.Microsecond
fmt.Println(rtt)
}(conn.(*net.TCPConn))
}
}
func tcpInfo(conn *net.TCPConn) (*unix.TCPInfo, error) {
raw, err := conn.SyscallConn()
if err != nil {
return nil, err
}
var info *unix.TCPInfo
ctrlErr := raw.Control(func(fd uintptr) {
info, err = unix.GetsockoptTCPInfo(int(fd), unix.IPPROTO_TCP, unix.TCP_INFO)
})
switch {
case ctrlErr != nil:
return nil, ctrlErr
case err != nil:
return nil, err
}
return info, nil
}
func check(err error) {
if err != nil {
panic(err)
}
}
本地主机连接的示例输出:
$ ./tcpinfo
Listening on [::]:34761
97μs
69μs
103μs
60μs
92μs
英文:
The only portable solution is using/designing an application protocol that lets you determine the RTT. Eg, time the difference between a request/response.
Alternatively, OS kernels often record the TCP connection latency. However:
- there isn't a portable way to retrieve TCP RTT
- TCP RTT isn't available on all platforms.
This cut-down example demonstrates reading the TCPInfo containing the TCP RTT under Linux:
//go:build linux
package main
import (
"fmt"
"net"
"time"
"golang.org/x/sys/unix"
)
func main() {
listener, err := net.Listen("tcp", ":0")
check(err)
fmt.Println("Listening on", listener.Addr())
for {
conn, err := listener.Accept()
check(err)
go func(conn *net.TCPConn) {
defer conn.Close()
info, err := tcpInfo(conn)
check(err)
rtt := time.Duration(info.Rtt) * time.Microsecond
fmt.Println(rtt)
}(conn.(*net.TCPConn))
}
}
func tcpInfo(conn *net.TCPConn) (*unix.TCPInfo, error) {
raw, err := conn.SyscallConn()
if err != nil {
return nil, err
}
var info *unix.TCPInfo
ctrlErr := raw.Control(func(fd uintptr) {
info, err = unix.GetsockoptTCPInfo(int(fd), unix.IPPROTO_TCP, unix.TCP_INFO)
})
switch {
case ctrlErr != nil:
return nil, ctrlErr
case err != nil:
return nil, err
}
return info, nil
}
func check(err error) {
if err != nil {
panic(err)
}
}
Example output for connections over localhost:
$ ./tcpinfo
Listening on [::]:34761
97µs
69µs
103µs
60µs
92µs
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论