irtt

package module
v0.9.0 Latest Latest
Warning

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

Go to latest
Published: Feb 10, 2018 License: GPL-3.0 Imports: 30 Imported by: 0

README

IRTT (Isochronous Round-Trip Tester)

IRTT measures round-trip time, one-way delay and other metrics using UDP packets sent on a fixed period, and produces both user and machine parseable output.

IRTT has reached version 0.9.0, and is usable today, but needs more work until version 1.0.0 can be released. I would appreciate any feedback, which you can send under Issues. However, it could be useful to first review the Roadmap section of the documentation before submitting a new bug or feature request.

Table of Contents

  1. Motivation
  2. Goals
  3. Features
  4. Limitations
  5. Installation
  6. Documentation
  7. Frequently Asked Questions
  8. Roadmap
  9. Changes
  10. Thanks

Motivation

Latency is an under-appreciated metric in network and application performance. As of this writing, many broadband connections are well past the point of diminishing returns when it comes to throughput, yet that’s what we continue to take as the primary measure of Internet performance. This is analogous to ordinary car buyers making top speed their first priority.

There is a certain hard to quantify but visceral “latency stress” that comes from waiting in expectation after a web page click, straining through a delayed and garbled VoIP conversation, or losing at your favorite online game (unless you like “lag” as an excuse). Those who work on reducing latency and improving network performance characteristics beyond just throughput may be driven by the idea of helping relieve this stress for others.

IRTT was originally written to improve the latency and packet loss measurements for the excellent Flent tool, but should be useful as a standalone tool as well. Flent was developed by and for the Bufferbloat project, which aims to reduce "chaotic and laggy network performance," making this project valuable to anyone who values their time and sanity while using the Internet.

Goals

The goals of this project are to:

  • Accurately measure latency and other relevant metrics of network behavior
  • Produce statistics via both human and machine parseable output
  • Provide for reasonably secure use on both public and private servers
  • Support small enough packet sizes for VoIP simulation
  • Support relevant socket options, including DSCP
  • Use a single UDP port for deployment simplicity
  • Provide an API for embedding and extensibility

Features:

  • Measurement of:
  • Statistics: min, max, mean, median (for most quantities) and standard deviation
  • Nanosecond time precision (where available), and robustness in the face of clock drift and NTP corrections through the use of both the wall and monotonic clocks
  • Binary protocol with negotiated format for test packet lengths down to 16 bytes (without timestamps)
  • HMAC support for private servers, preventing unauthorized discovery and use
  • Support for a wide range of Go supported platforms
  • Timer compensation to improve sleep send schedule accuracy
  • Support for IPv4 and IPv6
  • Public server protections, including:
    • Three-way handshake with returned 64-bit connection token, preventing reply redirection to spoofed source addresses
    • Limits on maximum test duration, minimum interval and maximum packet length, both advertised in the negotiation and enforced with hard limits to protect against rogue clients
    • Packet payload filling to prevent relaying of arbitrary traffic
  • Output to JSON

Limitations

See the LIMITATIONS section of the irtt(1) man page.

Installation

To install IRTT manually or build from source, you must:

  1. Install Go
  2. Install irtt: go get -u github.com/peteheist/irtt/cmd/irtt
  3. For convenience, copy the irtt executable, which should be in $HOME/go/bin, or $GOPATH/bin if you have $GOPATH defined, to somewhere on your PATH.

If you want to build the source for development, you must also:

  1. Install the pandoc utility for generating man pages and HTML documentation from their markdown source files. This can be done with apt-get install pandoc on Debian flavors of Linux or brew install pandoc on OS/X. See the Pandoc site for more information.
  2. Install the stringer utility by doing go get -u golang.org/x/tools/cmd/stringer. This is only necessary if you need to re-generate the *_string.go files that are generated by this tool, otherwise the checked in versions may also be used.
  3. Use build.sh to build during development, which helps with development related tasks, such as generating source files and docs, and cross-compiling for testing. For example, build.sh min linux-amd64 would compile a minimized binary for Linux on AMD64. See build.sh for more info and a "source-documented" list of platforms that the script supports. See this page for a full list of valid GOOS GOARCH combinations. build.sh install runs Go's install command, which puts the resulting executable in $GOPATH/bin.

If you want to build from a branch, you should first follow the steps above, then from the github.com/peteheist/irtt directory, do:

  1. git checkout branch
  2. go get ./...
  3. go install ./cmd/irtt or ./build.sh and move resulting irtt executable to install location

Documentation

After installing IRTT, see the man pages and their corresponding EXAMPLES sections to get started quickly:

