udpt

package module
v0.0.1 Latest Latest
Warning

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

Go to latest
Published: Apr 28, 2021 License: MIT Imports: 16 Imported by: 1

README

udpt

UDP Transport

Go Report Card godoc

Compresses, encrypts and transfers data between a sender and receiver using UDP protocol.

Features and Design Aims:

  • Avoid the overhead of establishing a TCP or TCP+TLS handshake.
  • Reliable transfer of data using an unreliable UDP connection.
  • Uses AES-256 symmetric cipher for encryption.
  • Uses zlib library for data compression.
  • No third-party dependencies. Only uses the standard library.
  • Readable, understandable code with explanatory comments.

Installation:

    go get github.com/balacode/udpt

Hello World:

This demo runs a receiver using RunReceiver() which listens for incoming data, then sends a "Hello World" to the receiver using Send().

package main

import (
    "fmt"
    "strings"
    "time"

    "github.com/balacode/udpt"
)

func main() {
    //
    // Specify required configuration parameters:
    //
    // Address is used by the sender to connect to the Receiver.
    //
    // Port is the port number on which the sender
    // sends and the receiver listens.
    //
    // AESKey is the secret AES encryption key shared by the
    // Sender and the Receiver. It must be exactly 32 bytes.
    //
    udpt.Config.Address = "127.0.0.1"
    udpt.Config.Port = 1234
    udpt.Config.AESKey = []byte{
        0xC4, 0x53, 0x67, 0xA7, 0xB7, 0x94, 0xE5, 0x30,
        0x6C, 0x4F, 0x43, 0x6C, 0xA9, 0x33, 0x85, 0xEA,
        0x1C, 0x37, 0xE3, 0x66, 0x7F, 0x14, 0x05, 0xE6,
        0x2F, 0x8F, 0xC6, 0x12, 0x67, 0x04, 0x86, 0xD1,
    }
    // disable log caching and enable verbose messages.
    // This should be done only during demos/prototyping/debugging.
    udpt.LogBufferSize = -1
    udpt.Config.VerboseSender = true
    udpt.Config.VerboseReceiver = true
    //
    prt("Running the receiver")
    //
    // write is the function that receives data sent to the receiver
    write := func(name string, data []byte) error {
        div := strings.Repeat("##", 40)
        prt(div)
        prt("You should see a 'Hello World!' message below:")
        prt(div)
        prt("Receiver's write()")
        prt("Received name:", name)
        prt("Received data:", string(data))
        prt(div)
        return nil
    }
    // read is the function used to read back the data previously
    // received by the receiver. This data is never sent back to the
    // sender. It is only used to generate a hash that is sent to
    // the sender only to confirm that a data item has been sent.
    read := func(name string) ([]byte, error) {
        prt("Receiver's read()")
        return nil, nil
    }
    udpt.RunReceiver(write, read)
    //
    time.Sleep(1 * time.Second)
    prt("Sending a message")
    udpt.Send("demo_data", []byte("Hello World!"))
    //
    wait := 30 * time.Second
    prt("Waiting", wait, "before exiting")
    time.Sleep(wait)
} //                                                                        main

// prt is like fmt.Println() but prefixes each line with a 'DEMO' tag.
func prt(args ...interface{}) {
    fmt.Println(append([]interface{}{"-------------> DEMO"}, args...)...)
} //                                                                         prt

Version History:

This project is in its DRAFT stage: very unstable. At this point it works, but the API may change rapidly.

Ideas:

  • Write unit tests
  • Create a drop-in replacement for TCP and TLS connections
  • Implement some form of transfer control
  • Improve performance
  • Allow multiple Senders and Receivers that use different Address and Port values.

Documentation

Index

Constants

View Source
const (
	// DATA_ITEM_HASH tag prefixes a UDP packet sent by the
	// sender to request a data item's hash from the receiver.
	// This is needed to check if a data item needs sending.
	DATA_ITEM_HASH = "HASH:"

	// FRAGMENT tag prefixes a UDP packet sent by the sender to the
	// receiver containing a fragment of a data item being sent.
	FRAGMENT = "FRAG:"

	// FRAGMENT_CONFIRMATION tag prefixes a UDP packet sent back by
	// the receiver confirming a FRAGMENT packet sent by the sender.
	FRAGMENT_CONFIRMATION = "CONF:"
)
View Source
const ENilReceiver = "nil receiver"

ENilReceiver indicates a method call on a nil object.

Variables

View Source
var (
	// LogFile specifies the name of the output log file.
	LogFile string

	// LogBufferSize specifies the number of messages buffered in logChan.
	// If you don't specify it, initLog will set it to 1024 by default.
	//
	// To disable log buffering, set it to -1. This can be useful if you want
	// to see log messages in-order with code execution, not after the fact.
	// But this will slow it down while it waits for log messages to be written.
	LogBufferSize int
)
View Source
var Config = ConfigStruct{

	Address: "127.0.0.1",
	Port:    0,
	AESKey:  []byte{},

	PacketSizeLimit:   1450,
	PacketPayloadSize: 1024,

	ReplyTimeout: 15 * time.Second,
	SendRetries:  10,
	WriteTimeout: 15 * time.Second,

	VerboseReceiver: false,
	VerboseSender:   false,

} //                                                                      Config

