BSD sockets are the standard way to do socket networking on nearly all modern platforms. Tinlok
provides a cross-platform wrapper over BSD sockets (the POSIX socket implementation on Linux, or
WinSock2 on Windows), for both blocking and non-blocking sockets, via the
Sockets are low-level and Unsafe as they hold a reference to a managed resource (the actual socket).
Sockets can be created easily with the helper functions on the
val tcp = Socket.tcp(AddressFamily.AF_INET6) val udp = Socket.udp(AddressFamily.AF_INET6)
Socket options can be set on a socket with the
setsockopt method and retrieved with the
sock.setsockopt(StandardSocketOptions.SO_REUSEADDR, true) val opt = sock.getsockopt(StandardSocketOptions.SO_KEEPALIVE)
Sockets can operate in both blocking and non-blocking mode, controlled by the
property on the socket object.
Do NOT set the socket to non-blocking with an
ioctlsocket call on the
underlying handle. Internally, implementations track the non-blocking status with a boolean
value, as the status cannot be simply retreived on some implementations.
Due to this, sockets return a
BlockingResult inline class in various locations, which wraps
either the result of a call, or
-1 to signify that the socket needs to be polled on until
the operation can complete. In blocking mode, this result will never be -1.
Client sockets are connected using
connect with a
ConnectionInfo object of the right type.
Despite the naming,
SocketAddress is not the object to use here.
For blocking sockets, this will either throw an exception or return true to indicate successful connection. For non-blocking connection, this will return a boolean for if the socket has connected immediately, or if it needs to be polled upon.
Server sockets are bound using
bind with a
ConnectionInfo, and then set into listen mode.
sock.bind(TcpConnectionInfo(...)) sock.listen(backlog = 16)
Server sockets accept with the
This method is unsafe because failing to close the socket will leak a file descriptor.
val client: Socket<I>? = sock.accept()
For blocking sockets, this will return the accepted client socket. For non-blocking sockets, this will either return the accepted client socket, or null if no client is available yet and the socket needs to be polled on.
recv method is used to receive data on connection-oriented sockets. This works for both
ByteArray objects and
val ba = ByteArray(1024) val count = sock.recv(ba, ba.size, 0, 0).ensureNonBlocking()
recvfrom method is used to receive data on datagram-oriented sockets. It is similar to
recv, but instead of returning the count alone, it returns a
RecvFrom object which wraps
BlockingResult and the
ConnectionInfo remote address data was received from.
Sending data has three forms:
sendcall, which does ONE attempt at sending data.
sendallcall, which will RETRY until all data is sent, or the socket blocks.
sendtocall, which does ONE attempt at sending data to the specified address.