Frequently Asked Questions

  1. Why not just use ping?

    Ping may be the preferred tool when measuring minimum latency, or for other reasons. IRTT's reported mean RTT is likely to be a bit higher (on the order of hundreds of microseconds) and a bit more variable than the results reported by ping, due to the overhead of entering userspace, together with Go's system call overhead and scheduling variability. That said, this overhead should be negligible at most Internet RTTs, and there are advantages that IRTT has over ping when minimum RTT is not what you're measuring:

    • In addition to round-trip time, IRTT also measures OWD, IPDV and upstream vs downstream packet loss.
    • Some device vendors prioritize ICMP, so ping may not be an accurate measure of user-perceived latency.
    • IRTT can use HMACs to protect private servers from unauthorized discovery and use.
    • IRTT has a three-way handshake to prevent test traffic redirection from spoofed source IPs.
    • IRTT can fill the payload (if included) with random or arbitrary data.
  2. Why does irtt client use -l for packet length instead of following ping and using -s for size?

    I felt it more appropriate to follow the RFC 768 term length for UDP packets, since IRTT uses UDP.

  3. Why is the send (or receive) delay negative or much larger than I expect?

    The client and server clocks must be synchronized for one-way delay values to be meaningful (although, the relative change of send and receive delay may be useful to look at even without clock synchronization). Well-configured NTP hosts may be able to synchronize to within a few milliseconds. PTP (Linux implementation here) is capable of much higher precision. For example, using two PCEngines APU2 boards (which support PTP hardware timestamps) connected directly by Ethernet, the clocks may be synchronized within a few microseconds.

    Note that client and server synchronization is not needed for either RTT or IPDV (even send and receive IPDV) values to be correct. RTT is measured with client times only, and since IPDV is measuring differences between successive packets, it's not affected by time synchronization.

  4. Why is the receive rate 0 when a single packet is sent?

    Receive rate is measured from the time the first packet is received to the time the last packet is received. For a single packet, those times are the same.

  5. Why does a test with a one second duration and 200ms interval run for around 800ms and not one second?

    The test duration is exclusive, meaning requests will not be sent exactly at or after the test duration has elapsed. In this case, the interval is 200ms, and the fifth and final request is sent at around 800ms from the start of the test. The test ends when all replies have been received from the server, so it may end shortly after 800ms. If there are any outstanding packets, the wait time is observed, which by default is a multiple of the maximum RTT.

  6. Why is IPDV not reported when only one packet is received?

    IPDV is the difference in delay between successfully returned replies, so at least two reply packets are required to make this calculation.

  7. Why does wait fall back to fixed duration when duration is less than RTT?

    If a full RTT has not elapsed, there is no way to know how long an appropriate wait time would be, so the wait falls back to a default fixed time (default is 4 seconds, same as ping).

  8. Why can't the client connect to the server, and instead I get Error: no reply from server?

    There are a number of possible reasons for this:

    1. You've specified an incorrect hostname or IP address for the server.
    2. There is a firewall blocking packets from the client to the server. Traffic must be allowed on the chosen UDP port (default 2112).
    3. There is high packet loss. By default, up to four packets are sent when the client tries to connect to the server, using timeouts of 1, 2, 4 and 8 seconds. If all of these are lost, the client won't connect to the server. In environments with known high packet loss, the --timeouts flag may be used to send more packets with the chosen timeouts before abandoning the connection.
    4. The server has an HMAC key set with --hmac and the client either has not specified a key or it's incorrect. Make sure the client has the correct HMAC key, also specified with the --hmac flag.
    5. You're trying to connect to a listener that's listening on an unspecified IP address, and return packets are not routing properly, which can happen in some network configurations. Try running the server with the --set-src-ip flag, which sets the source address on all reply packets from listeners on unspecified IP addresses. This is not done by default in order to avoid the additional per-packet heap allocations required by the golang.org/x/net packages.
  9. Why can't the client connect to the server, and I either see [Drop] [UnknownParam] unknown negotiation param (0x8 = 0) on the server, or a strange message on the client like [InvalidServerRestriction] server tried to reduce interval to < 1s, from 1s to 92ns?

    You're using a 0.1 development version of the server with a newer client. Make sure both client and server are up to date. Going forward, the protocol is versioned (independently from IRTT in general), and is checked when the client connects to the server. For now, the protocol versions must match exactly.

  10. Why don't you include median values for send call time, timer error and server processing time?

    Those values aren't stored for each round trip, and it's difficult to do a running calculation of the median, although this method of using skip lists appears to have promise. It's a possibility for the future, but so far it isn't a high priority. If it is for you, file an Issue.

  11. I see you use MD5 for the HMAC. Isn't that insecure?

    MD5 should not have practical vulnerabilities when used in a message authenticate code. See this page for more info.

  12. Will you add unit tests?

    Maybe some. I feel that the most important thing for a project of this size is that the design is clear enough that bugs are next to impossible. IRTT is not there yet though, particularly when it comes to packet manipulation.

  13. Are there any plans for translation to other languages?

    While some parts of the API were designed to keep i18n possible, there is no support for i18n built in to the Go standard libraries. It should be possible, but could be a challenge, and is not something I'm likely to undertake myself.

  14. Why do I get Error: failed to allocate results buffer for X round trips (runtime error: makeslice: cap out of range)?

    Your test interval and duration probably require a results buffer that's larger than Go can allocate on your platform. Lower either your test interval or duration. See the following additional documentation for reference: In-memory results storage, maxSliceCap in slice.go and _MaxMem in malloc.go.

  15. Why is little endian byte order used in the packet format?

    As for Google's protobufs, this was chosen because the vast majority of modern processors use little-endian byte order. In the future, packet manipulation may be optimized for little-endian architecutures by doing conversions with Go's unsafe package, but so far this optimization has not been shown to be necessary.

  16. Why is the virt size (vsz) memory usage so high in Linux?

    This has to do with the way Go allocates memory. See this article for more information. File an Issue if your resident usage (rss/res) is high or you feel that memory consumption is somehow a problem.

Changes

See CHANGES.md.

Roadmap

v1.0.0

Planned for v1.0.0...

  • Refactor packet manipulation to improve readability, prevent multiple validations and support unit tests
  • Improve open/close process:
    • Make timeout support automatic exponential backoff, like 4x15s
    • Repeat close packets until acknowledgement, like open
    • Include final stats in the close acknowledgement from the server
  • Improve robustness and security of public servers:
    • Add bitrate limiting
    • Limit open requests rate and coordinate with sconn cleanup
    • Add separate, shorter timeout for open
    • Specify close timeout as param from client, which may be restricted
    • Make connref mechanism robust to listener failure
      • Add per-IP limiting
  • Write a SmokePing probe
Inbox

Collection area for the future...

  • Improve induced latency and jitter:
    • Use Go profiling, scheduler tracing, strace and sar
    • Do more thorough tests of chrt -r 99, --thread and --gc
    • Find or file issue with Go team over scheduler performance, if needed
    • Prototype doing thread scheduling or socket i/o for Linux in C
  • Add different server authentication modes:
    • none (no conn token in header, for minimum packet sizes during local use)
    • token (what we have today, 64-bit token in header)
    • nacl-hmac (hmac key negotiated with public/private key encryption)
  • Implement graceful server shutdown with sconn close
  • Implement zero-downtime restarts
  • Add a Scheduler interface to allow non-isochronous send schedules and variable packet lengths
    • Find some way to determine packet interval and length distributions for captured traffic
    • Determine if asymmetric send schedules (between client and server) required
  • Add an overhead test mode to compare ping vs irtt
  • Add client flag to skip sleep and catch up after timer misses
  • Always return instance of irtt.Error? If so, look at exitOnError.
  • Find better model for concurrency (one goroutine per sconn induces latency)
  • Use error code (if available) as exit code
  • Add seqno to the Max and maybe Min columns in the text output
  • Prototype TCP throughput test and compare straight Go vs iperf/netperf
  • Add a subcommand to the CLI to convert JSON to CSV
  • Support a range of server ports to improve concurrency and maybe defeat latency "slotting" on multi-queue interfaces
  • Prompt to write JSON file on cancellation
  • Add unit tests
  • Add support for load balanced conns (multiple source addresses for same conn)
  • Use unsafe package to speed up packet buffer manipulation
  • Add encryption
  • Add estimate for HMAC calculation time and correct send timestamp by this time
  • Implement web interface for client and server
  • Set DSCP per-packet, at least for IPv6
  • Add NAT hole punching
  • Add a flag to disable per-packet results
  • Use a larger, internal received window on the server to increase up/down loss accuracy
  • Implement median calculation for timer error, send call time and server processing time
  • Allow specifying two out of three of interval, bitrate and packet size
  • Calculate per-packet arrival order during results generation using timestamps
  • Add OWD compensation at results generation stage for shifting mean value to 0 to improve readability for clocks that are badly out of sync
  • Add a way to keep out "internal" info from JSON, like IP and hostname, and a subcommand to strip these out after the JSON is created
  • Make it possible to add custom per-round-trip statistics programmatically
  • Add more info on outliers and possibly a textual histogram
  • Allow Client Dial to try multiple IPs when a hostname is given
  • Allow Server listen to listen on multiple IPs for a hostname
  • What do I do for IPDV when there are out of order packets?
  • Does exposing both monotonic and wall clock values open the server to any timing attacks?
  • Should I request a reserved IANA port?