Config contains global configuration settings.

Functions

func RunReceiver

func RunReceiver(
	writeDataFn func(name string, data []byte) error,
	readDataFn func(name string) ([]byte, error),
)

RunReceiver starts a goroutine that runs the receiver in an infinite loop.

func Send

func Send(name string, data []byte) error

Send sends (transfers) a sequence of bytes ('data') to the Receiver specified by Config.Address and Config.Port.

Types

type ConfigStruct

type ConfigStruct struct {

	// Address is the domain name or IP address of the listening receiver,
	// excluding the port number.
	Address string

	// Port is the port number of the listening server.
	// This number must be between 1 and 65535.
	Port int

	// AESKey the secret AES encryption key that must be shared
	// between the sendder (client) and the receiver (server).
	// This key must be exactly 32 bytes long.
	// This is the key AES uses for symmetric encryption.
	AESKey []byte

	// PacketSizeLimit is the maximum size of a datagram in bytes,
	// including the headers, metadata and data payload.
	//
	// Maximum Transmission Unit (MTU):
	//
	// Internet Protocol requires hosts to process IP datagrams
	// of at least 576 bytes for IPv4 (or 1280 bytes for IPv6).
	// The IPv4 header is 20 bytes (or up to 60 with options).
	// UDP header is 8 bytes. 576 - 60 - 8 = 508 bytes available.
	//
	// The maximum Ethernet (v2) frame size is 1518 bytes, 18
	// of which are overhead, giving a usable size of 1500.
	// (To be on the safe side, we further reduce this by 50 bytes)
	//
	PacketSizeLimit int

	// PacketPayloadSize is the size of a single packet's payload, in bytes.
	// That is the part of the packet that contains actual useful data.
	// PacketPayloadSize must always be smaller that PacketSizeLimit.
	PacketPayloadSize int

	// ReplyTimeout is the maximum time to wait for reply
	// datagram(s) to arrive in a UDP connection.
	ReplyTimeout time.Duration

	// SendRetries is the number of times for
	// Send() to retry sending lost packets.
	SendRetries int

	// WriteTimeout is the maximum time to
	// wait for writing to a UDP connection.
	WriteTimeout time.Duration

	// VerboseReceiver specifies if the receiver should print
	// informational messages to the standard output.
	VerboseReceiver bool

	// VerboseSender specifies if Send() should print
	// informational messages to the standard output.
	VerboseSender bool

} //                                                                ConfigStruct

ConfigStruct contains UDP configuration settings, including the address of the server to which Send() should connect.

func (*ConfigStruct) Validate

func (ob *ConfigStruct) Validate() error

Validate checks the configuration to make sure all required fields like Address, Port and AESKey have been specified and are consistent.

Returns nil if there is no problem, or the error code of the erorr.

type DataItem

type DataItem struct {
	Name             string
	Hash             []byte
	CompressedPieces [][]byte
	UncompressedSize int
	CompressedSize   int

} //                                                                    DataItem

DataItem holds a data item being received by the Receiver. A data item is just a sequence of bytes being transferred. It could be a file, a JSON string or any other resource.

Since we're using UDP, which has a limited packet size, the resource is split into several smaller pieces that are sent as UDP packets.

func (*DataItem) IsLoaded

func (ob *DataItem) IsLoaded() bool

IsLoaded returns true if the current data item has been fully received (all its pieces have been collected).

func (*DataItem) PrintInfo

func (ob *DataItem) PrintInfo(tag string)

PrintInfo prints information on the current data item

func (*DataItem) Reset

func (ob *DataItem) Reset()

Reset discards the contents of the data item and clears its name and hash.

func (*DataItem) Retain

func (ob *DataItem) Retain(name string, hash []byte, packetCount int)

Retain _ _

func (*DataItem) UnpackBytes

func (ob *DataItem) UnpackBytes() ([]byte, error)

UnpackBytes _ _

type Packet

type Packet struct {
	// contains filtered or unexported fields

} //                                                                      Packet

Packet _ _

func NewPacket

func NewPacket(data []byte) (*Packet, error)

NewPacket _ _

func (*Packet) IsDelivered

func (ob *Packet) IsDelivered() bool

IsDelivered returns true if a packet has been successfully delivered (by receiving a successful confirmation packet).

type Receiver

type Receiver struct {
	// contains filtered or unexported fields

} //                                                                    Receiver

Receiver _ _

Directories

Path Synopsis

Jump to

Keyboard shortcuts

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