6.93. kink/socket/TCP

Provides stream based connecting sockets.

Example

The following program spawns a TCP/IP server and a client. The client sets socket options TCP_NODELAY, SO_SNDBUF, and SO_LINGER, then sends bytes [1, 2, 3] to the server.

:TCP.require_from('kink/socket/')
:TCP_SERVER.require_from('kink/socket/')
:IP.require_from('kink/socket/')
:IP_PORT.require_from('kink/socket/')
:THREAD.require_from('kink/thread/')
:BIN.require_from('kink/')
:CONTROL.require_from('kink/')

:spawn_client <- {(:Server_addr)
  THREAD.spawn_io{
    CONTROL.with_finally{(:finally)
      :Tcp = TCP.open
      finally{ Tcp.close }

      # initial socket options
      stdout.print_line(Tcp.tcp_nodelay.load.repr) # => false
      stdout.print_line(Tcp.so_sndbuf.load.repr) # => 8192 (initial values vary for systems)
      stdout.print_line(Tcp.so_linger.load.repr) # => []

      # set socket options
      Tcp.tcp_nodelay <- true
      Tcp.so_sndbuf <- 1024
      Tcp.so_linger <- [10]
      stdout.print_line(Tcp.tcp_nodelay.load.repr) # => true
      stdout.print_line(Tcp.so_sndbuf.load.repr) # => 2304 (the value was adjusted)
      stdout.print_line(Tcp.so_linger.load.repr) # => [10]

      Tcp.connect(Server_addr)
      :Out = Tcp.output
      finally{ Out.close }

      Out.write(BIN.of(1))
      Out.write(BIN.of(2))
      Out.write(BIN.of(3))
    }
  }
}

CONTROL.with_finally{(:finally)
  :Server = TCP_SERVER.open
  finally{ Server.close }

  Server.bind_listen(IP_PORT.new(IP.for_hostname('::1') 0))
  :Server_addr = Server.local_address
  spawn_client(Server_addr)

  :Tcp = Server.accept
  finally{ Tcp.close }
  :In = Tcp.input
  finally{ In.close }

  :Bin = In.read_all
  stdout.print_line(Bin.repr) # => (bin 0x01 0x02 0x03)
}

6.93.1. type tcp

`tcp` is a stream-based connecting socket. The protocol can be either TCP/IP, or stream based Unix domain socket.

6.93.1.1. Tcp.connect(Remote ...[$config={}])

`connect` tries to connect this socket to the remote address specified by `Remote`.

Config methods:

• C.on_success($success): default = {}

• C.on_error($error): default = {(:Exc) Exc.raise }

• C.on_closed($closed): default = a thunk which tail-calls $error

`connect` waits until connection is established, the socket is closed, or an IO error occurs.

If connection is established, `connect` tail-calls $success with no arg. The client can read data from, or write data to the socket.

If the connecting socket is closed, before or during invocation of `connect`, `connect` tail-calls $closed with no arg.

If an IO error occurs, `connect` closes the connecting socket and tail-calls $error with an `exception`.

Timeout

Connection timeout can be implemented using C.on_closed and Tcp.close_unless_connected.

:TCP.require_from('kink/socket/')
:IP.require_from('kink/socket/')
:IP_PORT.require_from('kink/socket/')
:THREAD.require_from('kink/thread/')
:CONTROL.require_from('kink/')
:PROCESS.require_from('kink/')

CONTROL.with_finally{(:finally)
  :Tcp = TCP.open
  finally{ Tcp.close }

  # a blackhole address. see RFC 6666
  :Blackhole = IP_PORT.new(IP.for_hostname('0100::1234') 8080)

  # times out connection after 10 seconds
  THREAD.spawn_io{
    THREAD.sleep(10)
    Tcp.close_unless_connected
  }

  # exits the process when connection times out
  Tcp.connect(Blackhole){(:C)
    C.on_closed{
      stderr.print_line('connection time out')
      PROCESS.exit(76)
    }
  }
}
# => connection time out

Note that the runtime or the operating system might also time out connection. In that case, an IO error might occur, rather than tail-calling $closed.

Preconditions

`Remote` must be a `socket_address`. It must match th protocol family of the socket.

$success must be a thunk.

$error must be a function which takes an `exception`.

$closed must be a thunk.

6.93.1.2. Tcp.bind(Local ...[$config])

`bind` binds a local address specified by `Local` to the socket.

Config methods:

• C.on_success($success): default = {}

• C.on_error($error): default = {(:Exc) Exc.raise }

If the address is successfully bound, `bind` tail-calls $success with no arg.

If an IO error occurs, `bind` tail-calls $error with an `exception`.

Preconditions

`Local` must be a `socket_address`, whose protocol family matches one of the socket.

$success must be a thunk.

$error must be a function which takes an `exception`.

6.93.1.3. Tcp.close(...[$config={}])

`close` closes the connecting socket.

Config methods:

• C.on_success($success): default = {}

• C.on_error($error): default = {(:Exc) Exc.raise }

If the socket is closed successfully, `close` tail-calls $success with no arg.

If an IO error occurs, `close` tail-calls $error with an `exception`.

Preconditions

$success must be a thunk.

$error must be a function which takes an `exception`.

6.93.1.4. Tcp.close_unless_connected(...[$config={}])