Thanks

Many thanks to both Toke Høiland-Jørgensen and Dave Täht from the Bufferbloat project for their valuable advice. Any problems in design or implementation are entirely my own.

Documentation

Index

Constants

View Source
const (
	DefaultIPVersion  = DualStack
	DefaultPort       = "2112"
	DefaultPortInt    = 2112
	DefaultTTL        = 0
	DefaultThreadLock = false
)

Common defaults.

View Source
const (
	DefaultDuration                = 1 * time.Minute
	DefaultInterval                = 1 * time.Second
	DefaultLength                  = 0
	DefaultReceivedStats           = ReceivedStatsBoth
	DefaultStampAt                 = AtBoth
	DefaultClock                   = BothClocks
	DefaultDSCP                    = 0
	DefaultLoose                   = false
	DefaultLocalAddress            = ":0"
	DefaultLocalPort               = "0"
	DefaultDF                      = DFDefault
	DefaultCompTimerMinErrorFactor = 0.0
	DefaultCompTimerMaxErrorFactor = 2.0
	DefaultHybridTimerSleepFactor  = 0.95
	DefaultAverageWindow           = 5
	DefaultExponentialAverageAlpha = 0.1
)

Client defaults.

View Source
const (
	DefaultMaxDuration   = time.Duration(0)
	DefaultMinInterval   = 10 * time.Millisecond
	DefaultMaxLength     = 0
	DefaultServerTimeout = 1 * time.Minute
	DefaultPacketBurst   = 5
	DefaultAllowStamp    = DualStamps
	DefaultAllowDSCP     = true
	DefaultSetSrcIP      = false
	DefaultGCMode        = GCOn
)

Server defaults.

View Source
const InvalidDuration = time.Duration(math.MaxInt64)

InvalidDuration indicates a duration that is not valid.

View Source
const InvalidSeqno = Seqno(math.MaxUint32)

InvalidSeqno indicates a sequence number that is not valid.

Variables

View Source
var AveragerFactories = make([]AveragerFactory, 0)

AveragerFactories are the registered Averager factories.

View Source
var DefaultAllowFills = []string{"rand"}

DefaultAllowFills are the default allowed fill prefixes.

View Source
var DefaultBindAddrs = []string{":2112"}

DefaultBindAddrs are the default bind addresses.

View Source
var DefaultCompTimerAverage = NewDefaultExponentialAverager()

DefaultCompTimerAverage is the default timer error averaging algorithm for the CompTimer.

View Source
var DefaultFillPattern = []byte("irtt")

DefaultFillPattern is the default fill pattern.

View Source
var DefaultOpenTimeouts = Durations([]time.Duration{
	1 * time.Second,
	2 * time.Second,
	4 * time.Second,
	8 * time.Second,
})

DefaultOpenTimeouts are the default timeouts used when the client opens a connection to the server.

View Source
var DefaultServerFiller = NewDefaultPatternFiller()

DefaultServerFiller it the default filler for the server, PatternFiller.

DefaultTimer is the default timer implementation, CompTimer.

View Source
var DefaultWait = &WaitMaxRTT{time.Duration(4) * time.Second, 3}

DefaultWait is the default client wait time for the final responses after all packets have been sent.

View Source
var FillerFactories = make([]FillerFactory, 0)

FillerFactories are the registered Filler factories.

View Source
var JSONFormatVersion = 1

JSONFormatVersion is the JSON format number.

View Source
var ProtocolVersion = 1

ProtocolVersion is the protocol version number, which must match between client and server.

View Source
var TimerFactories = make([]TimerFactory, 0)

TimerFactories are the registered Timer factories.

View Source
var Version = "0.9.0"

Version is the IRTT version number (replaced during build).

View Source
var WaiterFactories = make([]WaiterFactory, 0)

WaiterFactories are the registered Waiter factories.

Functions

func AbsDuration

func AbsDuration(d time.Duration) time.Duration

AbsDuration returns the absolute value of a duration.

func RegisterAverager

func RegisterAverager(fn func(string) (Averager, error), usage string)

RegisterAverager registers a new Averager.

func RegisterFiller

func RegisterFiller(fn func(string) (Filler, error), usage string)

RegisterFiller registers a new Filler.

func RegisterTimer

func RegisterTimer(fn func(string, Averager) (Timer, error), usage string)

RegisterTimer registers a new Timer.

func RegisterWaiter

func RegisterWaiter(fn func(string) (Waiter, error), usage string)

RegisterWaiter registers a new Waiter.

func RunCLI

func RunCLI(args []string)

RunCLI runs the command line interface with the given arguments (typically os.Args).

Types

type AllowStamp

type AllowStamp int

AllowStamp selects the timestamps that are allowed by the server.

const (
	NoStamp AllowStamp = iota
	SingleStamp
	DualStamps
)

AllowStamp constants.

func ParseAllowStamp

func ParseAllowStamp(s string) (AllowStamp, error)

ParseAllowStamp returns an AllowStamp from a string.

func (AllowStamp) Restrict

func (a AllowStamp) Restrict(at StampAt) StampAt

Restrict returns the StampAt allowed for a given StampAt requested.

func (AllowStamp) String

func (a AllowStamp) String() string

type Averager

type Averager interface {
	// Push adds a value to be averaged.
	Push(val float64)

	// Average returns the average.
	Average() float64

	String() string
}

Averager is implemented to return an average of a series of given values.

func NewAverager

func NewAverager(s string) (Averager, error)

NewAverager returns an Averager from a string.

type AveragerFactory

type AveragerFactory struct {
	FactoryFunc func(string) (Averager, error)
	Usage       string
}

AveragerFactory is the definition for an Averager.

type Bitrate

type Bitrate uint64

Bitrate is a bit rate in bits per second.

func (Bitrate) MarshalJSON

func (r Bitrate) MarshalJSON() ([]byte, error)

MarshalJSON implements the json.Marshaler interface.

func (Bitrate) String

func (r Bitrate) String() string

String returns a Bitrate string in appropriate units.

type BusyTimer

type BusyTimer struct {
}

BusyTimer uses a busy wait loop to wait for the next send. It wastes CPU and should only be used for extremely tight timing requirements.

func (*BusyTimer) Sleep

func (bt *BusyTimer) Sleep(ctx context.Context, t time.Time, d time.Duration) (time.Time, error)

Sleep waits with a busy loop and checks the done channel every iteration.

func (*BusyTimer) String

func (bt *BusyTimer) String() string

type Client

type Client struct {
	*ClientConfig
	// contains filtered or unexported fields
}

Client is the Client. It must be created with NewClient. It may not be used concurrently.

func NewClient

func NewClient(cfg *ClientConfig) *Client

NewClient returns a new client.

