Home > OS >  go udp requests (packet) loss
go udp requests (packet) loss

Time:10-28

i wrote simple server and client for tcp and udp connection

package main

//server.go

import (
    "fmt"
    "net"
    "os"
    "os/signal"
    "syscall"
)

func main() {
    tcp := 0
    udp := 0

    defer func(o, t *int) {
        fmt.Println(*o, *t)
    }(&tcp, &udp)

    go func() {
        l, err := net.ListenTCP("tcp", &net.TCPAddr{
            IP:   net.ParseIP("0.0.0.0"),
            Port: 3000,
        })
        if err != nil {
            panic(err)
        }

        b := make([]byte, 24)

        for {
            conn, err := l.Accept()
            if err != nil {
                continue
            }

            n, err := conn.Read(b)
            if err != nil {
                continue
            }

            r := string(b[:n])

            if r == "close" {
                conn.Close()
                break
            } else {
                _, err = conn.Write([]byte("pong"))
                if err != nil {
                    continue
                }
            }

            conn.Close()
            tcp  
        }

        l.Close()
    }()

    go func() {
        conn, err := net.ListenUDP("udp", &net.UDPAddr{
            IP:   net.ParseIP("0.0.0.0"),
            Port: 3000,
        })
        if err != nil {
            panic(err)
        }

        b := make([]byte, 24)

        for {
            n, addr, err := conn.ReadFromUDP(b)
            if err != nil {
                continue
            }

            r := string(b[:n])

            if r == "close" {
                break
            } else {
                _, err = conn.WriteToUDP([]byte("pong"), addr)
                if err != nil {
                    continue
                }
            }

            udp  
        }

        conn.Close()
    }()

    signals := make(chan os.Signal, 1)
    signal.Notify(signals, os.Interrupt, syscall.SIGTERM)

    <-signals
}
package main

//client.go

import (
    "fmt"
    "net"
    "os"
    "os/signal"
    "strconv"
    "sync/atomic"
    "syscall"
    "time"
)

func main() {
    t := "tcp"
    m := "ping"

    c := 1

    if len(os.Args) > 1 {
        t = os.Args[1]
    }

    if len(os.Args) > 2 {
        m = os.Args[2]
    }

    if len(os.Args) > 3 {
        c, _ = strconv.Atoi(os.Args[3])
    }

    tcp := int64(0)
    udp := int64(0)

    defer func(o, t *int64) {
        fmt.Println(*o, *t)
    }(&tcp, &udp)

    if c == 1 {
        if t == "tcp" {
            addr, err := net.ResolveTCPAddr("tcp", ":3000")
            if err != nil {
                panic(err)
            }

            conn, err := net.DialTCP("tcp", nil, addr)
            if err != nil {
                panic(err)
            }

            _, err = conn.Write([]byte(m))
            if err != nil {
                panic(err)
            }

            tcp  
        }

        if t == "udp" {
            addr, err := net.ResolveUDPAddr("udp", ":3000")
            if err != nil {
                panic(err)
            }

            conn, err := net.DialUDP("udp", nil, addr)
            if err != nil {
                panic(err)
            }

            _, err = conn.Write([]byte(m))
            if err != nil {
                panic(err)
            }

            udp  
        }

        os.Exit(0)
    }

    for i := 0; i < c; i   {
        go func() {
            a1, err := net.ResolveTCPAddr("tcp", ":3000")
            if err != nil {
                panic(err)
            }
            c1, err := net.DialTCP("tcp", nil, a1)
            if err != nil {
                panic(err)
            }

            _, err = c1.Write([]byte(m))
            if err != nil {
                panic(err)
            }

            buf := make([]byte, 24)

            n, err := c1.Read(buf)
            if err != nil {
                panic(err)
            }

            if string(buf[:n]) != "pong" {
                panic(1)
            }

            err = c1.Close()
            if err != nil {
                panic(err)
            }

            g := atomic.AddInt64(&tcp, 1)

            if g % 100 == 0 {
                fmt.Println("tcp", g)

                time.Sleep(time.Millisecond * 1000)
            }
        }()

        go func() {
            a2, err := net.ResolveUDPAddr("udp", ":3000")
            if err != nil {
                panic(err)
            }
            c2, err := net.DialUDP("udp", nil, a2)
            if err != nil {
                panic(err)
            }

            _, err = c2.Write([]byte(m))
            if err != nil {
                panic(err)
            }

            buf := make([]byte, 24)

            n, err := c2.Read(buf)
            if err != nil {
                panic(err)
            }

            if string(buf[:n]) != "pong" {
                panic(1)
            }

            err = c2.Close()
            if err != nil {
                panic(err)
            }

            g := atomic.AddInt64(&udp, 1)

            if g % 100 == 0 {
                fmt.Println("udp", g)

                time.Sleep(time.Millisecond * 1000)
            }
        }()
    }


    signals := make(chan os.Signal, 1)
    signal.Notify(signals, os.Interrupt, syscall.SIGTERM)

    <-signals
}

and get a strange behavior: not all udp requests are sent or handled on many connections.

When i sent 100 or 200 both server and client tells me that all requests worked but from 1000 there is a strange ~5% requests loss on udp for both server and client but no panic.
I know udp allows packet loss but 5% on localhost requests seems like an error.

CodePudding user response:

As we know, the UDP is connectionless, so packet loss may be caused by this nature of UDP. There could be several ways to lower the rate of packet loss.

  • Slow down the rate of packets sent on the client side
  • Call SetWriteBuffer to increase the buffer size on the client side and set SetReadBuffer to increase the buffer size on the server side
    conn, err := net.DialUDP("udp", nil, addr)

    err = conn.SetWriteBuffer(64 * 1024 * 1024)
  • Check the system network information through netstat -s -udp and get UDP statistics information. You could try to change the default value of rmem_max and rmem_default on the server side. For more details, please refer to 1 and 2
  • Related