lossy

package module
Version: v0.2.1 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Aug 25, 2019 License: MIT Imports: 4 Imported by: 1

README

lossy

GoDoc GitHub release (latest SemVer) GitHub Go Report Card

Go package to simulate bandwidth, latency and packet loss for net.PacketConn and net.Conn interfaces.

Its main usage is to test robustness of applications and network protocols run over unreliable transport protocols such as UDP or IP. As a side benefit, it can also be used as outbound bandwidth limiter.

lossy only alters the writing side of the connection, reading side is kept as it is.

Example

package main

import (
	"fmt"
	"github.com/cevatbarisyilmaz/lossy"
	"log"
	"math/rand"
	"net"
	"time"
)

const packetSize = 64

func main() {
	// Create two connection endpoints over UDP
	packetConn, conn := createConnections()

	// Create a lossy packet connection
	bandwidth := 1048 // 8 Kbit/s
	minLatency := 100 * time.Millisecond
	maxLatency := time.Second
	packetLossRate := 0.33
	headerOverhead := lossy.UDPv4MinHeaderOverhead
	lossyPacketConn := lossy.NewPacketConn(packetConn, bandwidth, minLatency, maxLatency, packetLossRate, headerOverhead)

	// Write some packets via lossy
	var bytesWritten int
	const packetCount = 32
	go func() {
		for i := 0; i < packetCount; i++ {
			packet := createPacket()
			_, err := lossyPacketConn.WriteTo(packet, conn.LocalAddr())
			if err != nil {
				log.Fatal(err)
			}
			bytesWritten += len(packet) + headerOverhead
		}
		fmt.Println("Sent", packetCount, "packets with total size of", bytesWritten, "bytes")
		baseTransmissionDuration := time.Duration(float64(bytesWritten * int(time.Second)) / float64(bandwidth))
		earliestCompletion := baseTransmissionDuration + minLatency
		latestCompletion := baseTransmissionDuration + maxLatency
		fmt.Println("Expected transmission duration is between", earliestCompletion, "and", latestCompletion)
	}()

	// Read packets at the other side
	const timeout = 3 * time.Second
	var packets, bytesRead int
	startTime := time.Now()
	for {
		_ = conn.SetReadDeadline(time.Now().Add(timeout))
		buffer := make([]byte, packetSize)
		n, err := conn.Read(buffer)
		if err != nil {
			break
		}
		bytesRead += n + headerOverhead
		packets++
	}
	dur := time.Now().Sub(startTime) - timeout
	fmt.Println("Received", packets, "packets with total size of", bytesRead, "bytes in", dur)

	// Close the connections
	_ = lossyPacketConn.Close()
	_ = conn.Close()
}

func createConnections() (net.PacketConn, net.Conn) {
	packetConn, err := net.ListenUDP("udp", &net.UDPAddr{
		IP:   net.IPv4(127, 0, 0, 1),
		Port: 0,
	})
	if err != nil {
		log.Fatal(err)
	}
	conn, err := net.DialUDP("udp", nil, packetConn.LocalAddr().(*net.UDPAddr))
	if err != nil {
		log.Fatal(err)
	}
	return packetConn, conn
}

func createPacket() []byte {
	packet := make([]byte, packetSize)
	rand.Read(packet)
	return packet
}

Output

Sent 32 packets with total size of 2944 bytes
Expected transmission duration is between 2.909160305s and 3.809160305s
Received 23 packets with total size of 2116 bytes in 3.2507523s

Documentation

Overview

Package lossy simulates bandwidth, latency and packet loss for net.PacketConn and net.Conn interfaces.

Its main usage is to test robustness of applications and network protocols run over unreliable transport protocols such as UDP or IP. As a side benefit, it can also be used as outbound bandwidth limiter.

lossy only alters the writing side of the connection, reading side is kept as it is.

Example
package main

import (
	"fmt"
	"github.com/cevatbarisyilmaz/lossy"
	"log"
	"math/rand"
	"net"
	"time"
)