func (*Client) Run

func (c *Client) Run(ctx context.Context) (r *Result, err error)

Run runs the test and returns the Result. An error is returned if the test could not be started. If an error occurs during the test, the error is nil, partial results are returned and either or both of the SendErr or ReceiveErr fields of Result will be non-nil. Run may only be called once.

type ClientConfig

type ClientConfig struct {
	LocalAddress  string
	RemoteAddress string
	LocalAddr     net.Addr
	RemoteAddr    net.Addr
	OpenTimeouts  Durations
	NoTest        bool
	Params
	Loose      bool
	IPVersion  IPVersion
	DF         DF
	TTL        int
	Timer      Timer
	Waiter     Waiter
	Filler     Filler
	FillOne    bool
	HMACKey    []byte
	Handler    ClientHandler
	ThreadLock bool
	Supplied   *ClientConfig
}

ClientConfig defines the Client configuration.

func NewClientConfig

func NewClientConfig() *ClientConfig

NewClientConfig returns a new ClientConfig with the default settings.

func (*ClientConfig) MarshalJSON

func (c *ClientConfig) MarshalJSON() ([]byte, error)

MarshalJSON implements the json.Marshaler interface.

type ClientHandler

type ClientHandler interface {
	Handler

	RecorderHandler
}

ClientHandler is called with client events, as well as separately when packets are sent and received. See the documentation for Recorder for information on locking for concurrent access.

type Clock

type Clock int

Clock selects the clock/s to use for timestamps.

const (
	Wall       Clock = 0x01
	Monotonic  Clock = 0x02
	BothClocks Clock = Wall | Monotonic
)

Clock constants.

func ClockFromInt

func ClockFromInt(v int) (Clock, error)

ClockFromInt returns a Clock value from its int constant.

func ParseClock

func ParseClock(s string) (Clock, error)

ParseClock returns a Clock from a string.

func (Clock) MarshalJSON

func (tc Clock) MarshalJSON() ([]byte, error)

MarshalJSON implements the json.Marshaler interface.

func (Clock) String

func (tc Clock) String() string

type Code

type Code int

Code uniquely identifies events and errors to improve context.

const (
	ShortWrite Code = -1 * (iota + 1)
	InvalidDFString
	FieldsLengthTooLarge
	FieldsCapacityTooLarge
	InvalidStampAtString
	InvalidStampAtInt
	InvalidAllowStampString
	InvalidClockString
	InvalidClockInt
	BadMagic
	NoHMAC
	BadHMAC
	UnexpectedHMAC
	NonexclusiveMidpointTStamp
	InconsistentClocks
	DFNotSupported
	InvalidFlagBitsSet
	ShortParamBuffer
	ParamOverflow
	InvalidParamValue
	ProtocolVersionMismatch
)

Common error codes.

const (
	NoMatchingInterfaces Code = -1 * (iota + 1*1024)
	NoMatchingInterfacesUp
	UnspecifiedWithSpecifiedAddresses
	InvalidGCModeString
	UnexpectedReplyFlag
	NoSuitableAddressFound
	InvalidConnToken
	ShortInterval
	LargeRequest
	AddressMismatch
)

Server error codes.

const (
	InvalidWinAvgWindow Code = -1 * (iota + 2*1024)
	InvalidExpAvgAlpha
	AllocateResultsPanic
	UnexpectedOpenFlag
	DFError
	TTLError
	ExpectedReplyFlag
	ShortReply
	StampAtMismatch
	ClockMismatch
	UnexpectedSequenceNumber
	InvalidSleepFactor
	InvalidWaitString
	InvalidWaitFactor
	InvalidWaitDuration
	NoSuchAverager
	NoSuchFiller
	NoSuchTimer
	NoSuchWaiter
	IntervalNonPositive
	DurationNonPositive
	ConnTokenZero
	ServerClosed
	OpenTimeout
	InvalidServerRestriction
	InvalidReceivedStatsInt
	InvalidReceivedStatsString
	OpenTimeoutTooShort
	ServerFillTooLong
)

Client error codes.

const (
	MultipleAddresses Code = iota + 1*1024
	ServerStart
	ServerStop
	ListenerStart
	ListenerStop
	ListenerError
	Drop
	NewConn
	OpenClose
	CloseConn
	NoDSCPSupport
	ExceededDuration
	NoReceiveDstAddrSupport
	RemoveNoConn
	InvalidServerFill
)

Server event codes.

const (
	Connecting Code = iota + 2*1024
	Connected
	WaitForPackets
	ServerRestriction
	NoTest
	ConnectedClosed
)

Client event codes.

func (Code) String

func (i Code) String() string

type CompTimer

type CompTimer struct {
	MinErrorFactor float64 `json:"min_error_factor"`
	MaxErrorFactor float64 `json:"max_error_factor"`
	// contains filtered or unexported fields
}

CompTimer uses Go's default time functions and performs compensation by continually measuring the timer error and applying a correction factor to try to improve precision. It must be created using NewCompTimer. MinErrorFactor and MaxErrorFactor may be adjusted to reject correction factor outliers, which may be seen before enough data is collected. They default to 0 and 2, respectively.

func NewCompTimer

func NewCompTimer(a Averager) *CompTimer

NewCompTimer returns a new CompTimer with the specified Average. MinErrorFactor and MaxErrorFactor may be changed before use.

func NewDefaultCompTimer

func NewDefaultCompTimer() *CompTimer

NewDefaultCompTimer returns a new CompTimer with the default Average. MinErrorFactor and MaxErrorFactor may be changed before use.

func (*CompTimer) Sleep

func (ct *CompTimer) Sleep(ctx context.Context, t time.Time, d time.Duration) (time.Time, error)

Sleep selects on both a time.Timer channel and the done channel.

func (*CompTimer) String

func (ct *CompTimer) String() string

type CumulativeAverager

type CumulativeAverager struct {
	// contains filtered or unexported fields
}

CumulativeAverager implements the cumulative moving average (takes into account all values equally).

func (*CumulativeAverager) Average

func (ca *CumulativeAverager) Average() float64

Average gets the cumulative average.

func (*CumulativeAverager) Push

func (ca *CumulativeAverager) Push(val float64)

Push adds a value.

func (*CumulativeAverager) String

func (ca *CumulativeAverager) String() string

type DF

type DF int

DF is the value for the do not fragment bit.

const (
	DFDefault DF = iota
	DFFalse
	DFTrue
)

DF constants.

func ParseDF

func ParseDF(s string) (DF, error)

ParseDF returns a DF value from its string.

func (DF) String

func (d DF) String() string

type DurationStats

type DurationStats struct {
	Total time.Duration `json:"total"`
	N     uint          `json:"n"`
	Min   time.Duration `json:"min"`
	Max   time.Duration `json:"max"`
	// contains filtered or unexported fields
}

