如何通过 TCP 客户端(在 GoLang 中创建)从 GoLang 中创建的 TCP 服务器测量 RTT/延迟?

发布于 2025-01-19 18:26:16 字数 3020 浏览 2 评论 0原文

所以我通过 GoLang 托管一个 TCP 服务器,然后我想使用多个 TCP 客户端连接到我的 TCP 服务器,并在每次连接新客户端时测量 RTT。我还没有找到任何可以让我在 Golang 中测量 RTT 连接到该服务器的东西(就像我连接到本地主机一样,它不起作用)下面是我的 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.

enter image description here

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

送君千里 2025-01-26 18:26:16

唯一的便携式解决方案是使用/设计一个可让您确定 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)
    }
}

通过 localhost 进行连接的示例输出:

$ ./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
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文