func main() {
	packetConn, err := net.ListenUDP("udp", &net.UDPAddr{
		IP:   net.IPv4(127, 0, 0, 1),
		Port: 0,
	})
	if err != nil {
		log.Fatal(err)
	}
	conn, err := net.DialUDP("udp", nil, packetConn.LocalAddr().(*net.UDPAddr))
	if err != nil {
		log.Fatal(err)
	}
	lossyConn := lossy.NewConn(conn, 1024, time.Second, time.Second*2, 0.25, lossy.UDPv4MinHeaderOverhead)
	var bytesWritten int
	const packetCount = 32
	go func() {
		for i := 0; i < packetCount; i++ {
			packet := make([]byte, i*64)
			bytesWritten += len(packet) // Ignoring the packet headers
			rand.Read(packet)
			_, err := lossyConn.Write(packet)
			if err != nil {
				log.Fatal(err)
			}
		}
		fmt.Println("Sent", packetCount, "packets with total size of", bytesWritten, "bytes")
	}()
	const timeOut = time.Second * 5
	var bytesRead int
	var packetsReceived int
	startTime := time.Now()
	for {
		err = packetConn.SetReadDeadline(time.Now().Add(timeOut))
		if err != nil {
			log.Fatal(err)
		}
		buffer := make([]byte, 32*64)
		n, addr, err := packetConn.ReadFrom(buffer)
		if err != nil {
			if nerr, ok := err.(net.Error); ok {
				if nerr.Timeout() {
					break
				}
			}
			log.Fatal(err)
		}
		if addr.String() != conn.LocalAddr().String() {
			log.Fatal("hijacked by", addr.String())
		}
		packetsReceived++
		bytesRead += n // Ignoring the packet headers
	}
	dur := time.Now().Sub(startTime) - timeOut
	fmt.Println("Received", packetsReceived, "packets with total size of", bytesRead, "bytes in", dur.Seconds(), "seconds")
}
Output:

Index

Examples

Constants

View Source
const IPv4MaxHeaderOverhead = 60

IPv4MaxHeaderOverhead is the maximum header overhead for IPv4 based connections.

View Source
const IPv4MinHeaderOverhead = 20

IPv4MinHeaderOverhead is the minimum header overhead for IPv4 based connections.

View Source
const IPv6HeaderOverhead = 40

IPv6HeaderOverhead is the header overhead for IPv6 based connections.

View Source
const UDPv4MaxHeaderOverhead = 68

UDPv4MaxHeaderOverhead is the maximum header overhead for UDP based connections over IPv4.

View Source
const UDPv4MinHeaderOverhead = 28

UDPv4MinHeaderOverhead is the minimum header overhead for UDP based connections over IPv4.

View Source
const UDPv6HeaderOverhead = 48

UDPv6HeaderOverhead is the header overhead for UDP based connections over IPv6.

Variables

This section is empty.

Functions

func NewConn added in v0.2.0

func NewConn(c net.Conn, bandwidth int, minLatency, maxLatency time.Duration, packetLossRate float64, headerOverhead int) net.Conn

NewConn wraps the given net.Conn with a lossy connection.

bandwidth is in bytes/second. i.e. enter 1024 * 1024 for a 8 Mbit/s connection. Enter 0 or a negative value for an unlimited bandwidth.

minLatency and maxLatency is used to create a random latency for each packet. maxLatency should be equal or greater than minLatency. If bandwidth is not unlimited and there's no other packets waiting to be delivered, time to deliver a packet is (len(packet) + headerOverhead) / bandwidth + randomDuration(minLatency, maxLatency)

packetLossRate is chance of a packet to be dropped. It should be less than 1 and equal or greater than 0.

headerOverhead is the header size of the underlying protocol of the connection. It is used to simulate bandwidth more realistically. If bandwidth is unlimited, headerOverhead is ignored.

func NewPacketConn added in v0.2.0

func NewPacketConn(c net.PacketConn, bandwidth int, minLatency, maxLatency time.Duration, packetLossRate float64, headerOverhead int) net.PacketConn

NewPacketConn wraps the given net.PacketConn with a lossy packet connection.

bandwidth is in bytes/second. i.e. enter 1024 * 1024 for a 8 Mbit/s connection. Enter 0 or a negative value for an unlimited bandwidth.

minLatency and maxLatency is used to create a random latency for each packet. maxLatency should be equal or greater than minLatency. If bandwidth is not unlimited and there's no other packets waiting to be delivered, time to deliver a packet is (len(packet) + headerOverhead) / bandwidth + randomDuration(minLatency, maxLatency)

packetLossRate is chance of a packet to be dropped. It should be less than 1 and equal or greater than 0.

headerOverhead is the header size of the underlying protocol of the connection. It is used to simulate bandwidth more realistically. If bandwidth is unlimited, headerOverhead is ignored.

Types

This section is empty.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL