Skip to content
This repository has been archived by the owner on Jun 9, 2023. It is now read-only.

Latest commit

 

History

History
321 lines (280 loc) · 30.9 KB

hs.socket.udp.md

File metadata and controls

321 lines (280 loc) · 30.9 KB

docs » hs.socket.udp


Talk to custom protocols using asynchronous UDP sockets

For TCP sockets see hs.socket

You can do a lot of neat trivial and non-trivial things with these. A simple ping ponger:

function ping(data, addr)
  print(data)
  addr = hs.socket.parseAddress(addr)
  hs.timer.doAfter(1, function()
    client:send("ping", addr.host, addr.port)
  end)
end

function pong(data, addr)
  print(data)
  addr = hs.socket.parseAddress(addr)
  hs.timer.doAfter(1, function()
    server:send("pong", addr.host, addr.port)
  end)
end

server = hs.socket.udp.server(9001, pong):receive()
client = hs.socket.udp.new(ping):send("ping", "localhost", 9001):receive()

Resulting in the following endless exchange:

20:26:56    LuaSkin: (secondary thread): Data written to UDP socket
            LuaSkin: (secondary thread): Data read from UDP socket
ping
20:26:57    LuaSkin: (secondary thread): Data written to UDP socket
            LuaSkin: (secondary thread): Data read from UDP socket
pong
20:26:58    LuaSkin: (secondary thread): Data written to UDP socket
            LuaSkin: (secondary thread): Data read from UDP socket
ping
20:26:59    LuaSkin: (secondary thread): Data written to UDP socket
            LuaSkin: (secondary thread): Data read from UDP socket
pong
...

You can do some silly things with a callback factory and enabling broadcasting:

local function callbackMaker(name)
  local fun = function(data, addr)
    addr = hs.socket.parseAddress(addr)
    print(name.." received data:\n"..data.."\nfrom host: "..addr.host.." port: "..addr.port)
  end
  return fun
end

local listeners = {}
local port = 9001

for i=1,3 do
  table.insert(listeners, hs.socket.udp.new(callbackMaker("listener "..i)):reusePort():listen(port):receive())
end

broadcaster = hs.socket.udp.new():broadcast()
broadcaster:send("hello!", "255.255.255.255", port)

Since neither IPv4 nor IPv6 have been disabled, the broadcast is received on both protocols ('dual-stack' IPv6 addresses shown):

listener 2 received data:
hello!
from host: ::ffff:192.168.0.3 port: 53057
listener 1 received data:
hello!
from host: ::ffff:192.168.0.3 port: 53057
listener 3 received data:
hello!
from host: ::ffff:192.168.0.3 port: 53057
listener 1 received data:
hello!
from host: 192.168.0.3 port: 53057
listener 3 received data:
hello!
from host: 192.168.0.3 port: 53057
listener 2 received data:
hello!
from host: 192.168.0.3 port: 53057

API Overview

API Documentation

Variables

Signature hs.socket.udp.timeout
Type Variable
Description Timeout for the socket operations, in seconds. New hs.socket.udp objects will be created with this timeout value, but can individually change it with the setTimeout method

Functions

Signature hs.socket.udp.parseAddress(sockaddr) -> table or nil
Type Function
Description Alias for hs.socket.parseAddress

Constructors

Signature hs.socket.udp.new([fn]) -> hs.socket.udp object
Type Constructor
Description Creates an unconnected asynchronous UDP socket object
Parameters
  • fn - An optional callback function for reading data from the socket, settable here for convenience
Returns
Signature hs.socket.udp.server(port[, fn]) -> hs.socket.udp object
Type Constructor
Description Creates and binds an hs.socket.udp instance to a port for listening
Parameters
  • port - A port number [0-65535]. Ports [1-1023] are privileged. Port 0 allows the OS to select any available port
  • fn - An optional callback function for reading data from the socket, settable here for convenience
Returns

Methods

Signature hs.socket.udp:broadcast([flag]) -> self or nil
Type Method
Description Enables broadcasting on the underlying socket
Parameters
  • flag - An optional boolean: true to enable broadcasting, false to disable it. Defaults to true
Returns
Notes
  • By default, the underlying socket in the OS will not allow you to send broadcast messages
  • In order to send broadcast messages, you need to enable this functionality in the socket
  • A broadcast is a UDP message to addresses like "192.168.255.255" or "255.255.255.255" that is delivered to every host on the network.
  • The reason this is generally disabled by default (by the OS) is to prevent accidental broadcast messages from flooding the network.
