How to measure RTT/latency through TCP clients (created in GoLang) from a TCP server created in GoLang?

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

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并显示它。

How to measure RTT/latency through TCP clients (created in GoLang) from a TCP server created in GoLang?

英文:

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.

enter image description here

答案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

huangapple
  • 本文由 发表于 2022年4月8日 02:54:03
  • 转载请务必保留本文链接:https://go.coder-hub.com/71787548.html
匿名

发表评论

匿名网友

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

确定