DurationStats keeps basic time.Duration statistics. Welford's method is used to keep a running mean and standard deviation. In testing, this seemed to be worth the extra muls and divs necessary to maintain these stats. Worst case, there was a 2% reduction in the send rate on a Raspberry Pi 2 when sending the smallest packets possible at the smallest interval possible. This is not a typical test, however, and the argument is, it's worth paying this price to add standard deviation and variance for timer error and send call time, and running standard deviation for all packet times.

func (*DurationStats) IsZero

func (s *DurationStats) IsZero() bool

IsZero returns true if DurationStats has no recorded values.

func (*DurationStats) MarshalJSON

func (s *DurationStats) MarshalJSON() ([]byte, error)

MarshalJSON implements the json.Marshaler interface.

func (*DurationStats) Mean

func (s *DurationStats) Mean() time.Duration

Mean returns the arithmetical mean.

func (*DurationStats) Median

func (s *DurationStats) Median() (dur time.Duration, ok bool)

Median returns the median (externally calculated).

func (*DurationStats) Stddev

func (s *DurationStats) Stddev() time.Duration

Stddev returns the standard deviation.

func (*DurationStats) Variance

func (s *DurationStats) Variance() float64

Variance returns the variance.

type Durations

type Durations []time.Duration

Durations contains a slice of time.Duration.

func ParseDurations

func ParseDurations(sdurs string) (durs Durations, err error)

ParseDurations returns a Durations value from a comma separated list of time.Duration string representations.

func (Durations) String

func (ds Durations) String() string

type Error

type Error struct {
	*Event
}

Error is an IRTT error.

func Errorf

func Errorf(code Code, format string, detail ...interface{}) *Error

Errorf returns a new Error.

func (*Error) Error

func (e *Error) Error() string

type Event

type Event struct {
	Code       Code
	LocalAddr  *net.UDPAddr
	RemoteAddr *net.UDPAddr

	Detail []interface{}
	// contains filtered or unexported fields
}

Event is an event sent to a Handler.

func Eventf

func Eventf(code Code, laddr *net.UDPAddr, raddr *net.UDPAddr, format string,
	detail ...interface{}) *Event

Eventf returns a new event.

func (*Event) String

func (e *Event) String() string

type ExponentialAverager

type ExponentialAverager struct {
	Alpha float64
	// contains filtered or unexported fields
}

ExponentialAverager implements the exponential moving average. More recent values are given higher consideration. Alpha must be between 0 and 1, where a higher Alpha discounts older values faster. An Alpha of 0.1 - 0.2 may give good results for timer compensation, but experimentation is required as results are dependent on hardware and test config.

func NewDefaultExponentialAverager

func NewDefaultExponentialAverager() *ExponentialAverager

NewDefaultExponentialAverager returns a new ExponentialAverage with the default Alpha. This may be changed before used.

func NewExponentialAverager

func NewExponentialAverager(alpha float64) *ExponentialAverager

NewExponentialAverager returns a new ExponentialAverage with the specified Alpha.

func (*ExponentialAverager) Average

func (ea *ExponentialAverager) Average() float64

Average gets the exponential average.

func (*ExponentialAverager) Push

func (ea *ExponentialAverager) Push(val float64)

Push adds a value.

func (*ExponentialAverager) String

func (ea *ExponentialAverager) String() string

type Filler

type Filler interface {
	io.Reader

	String() string
}

Filler is a Reader used for filling the payload in packets.

func NewFiller

func NewFiller(s string) (Filler, error)

NewFiller returns a Filler from a string.

type FillerFactory

type FillerFactory struct {
	FactoryFunc func(string) (Filler, error)
	Usage       string
}

FillerFactory can create a Filler from a string.

type GCMode

type GCMode int

GCMode selects when the garbage collector is run.

const (
	GCOn GCMode = iota
	GCOff
	GCIdle
)

StampAt constants.

func ParseGCMode

func ParseGCMode(s string) (GCMode, error)

ParseGCMode returns a GCMode value from its string.

func (GCMode) String

func (gm GCMode) String() string

type Handler

type Handler interface {
	// OnEvent is called when an event occurs.
	OnEvent(e *Event)
}

Handler is called with events.

type HybridTimer

type HybridTimer struct {
	// contains filtered or unexported fields
}

HybridTimer uses Go's default time functions and performs compensation to try to improve precision. To further improve precision, it sleeps to within some factor of the target value, then uses a busy wait loop for the remainder. The CPU will be in a busy wait for 1 - sleep factor for each sleep performed, so ideally the sleep factor should be increased to some threshold before precision starts to be lost, or some balance between the desired precision and CPU load is struck. The sleep factor typically can be increased for longer intervals and must be decreased for shorter intervals to keep high precision. In one example, a sleep factor of 0.95 could be used for 15ns precision at an interval of 200ms, but a sleep factor of 0.80 was required for 100ns precision at an interval of 1ms. These requirements will likely vary for different hardware and OS combinations.

func NewDefaultHybridTimer

func NewDefaultHybridTimer() *HybridTimer

NewDefaultHybridTimer returns a new HybridTimer using the default Average and sleep factor.

func NewHybridTimer

func NewHybridTimer(a Averager, sleepFactor float64) *HybridTimer

NewHybridTimer returns a new HybridTimer using the given Average algorithm and sleep factor (0 - 1.0) before the busy wait.

func (*HybridTimer) Sleep

func (ht *HybridTimer) Sleep(ctx context.Context, t time.Time, d time.Duration) (time.Time, error)

Sleep selects on both a time.Timer channel and the done channel.

func (*HybridTimer) SleepFactor

func (ht *HybridTimer) SleepFactor() float64

SleepFactor returns the sleep factor.

func (*HybridTimer) String

func (ht *HybridTimer) String() string

type IPVersion

type IPVersion int

IPVersion is an IP version, or dual stack for IPv4 and IPv6.

const (
	IPv4 IPVersion = 1 << iota
	IPv6
	DualStack = IPv4 | IPv6
)

IPVersion constants.

func IPVersionFromBooleans

func IPVersionFromBooleans(ipv4 bool, ipv6 bool, dfl IPVersion) IPVersion

IPVersionFromBooleans returns an IPVersion from booleans. If both ipv4 and ipv6 are true, DualStack is returned. If neither are true, the value of dfl is returned.

func IPVersionFromIP

func IPVersionFromIP(ip net.IP) IPVersion

IPVersionFromIP returns an IPVersion from a net.IP.

func IPVersionFromUDPAddr

func IPVersionFromUDPAddr(addr *net.UDPAddr) IPVersion

IPVersionFromUDPAddr returns an IPVersion from a net.UDPAddr.

func (IPVersion) MarshalJSON

func (v IPVersion) MarshalJSON() ([]byte, error)

MarshalJSON implements the json.Marshaler interface.

func (IPVersion) Separate

func (v IPVersion) Separate() []IPVersion

Separate returns a slice of IPVersions, separating DualStack into IPv4 and IPv6 if necessary.