Signature hs.socket.udp:close() -> self
Type Method
Description Immediately closes the underlying socket, freeing the hs.socket.udp instance for reuse. Any pending send operations are discarded
Parameters
  • None
Returns
Signature hs.socket.udp:closed() -> bool
Type Method
Description Returns the closed status of the hs.socket.udp instance
Parameters
  • None
Returns
  • true if closed, otherwise false
Notes
  • UDP sockets are typically meant to be connectionless
  • Sending a packet anywhere, regardless of whether or not the destination receives it, opens the socket until it is explicitly closed
  • An active listening socket will not be closed, but will not be 'connected' unless the connect method has been called
Signature hs.socket.udp:connect(host, port[, fn]) -> self or nil
Type Method
Description Connects an unconnected hs.socket.udp instance
Parameters
  • host - A string containing the hostname or IP address
  • port - A port number [1-65535]
  • fn - An optional single-use callback function to execute after establishing the connection. Receives no parameters
Returns
Notes
  • By design, UDP is a connectionless protocol, and connecting is not needed
  • Choosing to connect to a specific host/port has the following effect:
  • You will only be able to send data to the connected host/port
  • You will only be able to receive data from the connected host/port
  • You will receive ICMP messages that come from the connected host/port, such as "connection refused"
  • The actual process of connecting a UDP socket does not result in any communication on the socket. It simply changes the internal state of the socket
  • You cannot bind a socket after it has been connected
  • You can only connect a socket once
Signature hs.socket.udp:connected() -> bool
Type Method
Description Returns the connection status of the hs.socket.udp instance
Parameters
  • None
Returns
  • true if connected, otherwise false
Notes
  • UDP sockets are typically meant to be connectionless
  • This method will only return true if the connect method has been explicitly called
Signature hs.socket.udp:enableIPv(version[, flag]) -> self or nil
Type Method
Description Enables or disables IPv4 or IPv6 on the underlying socket. By default, both are enabled
Parameters
  • version - A number containing the IP version (4 or 6) to enable or disable
  • flag - A boolean: true to enable the chosen IP version, false to disable it. Defaults to true
Returns
Notes
  • Must be called before binding the socket. If you want to create an IPv6-only server, do something like:
  • hs.socket.udp.new(callback):enableIPv(4, false):listen(port):receive()
  • The convenience constructor hs.socket.server will automatically bind the socket and requires closing and relistening to use this method
Signature hs.socket.udp:info() -> table
Type Method
Description Returns information on the hs.socket.udp instance
Parameters
  • None
Returns
  • A table containing the following keys:
  • connectedAddress - string (sockaddr struct)
  • connectedHost - string
  • connectedPort - number
  • isClosed - boolean
  • isConnected - boolean
  • isIPv4 - boolean
  • isIPv4Enabled - boolean
  • isIPv4Preferred - boolean
  • isIPv6 - boolean
  • isIPv6Enabled - boolean
  • isIPv6Preferred - boolean
  • isIPVersionNeutral - boolean
  • localAddress - string (sockaddr struct)
  • localAddress_IPv4 - string (sockaddr struct)
  • localAddress_IPv6 - string (sockaddr struct)
  • localHost - string
  • localHost_IPv4 - string
  • localHost_IPv6 - string
  • localPort - number
  • localPort_IPv4 - number
  • localPort_IPv6 - number
  • maxReceiveIPv4BufferSize - number
  • maxReceiveIPv6BufferSize - number
  • timeout - number
  • userData - string
Signature hs.socket.udp:listen(port) -> self or nil
Type Method
Description Binds an unconnected hs.socket.udp instance to a port for listening
Parameters
  • port - A port number [0-65535]. Ports [1-1023] are privileged. Port 0 allows the OS to select any available port
Returns
Signature hs.socket.udp:pause() -> self
Type Method
Description Suspends reading of packets from the socket. Call one of the receive methods to resume
Parameters
  • None
Returns
Signature hs.socket.udp:preferIPv([version]) -> self
Type Method
Description Sets the preferred IP version: IPv4, IPv6, or neutral (first to resolve)
Parameters
  • version - An optional number containing the IP version to prefer. Anything but 4 or 6 else sets the default neutral behavior
Returns
Notes
  • If a DNS lookup returns only IPv4 results, the socket will automatically use IPv4
  • If a DNS lookup returns only IPv6 results, the socket will automatically use IPv6
  • If a DNS lookup returns both IPv4 and IPv6 results, then the protocol used depends on the configured preference