`close_unless_connected` closes the connecting socket if and only if the socket is not yet connected. If the socket is connected, or already closed, `close_unless_connected` does nothing.

Config methods:

• C.on_success($success): default = {}

• C.on_error($error): default = {(:Exc) Exc.raise }

If the socket is connected, or already closed, `close_unless_connected` tail-calls $success with no arg.

If the socket is not yet connected, `close_unless_connected` closes the socket. If it is successful, `close_unless_connected` tail-calls $success with no arg. If an IO error occurs, `close_unless_connected` tail-calls $error with an `exception`.

Preconditions

$success must be a thunk.

$error must be a function which takes an `exception`.

6.93.1.5. Tcp.input(...[$config={}])

`input` method makes an `input` of the connecting socket.

Config method:

• C.buffer(...[Max_buf_size])

If C.buffer is called, the result `input` will perform userspace buffering. If `Max_buf_size` is given, the buffer size will not exceed `Max_buf_size`. If C.buffer is not called, the result `input` will not perform userspace buffering.

Closing the `input` will shut down the input stream of the socket.

Precondition

`Max_buf_size` must be a positive integer `num`.

6.93.1.6. Tcp.output(...[$config={}])

`output` method makes an `output` of the connecting socket.

Config method:

• C.buffer(...[Max_buf_size])

If C.buffer is called, the result `output` will perform userspace buffering. If `Max_buf_size` is given, the buffer size will not exceed `Max_buf_size`. If C.buffer is not called, the result `output` will not perform userspace buffering.

Closing the output will shut down the output stream of the socket.

Precondition

`Max_buf_size` must be a positive integer `num`.

6.93.1.7. Tcp.local_address(...[$config={}])

`local_address` retrieves a `socket_address` of the local address of the connecting socket.

Config methods:

• C.on_success($success): default = VAL.identity

• C.on_error($error): default = {(:Exc) Exc.raise }

If the local address is available, `local_address` tail-calls $success with the `socket_address`.

If an IO error occurs, `local_address` tail-calls $error with an `exception`.

Preconditions

$success must be a function which takes a `socket_address`.

$error must be a function which takes an `exception`.

6.93.1.8. Tcp.remote_address(...[$config={}])

`remote_address` retrieves a `socket_address` of the remote address of the connecting socket.

Config methods:

• C.on_success($success): default = VAL.identity

• C.on_error($error): default = {(:Exc) Exc.raise }

If the remote address is available, `remote_address` tail-calls $success with the `socket_address`.

If an IO error occurs, `remote_address` tail-calls $error with an `exception`.

Preconditions

$success must be a function which takes a `socket_address`.

$error must be a function which takes an `exception`.

6.93.1.9. Tcp.tcp_nodelay

`tcp_nodelay` returns a `ref` of a `bool` of TCP_NODELAY option.

For usage of socket options, see the example program at the beginning of this module.

Precondition

The protocol family must be ipv4 or ipv6.

6.93.1.10. Tcp.so_keepalive

`so_keepalive` returns a `ref` of a `bool` of SO_KEEPALIVE option.

For usage of socket options, see the example program at the beginning of this module.

Precondition

The protocol family must be ipv4 or ipv6.

6.93.1.11. Tcp.so_reuseaddr

`so_reuseaddr` returns a `ref` of a `bool` of SO_REUSEADDR option.

For usage of socket options, see the example program at the beginning of this module.

Precondition

The protocol family must be ipv4 or ipv6.

6.93.1.12. Tcp.so_linger

`so_linger` returns an optional non-negative integer `num` of SO_LINGER option.

The value of the `ref` is either an empty `vec`, or a singleton `vec` [l_linger], where `l_linger` is a non-negative integer `num`. When the value is an empty `vec`, SO_LINGER option is turned off. When the value is a singleton `vec`, SO_LINGER option is turned on, with `l_linger` as the timeout seconds.

For usage of socket options, see the example program at the beginning of this module.

Precondition

The protocol family must be ipv4 or ipv6.

6.93.1.13. Tcp.so_sndbuf

`so_sndbuf` returns a `ref` of a non-negative integer `num` of SO_SNDBUF option.

For usage of socket options, see the example program at the beginning of this module.

6.93.1.14. Tcp.so_rcvbuf

`so_rcvbuf` returns a `ref` of a non-negative integer `num` of SO_RCVBUF option.

For usage of socket options, see the example program at the beginning of this module.

6.93.2. TCP.open(...[Protocol_family=PROTOCOL_FAMILY.ipv6 $config={}])

`open` makes a new connecting socket.

Config methods:

• C.on_success($success): default = VAL.identity

• C.on_error($error): default = {(:Exc) Exc.raise }

• C.on_unsupported($unsupported): default = a function which tail-calls $error

If a socket is created, `open` tail-calls $success with a `tcp`.

If the specified protocol family is not supported by the runtime, `open` tail-calls $unsupported with no arg.

If an IO error occurs, `open` tail-calls $error with an `exception`.

Preconditions

Protocol_family must be a `protocol_family`.

$success must be a function which takes a `tcp`.

$error must be a function which takes an `exception`.

$unsupported must be a thunk.

6.93.3. TCP.is?(Val)

`is?` returns whether `Val` is a `tcp`.