mirror of
https://github.com/yggdrasil-network/yggdrasil-go.git
synced 2025-04-29 06:35:07 +03:00
Merge pull request #528 from yggdrasil-network/documentation
Documentation updates
This commit is contained in:
commit
d27891aaf6
10 changed files with 472 additions and 115 deletions
|
@ -16,29 +16,37 @@ import (
|
|||
)
|
||||
|
||||
// Peer represents a single peer object. This contains information from the
|
||||
// preferred switch port for this peer, although there may be more than one in
|
||||
// reality.
|
||||
// preferred switch port for this peer, although there may be more than one
|
||||
// active switch port connection to the peer in reality.
|
||||
//
|
||||
// This struct is informational only - you cannot manipulate peer connections
|
||||
// using instances of this struct. You should use the AddPeer or RemovePeer
|
||||
// functions instead.
|
||||
type Peer struct {
|
||||
PublicKey crypto.BoxPubKey
|
||||
Endpoint string
|
||||
BytesSent uint64
|
||||
BytesRecvd uint64
|
||||
Protocol string
|
||||
Port uint64
|
||||
Uptime time.Duration
|
||||
PublicKey crypto.BoxPubKey // The public key of the remote node
|
||||
Endpoint string // The connection string used to connect to the peer
|
||||
BytesSent uint64 // Number of bytes sent to this peer
|
||||
BytesRecvd uint64 // Number of bytes received from this peer
|
||||
Protocol string // The transport protocol that this peer is connected with, typically "tcp"
|
||||
Port uint64 // Switch port number for this peer connection
|
||||
Uptime time.Duration // How long this peering has been active for
|
||||
}
|
||||
|
||||
// SwitchPeer represents a switch connection to a peer. Note that there may be
|
||||
// multiple switch peers per actual peer, e.g. if there are multiple connections
|
||||
// to a given node.
|
||||
//
|
||||
// This struct is informational only - you cannot manipulate switch peer
|
||||
// connections using instances of this struct. You should use the AddPeer or
|
||||
// RemovePeer functions instead.
|
||||
type SwitchPeer struct {
|
||||
PublicKey crypto.BoxPubKey
|
||||
Coords []uint64
|
||||
BytesSent uint64
|
||||
BytesRecvd uint64
|
||||
Port uint64
|
||||
Protocol string
|
||||
Endpoint string
|
||||
PublicKey crypto.BoxPubKey // The public key of the remote node
|
||||
Coords []uint64 // The coordinates of the remote node
|
||||
BytesSent uint64 // Number of bytes sent via this switch port
|
||||
BytesRecvd uint64 // Number of bytes received via this switch port
|
||||
Port uint64 // Switch port number for this switch peer
|
||||
Protocol string // The transport protocol that this switch port is connected with, typically "tcp"
|
||||
Endpoint string // The connection string used to connect to the switch peer
|
||||
}
|
||||
|
||||
// DHTEntry represents a single DHT entry that has been learned or cached from
|
||||
|
@ -64,32 +72,36 @@ type NodeInfoPayload []byte
|
|||
// congestion and a list of switch queues created in response to congestion on a
|
||||
// given link.
|
||||
type SwitchQueues struct {
|
||||
Queues []SwitchQueue
|
||||
Count uint64
|
||||
Size uint64
|
||||
HighestCount uint64
|
||||
HighestSize uint64
|
||||
MaximumSize uint64
|
||||
Queues []SwitchQueue // An array of SwitchQueue objects containing information about individual queues
|
||||
Count uint64 // The current number of active switch queues
|
||||
Size uint64 // The current total size of active switch queues
|
||||
HighestCount uint64 // The highest recorded number of switch queues so far
|
||||
HighestSize uint64 // The highest recorded total size of switch queues so far
|
||||
MaximumSize uint64 // The maximum allowed total size of switch queues, as specified by config
|
||||
}
|
||||
|
||||
// SwitchQueue represents a single switch queue, which is created in response
|
||||
// to congestion on a given link.
|
||||
// SwitchQueue represents a single switch queue. Switch queues are only created
|
||||
// in response to congestion on a given link and represent how much data has
|
||||
// been temporarily cached for sending once the congestion has cleared.
|
||||
type SwitchQueue struct {
|
||||
ID string
|
||||
Size uint64
|
||||
Packets uint64
|
||||
Port uint64
|
||||
ID string // The ID of the switch queue
|
||||
Size uint64 // The total size, in bytes, of the queue
|
||||
Packets uint64 // The number of packets in the queue
|
||||
Port uint64 // The switch port to which the queue applies
|
||||
}
|
||||
|
||||
// Session represents an open session with another node.
|
||||
// Session represents an open session with another node. Sessions are opened in
|
||||
// response to traffic being exchanged between two nodes using Conn objects.
|
||||
// Note that sessions will automatically be closed by Yggdrasil if no traffic is
|
||||
// exchanged for around two minutes.
|
||||
type Session struct {
|
||||
PublicKey crypto.BoxPubKey
|
||||
Coords []uint64
|
||||
BytesSent uint64
|
||||
BytesRecvd uint64
|
||||
MTU uint16
|
||||
Uptime time.Duration
|
||||
WasMTUFixed bool
|
||||
PublicKey crypto.BoxPubKey // The public key of the remote node
|
||||
Coords []uint64 // The coordinates of the remote node
|
||||
BytesSent uint64 // Bytes sent to the session
|
||||
BytesRecvd uint64 // Bytes received from the session
|
||||
MTU uint16 // The maximum supported message size of the session
|
||||
Uptime time.Duration // How long this session has been active for
|
||||
WasMTUFixed bool // This field is no longer used
|
||||
}
|
||||
|
||||
// GetPeers returns one or more Peer objects containing information about active
|
||||
|
@ -236,7 +248,10 @@ func (c *Core) GetSessions() []Session {
|
|||
return sessions
|
||||
}
|
||||
|
||||
// ConnListen returns a listener for Yggdrasil session connections.
|
||||
// ConnListen returns a listener for Yggdrasil session connections. You can only
|
||||
// call this function once as each Yggdrasil node can only have a single
|
||||
// ConnListener. Make sure to keep the reference to this for as long as it is
|
||||
// needed.
|
||||
func (c *Core) ConnListen() (*Listener, error) {
|
||||
c.router.sessions.listenerMutex.Lock()
|
||||
defer c.router.sessions.listenerMutex.Unlock()
|
||||
|
@ -251,7 +266,10 @@ func (c *Core) ConnListen() (*Listener, error) {
|
|||
return c.router.sessions.listener, nil
|
||||
}
|
||||
|
||||
// ConnDialer returns a dialer for Yggdrasil session connections.
|
||||
// ConnDialer returns a dialer for Yggdrasil session connections. Since
|
||||
// ConnDialers are stateless, you can request as many dialers as you like,
|
||||
// although ideally you should request only one and keep the reference to it for
|
||||
// as long as it is needed.
|
||||
func (c *Core) ConnDialer() (*Dialer, error) {
|
||||
return &Dialer{
|
||||
core: c,
|
||||
|
@ -265,48 +283,69 @@ func (c *Core) ListenTCP(uri string) (*TcpListener, error) {
|
|||
return c.link.tcp.listen(uri)
|
||||
}
|
||||
|
||||
// NodeID gets the node ID.
|
||||
// NodeID gets the node ID. This is derived from your router encryption keys.
|
||||
// Remote nodes wanting to open connections to your node will need to know your
|
||||
// node ID.
|
||||
func (c *Core) NodeID() *crypto.NodeID {
|
||||
return crypto.GetNodeID(&c.boxPub)
|
||||
}
|
||||
|
||||
// TreeID gets the tree ID.
|
||||
// TreeID gets the tree ID. This is derived from your switch signing keys. There
|
||||
// is typically no need to share this key.
|
||||
func (c *Core) TreeID() *crypto.TreeID {
|
||||
return crypto.GetTreeID(&c.sigPub)
|
||||
}
|
||||
|
||||
// SigningPublicKey gets the node's signing public key.
|
||||
// SigningPublicKey gets the node's signing public key, as used by the switch.
|
||||
func (c *Core) SigningPublicKey() string {
|
||||
return hex.EncodeToString(c.sigPub[:])
|
||||
}
|
||||
|
||||
// EncryptionPublicKey gets the node's encryption public key.
|
||||
// EncryptionPublicKey gets the node's encryption public key, as used by the
|
||||
// router.
|
||||
func (c *Core) EncryptionPublicKey() string {
|
||||
return hex.EncodeToString(c.boxPub[:])
|
||||
}
|
||||
|
||||
// Coords returns the current coordinates of the node.
|
||||
// Coords returns the current coordinates of the node. Note that these can
|
||||
// change at any time for a number of reasons, not limited to but including
|
||||
// changes to peerings (either yours or a parent nodes) or changes to the network
|
||||
// root.
|
||||
//
|
||||
// This function may return an empty array - this is normal behaviour if either
|
||||
// you are the root of the network that you are connected to, or you are not
|
||||
// connected to any other nodes (effectively making you the root of a
|
||||
// single-node network).
|
||||
func (c *Core) Coords() []uint64 {
|
||||
table := c.switchTable.table.Load().(lookupTable)
|
||||
return wire_coordsBytestoUint64s(table.self.getCoords())
|
||||
}
|
||||
|
||||
// Address gets the IPv6 address of the Yggdrasil node. This is always a /128
|
||||
// address.
|
||||
// address. The IPv6 address is only relevant when the node is operating as an
|
||||
// IP router and often is meaningless when embedded into an application, unless
|
||||
// that application also implements either VPN functionality or deals with IP
|
||||
// packets specifically.
|
||||
func (c *Core) Address() net.IP {
|
||||
address := net.IP(address.AddrForNodeID(c.NodeID())[:])
|
||||
return address
|
||||
}
|
||||
|
||||
// Subnet gets the routed IPv6 subnet of the Yggdrasil node. This is always a
|
||||
// /64 subnet.
|
||||
// /64 subnet. The IPv6 subnet is only relevant when the node is operating as an
|
||||
// IP router and often is meaningless when embedded into an application, unless
|
||||
// that application also implements either VPN functionality or deals with IP
|
||||
// packets specifically.
|
||||
func (c *Core) Subnet() net.IPNet {
|
||||
subnet := address.SubnetForNodeID(c.NodeID())[:]
|
||||
subnet = append(subnet, 0, 0, 0, 0, 0, 0, 0, 0)
|
||||
return net.IPNet{IP: subnet, Mask: net.CIDRMask(64, 128)}
|
||||
}
|
||||
|
||||
// MyNodeInfo gets the currently configured nodeinfo.
|
||||
// MyNodeInfo gets the currently configured nodeinfo. NodeInfo is typically
|
||||
// specified through the "NodeInfo" option in the node configuration or using
|
||||
// the SetNodeInfo function, although it may also contain other built-in values
|
||||
// such as "buildname", "buildversion" etc.
|
||||
func (c *Core) MyNodeInfo() NodeInfoPayload {
|
||||
return c.router.nodeinfo.getNodeInfo()
|
||||
}
|
||||
|
@ -356,7 +395,9 @@ func (c *Core) SetSessionGatekeeper(f func(pubkey *crypto.BoxPubKey, initiator b
|
|||
}
|
||||
|
||||
// SetLogger sets the output logger of the Yggdrasil node after startup. This
|
||||
// may be useful if you want to redirect the output later.
|
||||
// may be useful if you want to redirect the output later. Note that this
|
||||
// expects a Logger from the github.com/gologme/log package and not from Go's
|
||||
// built-in log package.
|
||||
func (c *Core) SetLogger(log *log.Logger) {
|
||||
c.log = log
|
||||
}
|
||||
|
@ -427,12 +468,17 @@ func (c *Core) DisconnectPeer(port uint64) error {
|
|||
}
|
||||
|
||||
// GetAllowedEncryptionPublicKeys returns the public keys permitted for incoming
|
||||
// peer connections.
|
||||
// peer connections. If this list is empty then all incoming peer connections
|
||||
// are accepted by default.
|
||||
func (c *Core) GetAllowedEncryptionPublicKeys() []string {
|
||||
return c.peers.getAllowedEncryptionPublicKeys()
|
||||
}
|
||||
|
||||
// AddAllowedEncryptionPublicKey whitelists a key for incoming peer connections.
|
||||
// By default all incoming peer connections are accepted, but adding public keys
|
||||
// to the whitelist using this function enables strict checking from that point
|
||||
// forward. Once the whitelist is enabled, only peer connections from
|
||||
// whitelisted public keys will be accepted.
|
||||
func (c *Core) AddAllowedEncryptionPublicKey(bstr string) (err error) {
|
||||
c.peers.addAllowedEncryptionPublicKey(bstr)
|
||||
return nil
|
||||
|
|
|
@ -53,6 +53,9 @@ func (e *ConnError) Closed() bool {
|
|||
return e.closed
|
||||
}
|
||||
|
||||
// The Conn struct is a reference to an active connection session between the
|
||||
// local node and a remote node. Conn implements the io.ReadWriteCloser
|
||||
// interface and is used to send and receive traffic with a remote node.
|
||||
type Conn struct {
|
||||
phony.Inbox
|
||||
core *Core
|
||||
|
@ -78,6 +81,11 @@ func newConn(core *Core, nodeID *crypto.NodeID, nodeMask *crypto.NodeID, session
|
|||
return &conn
|
||||
}
|
||||
|
||||
// String returns a string that uniquely identifies a connection. Currently this
|
||||
// takes a form similar to "conn=0x0000000", which contains a memory reference
|
||||
// to the Conn object. While this value should always be unique for each Conn
|
||||
// object, the format of this is not strictly defined and may change in the
|
||||
// future.
|
||||
func (c *Conn) String() string {
|
||||
var s string
|
||||
phony.Block(c, func() { s = fmt.Sprintf("conn=%p", c) })
|
||||
|
@ -162,7 +170,12 @@ func (c *Conn) _getDeadlineCancellation(t *time.Time) (util.Cancellation, bool)
|
|||
}
|
||||
}
|
||||
|
||||
// SetReadCallback sets a callback which will be called whenever a packet is received.
|
||||
// SetReadCallback allows you to specify a function that will be called whenever
|
||||
// a packet is received. This should be used if you wish to implement
|
||||
// asynchronous patterns for receiving data from the remote node.
|
||||
//
|
||||
// Note that if a read callback has been supplied, you should no longer attempt
|
||||
// to use the synchronous Read function.
|
||||
func (c *Conn) SetReadCallback(callback func([]byte)) {
|
||||
c.Act(nil, func() {
|
||||
c.readCallback = callback
|
||||
|
@ -217,7 +230,14 @@ func (c *Conn) readNoCopy() ([]byte, error) {
|
|||
}
|
||||
}
|
||||
|
||||
// Implements net.Conn.Read
|
||||
// Read allows you to read from the connection in a synchronous fashion. The
|
||||
// function will block up until the point that either new data is available, the
|
||||
// connection has been closed or the read deadline has been reached. If the
|
||||
// function succeeds, the number of bytes read from the connection will be
|
||||
// returned. Otherwise, an error condition will be returned.
|
||||
//
|
||||
// Note that you can also implement asynchronous reads by using SetReadCallback.
|
||||
// If you do that, you should no longer attempt to use the Read function.
|
||||
func (c *Conn) Read(b []byte) (int, error) {
|
||||
bs, err := c.readNoCopy()
|
||||
if err != nil {
|
||||
|
@ -259,9 +279,9 @@ func (c *Conn) _write(msg FlowKeyMessage) error {
|
|||
return nil
|
||||
}
|
||||
|
||||
// WriteFrom should be called by a phony.Actor, and tells the Conn to send a message.
|
||||
// This is used internaly by Write.
|
||||
// If the callback is called with a non-nil value, then it is safe to reuse the argument FlowKeyMessage.
|
||||
// WriteFrom should be called by a phony.Actor, and tells the Conn to send a
|
||||
// message. This is used internaly by Write. If the callback is called with a
|
||||
// non-nil value, then it is safe to reuse the argument FlowKeyMessage.
|
||||
func (c *Conn) WriteFrom(from phony.Actor, msg FlowKeyMessage, callback func(error)) {
|
||||
c.Act(from, func() {
|
||||
callback(c._write(msg))
|
||||
|
@ -291,7 +311,11 @@ func (c *Conn) writeNoCopy(msg FlowKeyMessage) error {
|
|||
return err
|
||||
}
|
||||
|
||||
// Write implement the Write function of a net.Conn, and makes use of WriteNoCopy under the hood.
|
||||
// Write allows you to write to the connection in a synchronous fashion. This
|
||||
// function may block until either the write has completed, the connection has
|
||||
// been closed or the write deadline has been reached. If the function succeeds,
|
||||
// the number of written bytes is returned. Otherwise, an error condition is
|
||||
// returned.
|
||||
func (c *Conn) Write(b []byte) (int, error) {
|
||||
written := len(b)
|
||||
msg := FlowKeyMessage{Message: append(util.GetBytes(), b...)}
|
||||
|
@ -303,6 +327,10 @@ func (c *Conn) Write(b []byte) (int, error) {
|
|||
return written, err
|
||||
}
|
||||
|
||||
// Close will close an open connection and any blocking operations on the
|
||||
// connection will unblock and return. From this point forward, the connection
|
||||
// can no longer be used and you should no longer attempt to Read or Write to
|
||||
// the connection.
|
||||
func (c *Conn) Close() (err error) {
|
||||
phony.Block(c, func() {
|
||||
if c.session != nil {
|
||||
|
@ -317,10 +345,13 @@ func (c *Conn) Close() (err error) {
|
|||
return
|
||||
}
|
||||
|
||||
// LocalAddr returns the complete node ID of the local side of the connection.
|
||||
// This is always going to return your own node's node ID.
|
||||
func (c *Conn) LocalAddr() crypto.NodeID {
|
||||
return *crypto.GetNodeID(&c.core.boxPub)
|
||||
}
|
||||
|
||||
// RemoteAddr returns the complete node ID of the remote side of the connection.
|
||||
func (c *Conn) RemoteAddr() crypto.NodeID {
|
||||
// TODO warn that this can block while waiting for the Conn actor to run, so don't call it from other actors...
|
||||
var n crypto.NodeID
|
||||
|
@ -328,18 +359,32 @@ func (c *Conn) RemoteAddr() crypto.NodeID {
|
|||
return n
|
||||
}
|
||||
|
||||
// SetDeadline is equivalent to calling both SetReadDeadline and
|
||||
// SetWriteDeadline with the same value, configuring the maximum amount of time
|
||||
// that synchronous Read and Write operations can block for. If no deadline is
|
||||
// configured, Read and Write operations can potentially block indefinitely.
|
||||
func (c *Conn) SetDeadline(t time.Time) error {
|
||||
c.SetReadDeadline(t)
|
||||
c.SetWriteDeadline(t)
|
||||
return nil
|
||||
}
|
||||
|
||||
// SetReadDeadline configures the maximum amount of time that a synchronous Read
|
||||
// operation can block for. A Read operation will unblock at the point that the
|
||||
// read deadline is reached if no other condition (such as data arrival or
|
||||
// connection closure) happens first. If no deadline is configured, Read
|
||||
// operations can potentially block indefinitely.
|
||||
func (c *Conn) SetReadDeadline(t time.Time) error {
|
||||
// TODO warn that this can block while waiting for the Conn actor to run, so don't call it from other actors...
|
||||
phony.Block(c, func() { c.readDeadline = &t })
|
||||
return nil
|
||||
}
|
||||
|
||||
// SetWriteDeadline configures the maximum amount of time that a synchronous
|
||||
// Write operation can block for. A Write operation will unblock at the point
|
||||
// that the read deadline is reached if no other condition (such as data sending
|
||||
// or connection closure) happens first. If no deadline is configured, Write
|
||||
// operations can potentially block indefinitely.
|
||||
func (c *Conn) SetWriteDeadline(t time.Time) error {
|
||||
// TODO warn that this can block while waiting for the Conn actor to run, so don't call it from other actors...
|
||||
phony.Block(c, func() { c.writeDeadline = &t })
|
||||
|
|
176
src/yggdrasil/doc.go
Normal file
176
src/yggdrasil/doc.go
Normal file
|
@ -0,0 +1,176 @@
|
|||
/*
|
||||
Package yggdrasil implements the core functionality of the Yggdrasil Network.
|
||||
|
||||
Introduction
|
||||
|
||||
Yggdrasil is a proof-of-concept mesh network which provides end-to-end encrypted
|
||||
communication between nodes in a decentralised fashion. The network is arranged
|
||||
using a globally-agreed spanning tree which provides each node with a locator
|
||||
(coordinates relative to the root) and a distributed hash table (DHT) mechanism
|
||||
for finding other nodes.
|
||||
|
||||
Each node also implements a router, which is responsible for encryption of
|
||||
traffic, searches and connections, and a switch, which is responsible ultimately
|
||||
for forwarding traffic across the network.
|
||||
|
||||
While many Yggdrasil nodes in existence today are IP nodes - that is, they are
|
||||
transporting IPv6 packets, like a kind of mesh VPN - it is also possible to
|
||||
integrate Yggdrasil into your own applications and use it as a generic data
|
||||
transport, similar to UDP.
|
||||
|
||||
This library is what you need to integrate and use Yggdrasil in your own
|
||||
application.
|
||||
|
||||
Basics
|
||||
|
||||
In order to start an Yggdrasil node, you should start by generating node
|
||||
configuration, which amongst other things, includes encryption keypairs which
|
||||
are used to generate the node's identity, and supply a logger which Yggdrasil's
|
||||
output will be written to.
|
||||
|
||||
This may look something like this:
|
||||
|
||||
import (
|
||||
"os"
|
||||
"github.com/gologme/log"
|
||||
"github.com/yggdrasil-network/yggdrasil-go/src/config"
|
||||
"github.com/yggdrasil-network/yggdrasil-go/src/yggdrasil"
|
||||
)
|
||||
|
||||
type node struct {
|
||||
core yggdrasil.Core
|
||||
config *config.NodeConfig
|
||||
log *log.Logger
|
||||
}
|
||||
|
||||
You then can supply node configuration and a logger:
|
||||
|
||||
n := node{}
|
||||
n.log = log.New(os.Stdout, "", log.Flags())
|
||||
n.config = config.GenerateConfig()
|
||||
|
||||
In the above example, we ask the config package to supply new configuration each
|
||||
time, which results in fresh encryption keys and therefore a new identity. It is
|
||||
normally preferable in most cases to persist node configuration onto the
|
||||
filesystem or into some configuration store so that the node's identity does not
|
||||
change each time that the program starts. Note that Yggdrasil will automatically
|
||||
fill in any missing configuration items with sane defaults.
|
||||
|
||||
Once you have supplied a logger and some node configuration, you can then start
|
||||
the node:
|
||||
|
||||
n.core.Start(n.config, n.log)
|
||||
|
||||
Add some peers to connect to the network:
|
||||
|
||||
n.core.AddPeer("tcp://some-host.net:54321", "")
|
||||
n.core.AddPeer("tcp://[2001::1:2:3]:54321", "")
|
||||
n.core.AddPeer("tcp://1.2.3.4:54321", "")
|
||||
|
||||
You can also ask the API for information about our node:
|
||||
|
||||
n.log.Println("My node ID is", n.core.NodeID())
|
||||
n.log.Println("My public key is", n.core.EncryptionPublicKey())
|
||||
n.log.Println("My coords are", n.core.Coords())
|
||||
|
||||
Incoming Connections
|
||||
|
||||
Once your node is started, you can then listen for connections from other nodes
|
||||
by asking the API for a Listener:
|
||||
|
||||
listener, err := n.core.ConnListen()
|
||||
if err != nil {
|
||||
// ...
|
||||
}
|
||||
|
||||
The Listener has a blocking Accept function which will wait for incoming
|
||||
connections from remote nodes. It will return a Conn when a connection is
|
||||
received. If the node never receives any incoming connections then this function
|
||||
can block forever, so be prepared for that, perhaps by listening in a separate
|
||||
goroutine.
|
||||
|
||||
Assuming that you have defined a myConnectionHandler function to deal with
|
||||
incoming connections:
|
||||
|
||||
for {
|
||||
conn, err := listener.Accept()
|
||||
if err != nil {
|
||||
// ...
|
||||
}
|
||||
|
||||
// We've got a new connection
|
||||
go myConnectionHandler(conn)
|
||||
}
|
||||
|
||||
Outgoing Connections
|
||||
|
||||
If you know the node ID of the remote node that you want to talk to, you can
|
||||
dial an outbound connection to it. To do this, you should first ask the API for
|
||||
a Dialer:
|
||||
|
||||
dialer, err := n.core.ConnDialer()
|
||||
if err != nil {
|
||||
// ...
|
||||
}
|
||||
|
||||
You can then dial using the 16-byte node ID in hexadecimal format, for example:
|
||||
|
||||
conn, err := dialer.Dial("nodeid", "24a58cfce691ec016b0f698f7be1bee983cea263781017e99ad3ef62b4ef710a45d6c1a072c5ce46131bd574b78818c9957042cafeeed13966f349e94eb771bf")
|
||||
if err != nil {
|
||||
// ...
|
||||
}
|
||||
|
||||
Using Connections
|
||||
|
||||
Conn objects are implementations of io.ReadWriteCloser, and as such, you can
|
||||
Read, Write and Close them as necessary.
|
||||
|
||||
Each Read or Write operation can deal with a buffer with a maximum size of 65535
|
||||
bytes - any bigger than this and the operation will return an error.
|
||||
|
||||
For example, to write to the Conn from the supplied buffer:
|
||||
|
||||
buf := []byte{1, 2, 3, 4, 5}
|
||||
w, err := conn.Write(buf)
|
||||
if err != nil {
|
||||
// ...
|
||||
} else {
|
||||
// written w bytes
|
||||
}
|
||||
|
||||
Reading from the Conn into the supplied buffer:
|
||||
|
||||
buf := make([]byte, 65535)
|
||||
r, err := conn.Read(buf)
|
||||
if err != nil {
|
||||
// ...
|
||||
} else {
|
||||
// read r bytes
|
||||
}
|
||||
|
||||
When you are happy that a connection is no longer required, you can discard it:
|
||||
|
||||
err := conn.Close()
|
||||
if err != nil {
|
||||
// ...
|
||||
}
|
||||
|
||||
Limitations
|
||||
|
||||
You should be aware of the following limitations when working with the Yggdrasil
|
||||
library:
|
||||
|
||||
Individual messages written through Yggdrasil connections can not exceed 65535
|
||||
bytes in size. Yggdrasil has no concept of fragmentation, so if you try to send
|
||||
a message that exceeds 65535 bytes in size, it will be dropped altogether and
|
||||
an error will be returned.
|
||||
|
||||
Yggdrasil connections are unreliable by nature. Messages are delivered on a
|
||||
best-effort basis, and employs congestion control where appropriate to ensure
|
||||
that congestion does not affect message transport, but Yggdrasil will not
|
||||
retransmit any messages that have been lost. If reliable delivery is important
|
||||
then you should manually implement acknowledgement and retransmission of
|
||||
messages.
|
||||
|
||||
*/
|
||||
package yggdrasil
|
Loading…
Add table
Add a link
Reference in a new issue