func (IPVersion) String

func (v IPVersion) String() string

func (IPVersion) ZeroIP

func (v IPVersion) ZeroIP() net.IP

ZeroIP returns the zero IP for the IPVersion (net.IPv4zero for IPv4 and otherwise net.IPv6zero).

type Lost

type Lost int

Lost indicates the lost status of a packet.

const (
	LostTrue Lost = iota
	LostDown
	LostUp
	LostFalse
)

Lost constants.

func (Lost) MarshalJSON

func (l Lost) MarshalJSON() ([]byte, error)

MarshalJSON implements the json.Marshaler interface.

func (Lost) String

func (l Lost) String() string

type Params

type Params struct {
	ProtocolVersion int           `json:"proto_version"`
	Duration        time.Duration `json:"duration"`
	Interval        time.Duration `json:"interval"`
	Length          int           `json:"length"`
	ReceivedStats   ReceivedStats `json:"received_stats"`
	StampAt         StampAt       `json:"stamp_at"`
	Clock           Clock         `json:"clock"`
	DSCP            int           `json:"dscp"`
	ServerFill      string        `json:"server_fill"`
}

Params are the test parameters sent to and received from the server.

type PatternFiller

type PatternFiller struct {
	Bytes []byte
	// contains filtered or unexported fields
}

PatternFiller can be used to fill with a repeating byte pattern.

func NewDefaultPatternFiller

func NewDefaultPatternFiller() *PatternFiller

NewDefaultPatternFiller returns a new PatternFiller with the default pattern.

func NewPatternFiller

func NewPatternFiller(bytes []byte) *PatternFiller

NewPatternFiller returns a new PatternFiller.

func (*PatternFiller) Read

func (f *PatternFiller) Read(p []byte) (n int, err error)

func (*PatternFiller) String

func (f *PatternFiller) String() string

type RandFiller

type RandFiller struct {
	*rand.Rand
}

RandFiller is a Filler that fills with data from math.rand.

func NewRandFiller

func NewRandFiller() *RandFiller

NewRandFiller returns a new RandFiller.

func (*RandFiller) String

func (rf *RandFiller) String() string

type ReceivedCount

type ReceivedCount uint32

ReceivedCount is the received packet count.

type ReceivedStats

type ReceivedStats int

ReceivedStats selects what information to gather about received packets.

const (
	ReceivedStatsNone   ReceivedStats = 0x00
	ReceivedStatsCount  ReceivedStats = 0x01
	ReceivedStatsWindow ReceivedStats = 0x02
	ReceivedStatsBoth   ReceivedStats = ReceivedStatsCount | ReceivedStatsWindow
)

ReceivedStats constants.

func ParseReceivedStats

func ParseReceivedStats(s string) (ReceivedStats, error)

ParseReceivedStats returns a ReceivedStats value from its string.

func ReceivedStatsFromInt

func ReceivedStatsFromInt(v int) (ReceivedStats, error)

ReceivedStatsFromInt returns a ReceivedStats value from its int constant.

func (ReceivedStats) MarshalJSON

func (rs ReceivedStats) MarshalJSON() ([]byte, error)

MarshalJSON implements the json.Marshaler interface.

func (ReceivedStats) String

func (rs ReceivedStats) String() string

type ReceivedWindow

type ReceivedWindow uint64

ReceivedWindow is the received packet window.

type Recorder

type Recorder struct {
	Start                 time.Time       `json:"start_time"`
	FirstSend             time.Time       `json:"-"`
	LastSent              time.Time       `json:"-"`
	FirstReceived         time.Time       `json:"-"`
	LastReceived          time.Time       `json:"-"`
	SendCallStats         DurationStats   `json:"send_call"`
	TimerErrorStats       DurationStats   `json:"timer_error"`
	RTTStats              DurationStats   `json:"rtt"`
	SendDelayStats        DurationStats   `json:"send_delay"`
	ReceiveDelayStats     DurationStats   `json:"receive_delay"`
	ServerPacketsReceived ReceivedCount   `json:"server_packets_received"`
	BytesSent             uint64          `json:"bytes_sent"`
	BytesReceived         uint64          `json:"bytes_received"`
	Duplicates            uint            `json:"duplicates"`
	LatePackets           uint            `json:"late_packets"`
	Wait                  time.Duration   `json:"wait"`
	RoundTripData         []RoundTripData `json:"-"`
	RecorderHandler       RecorderHandler `json:"-"`
	// contains filtered or unexported fields
}

Recorder is used to record data during the test. It is available to the Handler during the test for display of basic statistics, and may be used later to create a Result for further statistical analysis and storage. Recorder is accessed concurrently while the test is running, so its RLock and RUnlock methods must be used during read access to prevent race conditions. When RecorderHandler is called, it is already locked and must not be locked again. It is not possible to lock Recorder externally for write, since all recording should be done internally.

func (*Recorder) RLock

func (r *Recorder) RLock()

RLock locks the Recorder for reading.

func (*Recorder) RUnlock

func (r *Recorder) RUnlock()

RUnlock unlocks the Recorder for reading.

type RecorderHandler

type RecorderHandler interface {
	// OnSent is called when a packet is sent.
	OnSent(seqno Seqno, rtd *RoundTripData)

	// OnReceived is called when a packet is received.
	OnReceived(seqno Seqno, rtd *RoundTripData, pred *RoundTripData, late bool, dup bool)
}

RecorderHandler is called when the Recorder records a sent or received packet.

type Result

type Result struct {
	VersionInfo *VersionInfo  `json:"version"`
	SystemInfo  *SystemInfo   `json:"system_info"`
	Config      *ClientConfig `json:"config"`
	SendErr     error         `json:"send_err,omitempty"`
	ReceiveErr  error         `json:"receive_err,omitempty"`
	*Stats      `json:"stats"`
	RoundTrips  []RoundTrip `json:"round_trips"`
}

Result is returned from Run.

type RoundTrip

type RoundTrip struct {
	Seqno          Seqno `json:"seqno"`
	Lost           Lost  `json:"lost"`
	*RoundTripData `json:"timestamps"`
	IPDV           time.Duration `json:"-"`
	SendIPDV       time.Duration `json:"-"`
	ReceiveIPDV    time.Duration `json:"-"`
}

RoundTrip stores the Timestamps and statistics for a single round trip.

func (*RoundTrip) MarshalJSON

func (rt *RoundTrip) MarshalJSON() ([]byte, error)

MarshalJSON implements the json.Marshaler interface.

type RoundTripData

type RoundTripData struct {
	Client Timestamp `json:"client"`
	Server Timestamp `json:"server"`
	// contains filtered or unexported fields
}

RoundTripData contains the information recorded for each round trip during the test.

func (*RoundTripData) IPDVSince

func (ts *RoundTripData) IPDVSince(pts *RoundTripData) time.Duration

IPDVSince returns the instantaneous packet delay variation since the specified RoundTripData.

