Skip to content

Instantly share code, notes, and snippets.

@marten-seemann
Created June 10, 2020 05:09
Show Gist options
  • Save marten-seemann/42199c0b8462c1a5748c6b1c1aca22c4 to your computer and use it in GitHub Desktop.
Save marten-seemann/42199c0b8462c1a5748c6b1c1aca22c4 to your computer and use it in GitHub Desktop.
UDP Reuseport
package main
import (
"encoding/binary"
"fmt"
"log"
"net"
"strings"
"time"
reuse "github.com/libp2p/go-reuseport"
"golang.org/x/sync/errgroup"
)
const addr = "localhost:12345"
const num = 100
func main() {
done := make(chan struct{})
go func() {
if err := runServer(); err != nil {
log.Fatal(err)
}
close(done)
}()
time.Sleep(200 * time.Millisecond)
// send packets from 9 different remote addresses
for i := 0; i < 9; i++ {
go func(i int) {
if err := runClient(uint64((i + 1) * num)); err != nil {
log.Fatal(err)
}
}(i)
}
<-done
}
func runServer() error {
var g errgroup.Group
// Create 5 listeners to listen on the same UDP port.
for i := 0; i < 5; i++ {
ln, err := reuse.ListenPacket("udp", addr)
if err != nil {
return err
}
g.Go(func() error {
pns, err := acceptPackets(ln)
if err != nil {
return err
}
fmt.Printf("Received packets: %v\n", pns)
return nil
})
}
return g.Wait()
}
// acceptPackets reads all packets arriving at ln.
// After a fixed time, it closes the listener and returns a slice of packet numbers received.
func acceptPackets(ln net.PacketConn) ([]uint64, error) {
pns := make([]uint64, 0, num)
b := make([]byte, 8)
time.AfterFunc(5*time.Second, func() { ln.Close() })
for {
_, _, err := ln.ReadFrom(b)
if err != nil {
if strings.Contains(err.Error(), "use of closed network connection") {
return pns, nil
}
return nil, err
}
pns = append(pns, binary.BigEndian.Uint64(b))
}
}
func runClient(start uint64) error {
raddr, err := net.ResolveUDPAddr("udp", addr)
if err != nil {
return err
}
laddr, err := net.ResolveUDPAddr("udp", "localhost:0")
if err != nil {
return err
}
conn, err := net.ListenUDP("udp", laddr)
if err != nil {
return err
}
b := make([]byte, 8)
for i := start; i < start+num; i++ {
binary.BigEndian.PutUint64(b, uint64(i))
if _, err := conn.WriteTo(b, raddr); err != nil {
return err
}
time.Sleep(100 * time.Microsecond)
}
return nil
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment