Socket.Tcp

A TCP connection or listener is made from an IP address and a port, and this module makes them. Once made, it is an ordinary Socket.Connection or Socket.Listener, used through Socket like a connection or listener of any other kind.

Addresses are Socket.Address.Address values, IPv4 or IPv6, not text, so a host name has to be resolved before anything connects to it. connect takes an address; connectToHost takes text, either an address literal, used as it is, or a host name, which it resolves with Socket.lookup first. A server listens on an address and gets its connections from Socket.accept or Socket.onConnection, by the rule the Socket module describes.

What this module offers for a connection that already exists is two TCP options: Nagle's algorithm, which holds back small writes to send them together, and keep-alive probes, which the operating system sends on an idle connection to find out whether the peer is still there. Both can be chosen when connecting and changed later with setNoDelay and setKeepAlive.

Times are given in milliseconds, as Maybe Int, and the two fields of ConnectOptions that take one read a non-positive value differently. A connect timeout of Just 0 or less is no timeout, the same as Nothing. A keepAlive of Just 0 or less turns keep-alive on, at 1 second; only Nothing leaves it off.

Socket.Tls takes the same ConnectOptions and ListenOptions for the TCP side of its connections and listeners.

import Socket
import Socket.Address as Address exposing (Family(..))
import Socket.Tcp


listener =
    Socket.Tcp.listen (Socket.Tcp.defaultListenOptions (Address.loopback IPv4) 8080)

Connecting

type alias ConnectOptions =
    { address : Address
    , port_ : Int
    , timeout : Maybe Int
    , noDelay : Bool
    , keepAlive : Maybe Int
    }

One attempt to open a TCP connection: where to connect, how long to wait, and the TCP options the connection starts with.

  • timeout, up to 2147483647 (about 24.8 days), is the most time, in milliseconds, to wait until the connection is established (and, for Socket.Tls.connect, until the TLS handshake is done as well); the attempt then fails with Socket.TimedOut. Nothing, or Just n with n <= 0, waits as long as the operating system does.
  • noDelay = True turns Nagle's algorithm off (TCP_NODELAY), so small writes are sent at once.
  • keepAlive is how long, in milliseconds, the connection must be idle before keep-alive probes are sent, within any limit the operating system sets (32767 seconds on Linux). It is rounded up to whole seconds, at least 1 second, so Just 0 or a negative value sends probes after 1 second. Nothing sends none.

Setting noDelay and keepAlive here is best effort: if the operating system refuses one, the connection is still made.

defaultConnectOptions : Address -> Int -> ConnectOptions

Returns options for connecting to address on port_ with no timeout, Nagle's algorithm on (noDelay = False) and no keep-alive.

connect : ConnectOptions -> Task Error Connection

Opens a TCP connection, succeeding once the connection is established.

Socket.Tcp.connect (Socket.Tcp.defaultConnectOptions (Address.loopback IPv4) 8080)

Fails with, for example, Socket.ConnectionRefused when nothing listens there, Socket.TimedOut when the timeout has passed, or Socket.NetworkUnreachable when no route leads to the address.

connectToHost : String -> Int -> Task Error Connection

Opens a TCP connection to a host given by name, or by an address literal, and a port, with the default options.

Text that Socket.Address.fromString reads as an address, such as "127.0.0.1" or "::1", is used as it is. Anything else is resolved with Socket.lookup, and a failed lookup fails the task with its error. The addresses found are tried one after the other, in the order lookup returns them, until one connects. If every attempt fails, the task fails with the last attempt's error; a name without addresses fails with Socket.HostNotFound.

Socket.Tcp.connectToHost "localhost" 8080

The default options have no timeout, so each attempt waits as long as the operating system does before the next address is tried. For a time limit on each attempt, resolve the name with Socket.lookup and call connect with a timeout.

Listening

type alias ListenOptions =
    { address : Address, port_ : Int, backlog : Int, ipv6Only : Bool }

Where a TCP listener listens, which connections it accepts, and how it queues the ones it has not handed out yet.

  • port_ 0 lets the operating system pick a free port, which Socket.listenerEndpoint then reports. The unspecified address (Socket.Address.any) listens on every interface.
  • backlog is the length of the operating system's queue of connections waiting to be accepted, clamped to between 1 and 65535 (the operating system may limit it further). On the JavaScript backend it is also how many connections the listener holds for a later accept, as the Socket module describes.
  • ipv6Only matters only for an IPv6 address. When False, a listener on any IPv6 also accepts IPv4 connections, whose addresses are then IPv4-mapped (see Socket.Address.unmapIPv4). When True, it accepts IPv6 connections only.

On the native backend the listening socket is made with SO_REUSEADDR, so the port can be listened on again as soon as an earlier listener on it has closed.

defaultListenOptions : Address -> Int -> ListenOptions

Returns options for listening on address and port_ with a backlog of 511 and ipv6Only = False.

listen : ListenOptions -> Task Error Listener

Starts listening for TCP connections, succeeding with the listener once it is bound and listening. Fails with, for example, Socket.AddressInUse when the address and port are taken, Socket.PermissionDenied for a privileged port, or Socket.AddressNotAvailable for an address that does not belong to this machine.

Connection options

setNoDelay : Bool -> Connection -> Task Error ()

Turns Nagle's algorithm off for the connection (True, so small writes are sent at once) or on (False). On a Unix domain connection, or one that has already been closed, this succeeds and does nothing.

setKeepAlive : Maybe Int -> Connection -> Task Error ()

Starts sending keep-alive probes once the connection has been idle for the given number of milliseconds, within any limit the operating system sets (32767 seconds on Linux), or stops sending them (Nothing). The time is rounded up to whole seconds, at least 1 second, so Just 0 or a negative value turns probes on at 1 second rather than off.

On a Unix domain connection, or one that has already been closed, this succeeds and does nothing.