func (*RoundTripData) IsBothTimestamped

func (ts *RoundTripData) IsBothTimestamped() bool

IsBothTimestamped returns true if the server returned both a send and receive timestamp.

func (*RoundTripData) IsMonoTimestamped

func (ts *RoundTripData) IsMonoTimestamped() bool

IsMonoTimestamped returns true if the server returned any timestamp with a valid monotonic clock value.

func (*RoundTripData) IsReceiveTimestamped

func (ts *RoundTripData) IsReceiveTimestamped() bool

IsReceiveTimestamped returns true if the server returned a receive timestamp.

func (*RoundTripData) IsSendTimestamped

func (ts *RoundTripData) IsSendTimestamped() bool

IsSendTimestamped returns true if the server returned a send timestamp.

func (*RoundTripData) IsTimestamped

func (ts *RoundTripData) IsTimestamped() bool

IsTimestamped returns true if the server returned any timestamp.

func (*RoundTripData) IsWallTimestamped

func (ts *RoundTripData) IsWallTimestamped() bool

IsWallTimestamped returns true if the server returned any timestamp with a valid wall clock value.

func (*RoundTripData) RTT

func (ts *RoundTripData) RTT() (rtt time.Duration)

RTT returns the round-trip time. The monotonic clock values are used for accuracy, and the server processing time is subtracted out if both send and receive timestamps are enabled.

func (*RoundTripData) ReceiveDelay

func (ts *RoundTripData) ReceiveDelay() time.Duration

ReceiveDelay returns the estimated one-way receive delay, valid only if wall clock timestamps are available and the server's system time has been externally synchronized.

func (*RoundTripData) ReceiveIPDVSince

func (ts *RoundTripData) ReceiveIPDVSince(pts *RoundTripData) (d time.Duration)

ReceiveIPDVSince returns the receive instantaneous packet delay variation since the specified RoundTripData.

func (*RoundTripData) ReceiveMonoDiff

func (ts *RoundTripData) ReceiveMonoDiff() time.Duration

ReceiveMonoDiff returns the difference in receive values from the monotonic clock. This is useful for measuring receive IPDV (jitter), but not for absolute receive delay.

func (*RoundTripData) ReceiveWallDiff

func (ts *RoundTripData) ReceiveWallDiff() time.Duration

ReceiveWallDiff returns the difference in receive values from the wall clock. This is useful for measuring receive IPDV (jitter), but not for absolute receive delay. Because the wall clock is used, it is subject to wall clock variability.

func (*RoundTripData) ReplyReceived

func (ts *RoundTripData) ReplyReceived() bool

ReplyReceived returns true if a reply was received from the server.

func (*RoundTripData) SendDelay

func (ts *RoundTripData) SendDelay() time.Duration

SendDelay returns the estimated one-way send delay, valid only if wall clock timestamps are available and the server's system time has been externally synchronized.

func (*RoundTripData) SendIPDVSince

func (ts *RoundTripData) SendIPDVSince(pts *RoundTripData) (d time.Duration)

SendIPDVSince returns the send instantaneous packet delay variation since the specified RoundTripData.

func (*RoundTripData) SendMonoDiff

func (ts *RoundTripData) SendMonoDiff() time.Duration

SendMonoDiff returns the difference in send values from the monotonic clock. This is useful for measuring send IPDV (jitter), but not for absolute send delay.

func (*RoundTripData) SendWallDiff

func (ts *RoundTripData) SendWallDiff() time.Duration

SendWallDiff returns the difference in send values from the wall clock. This is useful for measuring receive IPDV (jitter), but not for absolute send delay. Because the wall clock is used, it is subject to wall clock variability.

func (*RoundTripData) ServerProcessingTime

func (ts *RoundTripData) ServerProcessingTime() (d time.Duration)

ServerProcessingTime returns the amount of time between when the server received a request and when it sent its reply.

type Seqno

type Seqno uint32

Seqno is a sequence number.

type Server

type Server struct {
	*ServerConfig
	// contains filtered or unexported fields
}

Server is the irtt server.

func NewServer

func NewServer(cfg *ServerConfig) *Server

NewServer returns a new server.

func (*Server) ListenAndServe

func (s *Server) ListenAndServe() error

ListenAndServe creates listeners for all requested addresses and serves requests indefinitely. It exits after the listeners have exited. Errors for individual listeners may be handled with a ServerHandler, and will not be returned from this method.

func (*Server) Shutdown

func (s *Server) Shutdown()

Shutdown stops the Server. After this call, the Server may no longer be used.

type ServerConfig

type ServerConfig struct {
	Addrs       []string
	HMACKey     []byte
	MaxDuration time.Duration
	MinInterval time.Duration
	MaxLength   int
	Timeout     time.Duration
	PacketBurst int
	Filler      Filler
	AllowFills  []string
	AllowStamp  AllowStamp
	AllowDSCP   bool
	TTL         int
	IPVersion   IPVersion
	Handler     Handler
	SetSrcIP    bool
	GCMode      GCMode
	ThreadLock  bool
}

ServerConfig defines the Server configuration.

func NewServerConfig

func NewServerConfig() *ServerConfig

NewServerConfig returns a new ServerConfig with the default settings.

type SimpleTimer

type SimpleTimer struct {
	// contains filtered or unexported fields
}

SimpleTimer uses Go's default time functions. It must be created using NewSimpleTimer.

func NewSimpleTimer

func NewSimpleTimer() *SimpleTimer

NewSimpleTimer returns a new SimpleTimer.

func (*SimpleTimer) Sleep

func (st *SimpleTimer) Sleep(ctx context.Context, t time.Time, d time.Duration) (time.Time, error)

Sleep selects on both a time.Timer channel and the done channel.

func (*SimpleTimer) String

func (st *SimpleTimer) String() string

type StampAt

type StampAt int

StampAt selects the time/s when timestamps are made on the server.

const (
	AtNone     StampAt = 0x00
	AtSend     StampAt = 0x01
	AtReceive  StampAt = 0x02
	AtBoth     StampAt = AtSend | AtReceive
	AtMidpoint StampAt = 0x04
)

StampAt constants.

func ParseStampAt

func ParseStampAt(s string) (StampAt, error)

ParseStampAt returns a StampAt value from its string.

func StampAtFromInt

func StampAtFromInt(v int) (StampAt, error)

StampAtFromInt returns a StampAt value from its int constant.

func (StampAt) MarshalJSON

func (sa StampAt) MarshalJSON() ([]byte, error)

MarshalJSON implements the json.Marshaler interface.

func (StampAt) String

func (sa StampAt) String() string

type Stats