Signature hs.socket.udp:read(delimiter[, tag]) -> self
Type Method
Description Alias for hs.socket.udp:receive
Signature hs.socket.udp:readOne(delimiter[, tag]) -> self
Type Method
Description Alias for hs.socket.udp:receiveOne
Signature hs.socket.udp:receive([fn]) -> self or nil
Type Method
Description Reads packets from the socket as they arrive. Results are passed to the callback function, which must be set to use this method
Parameters
Returns
Notes
  • There are two modes of operation for receiving packets: one-at-a-time & continuous
  • In one-at-a-time mode, you call receiveOne every time you are ready process an incoming UDP packet
  • Receiving packets one-at-a-time may be better suited for implementing certain state machine code where your state machine may not always be ready to process incoming packets
  • In continuous mode, the callback is invoked immediately every time incoming udp packets are received
  • Receiving packets continuously is better suited to real-time streaming applications
  • You may switch back and forth between one-at-a-time mode and continuous mode
  • If the socket is currently in one-at-a-time mode, calling this method will switch it to continuous mode
Signature hs.socket.udp:receiveOne([fn]) -> self or nil
Type Method
Description Reads a single packet from the socket. Results are passed to the callback function, which must be set to use this method
Parameters
Returns
Notes
  • There are two modes of operation for receiving packets: one-at-a-time & continuous
  • In one-at-a-time mode, you call receiveOne every time you are ready process an incoming UDP packet
  • Receiving packets one-at-a-time may be better suited for implementing certain state machine code where your state machine may not always be ready to process incoming packets
  • In continuous mode, the callback is invoked immediately every time incoming udp packets are received
  • Receiving packets continuously is better suited to real-time streaming applications
  • You may switch back and forth between one-at-a-time mode and continuous mode
  • If the socket is currently in continuous mode, calling this method will switch it to one-at-a-time mode
Signature hs.socket.udp:reusePort([flag]) -> self or nil
Type Method
Description Enables port reuse on the underlying socket
Parameters
  • flag - An optional boolean: true to enable port reuse, false to disable it. Defaults to true
Returns
Notes
  • By default, only one socket can be bound to a given IP address+port at a time
  • To enable multiple processes to simultaneously bind to the same address+port, you need to enable this functionality in the socket
  • All processes that wish to use the address+port simultaneously must all enable reuse port on the socket bound to that port
  • Must be called before binding the socket
Signature hs.socket.udp:send(message, host, port[, tag][, fn]) -> self
Type Method
Description Sends a packet to the destination address
Parameters
  • message - A string containing data to be sent on the socket
  • host - A string containing the hostname or IP address
  • port - A port number [1-65535]
  • tag - An optional integer to assist with labeling writes
  • fn - An optional single-use callback function to execute after sending the packet. Receives the tag parameter
Returns
Notes
  • For non-connected sockets, the remote destination is specified for each packet
  • If the socket has been explicitly connected with connect, only the message parameter and an optional tag and/or write callback can be supplied
  • Recall that connecting is optional for a UDP socket
  • For connected sockets, data can only be sent to the connected address
Signature hs.socket.udp:setBufferSize(size[, version]) -> self
Type Method
Description Sets the maximum size of the buffer that will be allocated for receive operations
Parameters
  • size - An number containing the receive buffer size in bytes
  • version - An optional number containing the IP version for which to set the buffer size. Anything but 4 or 6 else sets the same size for both
Returns
Notes
  • The default maximum size is 9216 bytes
  • The theoretical maximum size of any IPv4 UDP packet is UINT16_MAX = 65535
  • The theoretical maximum size of any IPv6 UDP packet is UINT32_MAX = 4294967295
  • Since the OS notifies us of the size of each received UDP packet, the actual allocated buffer size for each packet is exact
  • In practice the size of UDP packets is generally much smaller than the max. Most protocols will send and receive packets of only a few bytes, or will set a limit on the size of packets to prevent fragmentation in the IP layer.
  • If you set the buffer size too small, the sockets API in the OS will silently discard any extra data
Signature hs.socket.udp:setCallback([fn]) -> self
Type Method
Description Sets the read callback for the hs.socket.udp instance. Must be set to read data from the socket
Parameters
  • fn - An optional callback function to process data read from the socket. nil or no argument clears the callback
Returns
Signature hs.socket.udp:setTimeout(timeout) -> self
Type Method
Description Sets the timeout for the socket operations. If the timeout value is negative, the operations will not use a timeout, which is the default
Parameters
  • timeout - A number containing the timeout duration, in seconds
Returns
Signature hs.socket.udp:write(message[, tag]) -> self
Type Method
Description Alias for hs.socket.udp:send