type Stats struct {
	*Recorder
	Duration                  time.Duration `json:"duration"`
	ExpectedPacketsSent       uint          `json:"-"`
	PacketsSent               uint          `json:"packets_sent"`
	PacketsReceived           uint          `json:"packets_received"`
	PacketLossPercent         float64       `json:"packet_loss_percent"`
	UpstreamLossPercent       float64       `json:"upstream_loss_percent"`
	DownstreamLossPercent     float64       `json:"downstream_loss_percent"`
	DuplicatePercent          float64       `json:"duplicate_percent"`
	LatePacketsPercent        float64       `json:"late_packets_percent"`
	SendIPDVStats             DurationStats `json:"ipdv_send"`
	ReceiveIPDVStats          DurationStats `json:"ipdv_receive"`
	RoundTripIPDVStats        DurationStats `json:"ipdv_round_trip"`
	ServerProcessingTimeStats DurationStats `json:"server_processing_time"`
	TimerErrPercent           float64       `json:"timer_err_percent"`
	TimerMisses               uint          `json:"timer_misses"`
	TimerMissPercent          float64       `json:"timer_miss_percent"`
	SendRate                  Bitrate       `json:"send_rate"`
	ReceiveRate               Bitrate       `json:"receive_rate"`
}

Stats are the statistics in the Result.

type SystemInfo

type SystemInfo struct {
	OS        string `json:"os"`
	NumCPU    int    `json:"cpus"`
	GoVersion string `json:"go_version"`
	Hostname  string `json:"hostname"`
}

SystemInfo stores based system information.

func NewSystemInfo

func NewSystemInfo() *SystemInfo

NewSystemInfo returns a new SystemInfo.

type Time

type Time struct {
	Wall int64         `json:"wall,omitempty"`
	Mono time.Duration `json:"monotonic,omitempty"`
}

Time contains both wall clock (subject to system time adjustments) and monotonic clock (relative to a fixed start time, and not subject to system time adjustments) times in nanoseconds. The monotonic value should be used for calculating time differences, and the wall value must be used for comparing wall clock time. Comparisons between wall clock values are only as accurate as the synchronization between the clocks that produced the values.

func (Time) IsMonoZero

func (ts Time) IsMonoZero() bool

IsMonoZero returns true if Mono is zero.

func (Time) IsWallZero

func (ts Time) IsWallZero() bool

IsWallZero returns true if Wall is zero.

func (Time) IsZero

func (ts Time) IsZero() bool

IsZero returns true if both Wall and Mono are zero.

type Timer

type Timer interface {
	// Sleep waits for at least duration d and returns the current time. The
	// current time is passed as t as a convenience for timers performing error
	// compensation. Timers should obey the Context and use a select that
	// includes ctx.Done() so that the sleep can be terminated early. In that
	// case, ctx.Err() should be returned.
	Sleep(ctx context.Context, t time.Time, d time.Duration) (time.Time, error)

	String() string
}

Timer is implemented to wait for the next send.

func NewTimer

func NewTimer(s string, a Averager) (Timer, error)

NewTimer returns a Timer from a string.

type TimerFactory

type TimerFactory struct {
	FactoryFunc func(string, Averager) (Timer, error)
	Usage       string
}

TimerFactory can create a Timer from a string.

type Timestamp

type Timestamp struct {
	Receive Time `json:"receive"`
	Send    Time `json:"send"`
}

Timestamp stores receive and send times. If the Timestamp was set to the midpoint on the server, Receive and Send will be the same.

func (Timestamp) BestReceive

func (t Timestamp) BestReceive() Time

BestReceive returns the best receive time. It prefers the actual receive time, but returns the receive time if it's not available.

func (Timestamp) BestSend

func (t Timestamp) BestSend() Time

BestSend returns the best send time. It prefers the actual send time, but returns the receive time if it's not available.

func (Timestamp) IsBothMono

func (t Timestamp) IsBothMono() bool

IsBothMono returns true if there are both send and receive times from the monotonic clock.

func (Timestamp) IsBothWall

func (t Timestamp) IsBothWall() bool

IsBothWall returns true if there are both send and receive times from the wall clock.

func (Timestamp) IsMidpoint

func (t Timestamp) IsMidpoint() bool

IsMidpoint returns true if this Timestamp was made with the midpoint time (halfway between send and receive). If so, Send and Receive are both non-zero and the same.

type VersionInfo

type VersionInfo struct {
	IRTT       string `json:"irtt"`
	Protocol   int    `json:"protocol"`
	JSONFormat int    `json:"json_format"`
}

VersionInfo stores the version information.

func NewVersionInfo

func NewVersionInfo() *VersionInfo

NewVersionInfo returns a new VersionInfo.

type WaitDuration

type WaitDuration struct {
	D time.Duration `json:"d"`
}

WaitDuration waits for a specific period of time.

func (*WaitDuration) String

func (w *WaitDuration) String() string

func (*WaitDuration) Wait

func (w *WaitDuration) Wait(r *Recorder) time.Duration

Wait returns the wait duration.

type WaitMaxRTT

type WaitMaxRTT struct {
	D      time.Duration `json:"d"`
	Factor int           `json:"factor"`
}

WaitMaxRTT waits for a factor of the maximum RTT

func (*WaitMaxRTT) String

func (w *WaitMaxRTT) String() string

func (*WaitMaxRTT) Wait

func (w *WaitMaxRTT) Wait(r *Recorder) time.Duration

Wait returns the wait duration.

type WaitMeanRTT

type WaitMeanRTT struct {
	D      time.Duration `json:"d"`
	Factor int           `json:"factor"`
}

WaitMeanRTT waits for a factor of the mean RTT.

func (*WaitMeanRTT) String

func (w *WaitMeanRTT) String() string

func (*WaitMeanRTT) Wait

func (w *WaitMeanRTT) Wait(r *Recorder) time.Duration

Wait returns the wait duration.

type Waiter

type Waiter interface {
	// Wait returns the wait duration.
	Wait(r *Recorder) time.Duration

	String() string
}

Waiter is implemented to return a wait time for final replies. See the documentation for Recorder for information on locking for concurrent access.

func NewWaiter

func NewWaiter(s string) (Waiter, error)

NewWaiter returns a Waiter from a string.

type WaiterFactory

type WaiterFactory struct {
	FactoryFunc func(string) (Waiter, error)
	Usage       string
}

WaiterFactory can create a Waiter from a string.

type WindowAverager

type WindowAverager struct {
	Window int
	// contains filtered or unexported fields
}

WindowAverager implements the moving average with a specified window.

func NewDefaultWindowAverager

func NewDefaultWindowAverager() *WindowAverager

NewDefaultWindowAverager returns a new WindowAverage with the default window.

func NewWindowAverage

func NewWindowAverage(window int) *WindowAverager

NewWindowAverage returns a new WindowAverage with the specified window.

func (*WindowAverager) Average

func (wa *WindowAverager) Average() float64

Average gets the moving average.

func (*WindowAverager) Push

func (wa *WindowAverager) Push(val float64)

Push adds a value.

func (*WindowAverager) String

func (wa *WindowAverager) String() string

Directories

Path Synopsis
cmd

Jump to

Keyboard shortcuts

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