asyncnet
This module implements a high-level asynchronous sockets API based on the asynchronous dispatcher defined in the asyncdispatch module.
Asynchronous IO in Nim
Async IO in Nim consists of multiple layers (from highest to lowest):
-
asyncnetmodule - Async await
-
asyncdispatchmodule (event loop) -
selectorsmodule
Each builds on top of the layers below it. The selectors module is an abstraction for the various system select() mechanisms such as epoll or kqueue. If you wish you can use it directly, and some people have done so successfully. But you must be aware that on Windows it only supports select().
The async dispatcher implements the proactor pattern and also has an implementation of IOCP. It implements the proactor pattern for other OS' via the selectors module. Futures are also implemented here, and indeed all the procedures return a future.
The final layer is the async await transformation. This allows you to write asynchronous code in a synchronous style and works similar to C#'s await. The transformation works by converting any async procedures into an iterator.
This is all single threaded, fully non-blocking and does give you a lot of control. In theory you should be able to work with any of these layers interchangeably (as long as you only care about non-Windows platforms).
For most applications using asyncnet is the way to go as it builds over all the layers, providing some extra features such as buffering.
SSL ===
SSL can be enabled by compiling with the -d:ssl flag.
You must create a new SSL context with the newContext function defined in the net module. You may then call wrapSocket on your socket using the newly created SSL context to get an SSL socket.
Examples
Chat server
The following example demonstrates a simple chat server.
import asyncnet, asyncdispatch
var clients {.threadvar.}: seq[AsyncSocket]
proc processClient(client: AsyncSocket) {.async.} =
while true:
let line = await client.recvLine()
if line.len == 0: break
for c in clients:
await c.send(line & "\c\L")
proc serve() {.async.} =
clients = @[]
var server = newAsyncSocket()
server.setSockOpt(OptReuseAddr, true)
server.bindAddr(Port(12345))
server.listen()
while true:
let client = await server.accept()
clients.add client
asyncCheck processClient(client)
asyncCheck serve()
runForever() Imports
Types
Procs
proc newAsyncSocket(fd: AsyncFD; domain: Domain = AF_INET; sockType: SockType = SOCK_STREAM; protocol: Protocol = IPPROTO_TCP; buffered = true; inheritable = defined(nimInheritHandles)): owned(AsyncSocket) {...}{. raises: [OSError], tags: [].}-
Creates a new
AsyncSocketbased on the supplied params.The supplied
fd's non-blocking state will be enabled implicitly.If
inheritableis false (the default), the suppliedfdwill not be inheritable by child processes.Note: This procedure will NOT register
Source Editfdwith the global async dispatcher. You need to do this manually. If you have usednewAsyncNativeSocketto createfdthen it's already registered. proc newAsyncSocket(domain: Domain = AF_INET; sockType: SockType = SOCK_STREAM; protocol: Protocol = IPPROTO_TCP; buffered = true; inheritable = defined(nimInheritHandles)): owned(AsyncSocket) {...}{. raises: [OSError, Exception], tags: [RootEffect].}-
Creates a new asynchronous socket.
This procedure will also create a brand new file descriptor for this socket.
If
Source Editinheritableis false (the default), the new file descriptor will not be inheritable by child processes. proc getLocalAddr(socket: AsyncSocket): (string, Port) {...}{. raises: [OSError, Exception], tags: [].}-
Get the socket's local address and port number.
This is high-level interface for getsockname.
Source Edit proc getPeerAddr(socket: AsyncSocket): (string, Port) {...}{. raises: [OSError, Exception], tags: [].}-
Get the socket's peer address and port number.
This is high-level interface for getpeername.
Source Edit proc newAsyncSocket(domain, sockType, protocol: cint; buffered = true; inheritable = defined(nimInheritHandles)): owned(AsyncSocket) {...}{. raises: [OSError, Exception], tags: [RootEffect].}-
Creates a new asynchronous socket.
This procedure will also create a brand new file descriptor for this socket.
If
Source Editinheritableis false (the default), the new file descriptor will not be inheritable by child processes. proc dial(address: string; port: Port; protocol = IPPROTO_TCP; buffered = true): owned( Future[AsyncSocket]) {...}{.raises: [Exception, ValueError], tags: [RootEffect].}- Establishes connection to the specified
address:portpair via the specified protocol. The procedure iterates through possible resolutions of theaddressuntil it succeeds, meaning that it seamlessly works with both IPv4 and IPv6. Returns AsyncSocket ready to send or receive data. Source Edit proc connect(socket: AsyncSocket; address: string; port: Port): owned( Future[void]) {...}{.raises: [Exception], tags: [RootEffect].}-
Connects
socketto server ataddress:port.Returns a
Source EditFuturewhich will complete when the connection succeeds or an error occurs. proc recvInto(socket: AsyncSocket; buf: pointer; size: int; flags = {SafeDisconn}): owned(Future[int]) {...}{. raises: [Exception, ValueError], tags: [RootEffect].}-
Reads up to
sizebytes fromsocketintobuf.For buffered sockets this function will attempt to read all the requested data. It will read this data in
BufferSizechunks.For unbuffered sockets this function makes no effort to read all the data requested. It will return as much data as the operating system gives it.
If socket is disconnected during the recv operation then the future may complete with only a part of the requested data.
If socket is disconnected and no data is available to be read then the future will complete with a value of
Source Edit0. proc recv(socket: AsyncSocket; size: int; flags = {SafeDisconn}): owned( Future[string]) {...}{.raises: [Exception, ValueError], tags: [RootEffect].}-
Reads up to
sizebytes fromsocket.For buffered sockets this function will attempt to read all the requested data. It will read this data in
BufferSizechunks.For unbuffered sockets this function makes no effort to read all the data requested. It will return as much data as the operating system gives it.
If socket is disconnected during the recv operation then the future may complete with only a part of the requested data.
If socket is disconnected and no data is available to be read then the future will complete with a value of
Source Edit"". proc send(socket: AsyncSocket; buf: pointer; size: int; flags = {SafeDisconn}): owned( Future[void]) {...}{.raises: [Exception], tags: [RootEffect].}- Sends
sizebytes frombuftosocket. The returned future will complete once all data has been sent. Source Edit proc send(socket: AsyncSocket; data: string; flags = {SafeDisconn}): owned( Future[void]) {...}{.raises: [Exception], tags: [RootEffect].}- Sends
datatosocket. The returned future will complete once all data has been sent. Source Edit proc acceptAddr(socket: AsyncSocket; flags = {SafeDisconn}; inheritable = defined(nimInheritHandles)): owned( Future[tuple[address: string, client: AsyncSocket]]) {...}{. raises: [Exception, ValueError, OSError], tags: [RootEffect].}-
Accepts a new connection. Returns a future containing the client socket corresponding to that connection and the remote address of the client.
If
inheritableis false (the default), the resulting client socket will not be inheritable by child processes.The future will complete when the connection is successfully accepted.
Source Edit proc accept(socket: AsyncSocket; flags = {SafeDisconn}): owned( Future[AsyncSocket]) {...}{.raises: [Exception, ValueError, OSError], tags: [RootEffect].}- Accepts a new connection. Returns a future containing the client socket corresponding to that connection. If
inheritableis false (the default), the resulting client socket will not be inheritable by child processes. The future will complete when the connection is successfully accepted. Source Edit proc recvLineInto(socket: AsyncSocket; resString: FutureVar[string]; flags = {SafeDisconn}; maxLength = MaxLineLength): owned( Future[void]) {...}{.raises: [ValueError, Exception], tags: [RootEffect].}-
Reads a line of data from
socketintoresString.If a full line is read
\r\Lis not added toline, however if solely\r\Lis read thenlinewill be set to it.If the socket is disconnected,
linewill be set to"".If the socket is disconnected in the middle of a line (before
\r\Lis read) then line will be set to"". The partial line will be lost.The
maxLengthparameter determines the maximum amount of characters that can be read.resStringwill be truncated after that.Warning: The
Peekflag is not yet implemented.Warning:
Source EditrecvLineIntoon unbuffered sockets assumes that the protocol uses\r\Lto delimit a new line. proc recvLine(socket: AsyncSocket; flags = {SafeDisconn}; maxLength = MaxLineLength): owned(Future[string]) {...}{. raises: [Exception, ValueError], tags: [RootEffect].}-
Reads a line of data from
socket. Returned future will complete once a full line is read or an error occurs.If a full line is read
\r\Lis not added toline, however if solely\r\Lis read thenlinewill be set to it.If the socket is disconnected,
linewill be set to"".If the socket is disconnected in the middle of a line (before
\r\Lis read) then line will be set to"". The partial line will be lost.The
maxLengthparameter determines the maximum amount of characters that can be read. The result is truncated after that.Warning: The
Peekflag is not yet implemented.Warning:
Source EditrecvLineon unbuffered sockets assumes that the protocol uses\r\Lto delimit a new line. proc listen(socket: AsyncSocket; backlog = SOMAXCONN) {...}{.tags: [ReadIOEffect], raises: [OSError].}-
Marks
socketas accepting connections.Backlogspecifies the maximum length of the queue of pending connections.Raises an OSError error upon failure.
Source Edit proc bindAddr(socket: AsyncSocket; port = Port(0); address = "") {...}{. tags: [ReadIOEffect], raises: [ValueError, OSError].}-
Binds
address:portto the socket.If
Source Editaddressis "" then ADDR_ANY will be bound. proc connectUnix(socket: AsyncSocket; path: string): owned(Future[void]) {...}{. raises: [], tags: [].}- Binds Unix socket to
path. This only works on Unix-style systems: Mac OS X, BSD and Linux Source Edit proc bindUnix(socket: AsyncSocket; path: string) {...}{.tags: [ReadIOEffect], raises: [].}- Binds Unix socket to
path. This only works on Unix-style systems: Mac OS X, BSD and Linux Source Edit proc close(socket: AsyncSocket) {...}{.raises: [Exception, LibraryError, SslError], tags: [RootEffect].}- Closes the socket. Source Edit
proc wrapSocket(ctx: SslContext; socket: AsyncSocket) {...}{.raises: [SslError], tags: [].}-
Wraps a socket in an SSL context. This function effectively turns
socketinto an SSL socket.Disclaimer: This code is not well tested, may be very unsafe and prone to security vulnerabilities.
Source Edit proc wrapConnectedSocket(ctx: SslContext; socket: AsyncSocket; handshake: SslHandshakeType; hostname: string = "") {...}{. raises: [SslError], tags: [].}-
Wraps a connected socket in an SSL context. This function effectively turns
socketinto an SSL socket.hostnameshould be specified so that the client knows which hostname the server certificate should be validated against.This should be called on a connected socket, and will perform an SSL handshake immediately.
Disclaimer: This code is not well tested, may be very unsafe and prone to security vulnerabilities.
Source Edit proc getPeerCertificates(socket: AsyncSocket): seq[Certificate] {...}{. raises: [Exception], tags: [].}- Returns the certificate chain received by the peer we are connected to through the given socket. The handshake must have been completed and the certificate chain must have been verified successfully or else an empty sequence is returned. The chain is ordered from leaf certificate to root certificate. Source Edit
proc getSockOpt(socket: AsyncSocket; opt: SOBool; level = SOL_SOCKET): bool {...}{. tags: [ReadIOEffect], raises: [OSError].}- Retrieves option
optas a boolean value. Source Edit proc setSockOpt(socket: AsyncSocket; opt: SOBool; value: bool; level = SOL_SOCKET) {...}{.tags: [WriteIOEffect], raises: [OSError].}- Sets option
optto a boolean value specified byvalue. Source Edit proc isSsl(socket: AsyncSocket): bool {...}{.raises: [], tags: [].}- Determines whether
socketis a SSL socket. Source Edit proc getFd(socket: AsyncSocket): SocketHandle {...}{.raises: [], tags: [].}- Returns the socket's file descriptor. Source Edit
proc isClosed(socket: AsyncSocket): bool {...}{.raises: [], tags: [].}- Determines whether the socket has been closed. Source Edit
proc sendTo(socket: AsyncSocket; address: string; port: Port; data: string; flags = {SafeDisconn}): owned(Future[void]) {...}{.raises: [Exception], tags: [RootEffect].}-
This proc sends
datato the specifiedaddress, which may be an IP address or a hostname. If a hostname is specified this function will try each IP of that hostname. The returned future will complete once all data has been sent.If an error occurs an OSError exception will be raised.
This proc is normally used with connectionless sockets (UDP sockets).
Source Edit proc recvFrom(socket: AsyncSocket; data: FutureVar[string]; size: int; address: FutureVar[string]; port: FutureVar[Port]; flags = {SafeDisconn}): owned(Future[int]) {...}{. raises: [ValueError, Exception], tags: [RootEffect].}-
Receives a datagram data from
socketintodata, which must be at least of sizesize. The address and port of datagram's sender will be stored intoaddressandport, respectively. Returned future will complete once one datagram has been received, and will return size of packet received.If an error occurs an OSError exception will be raised.
This proc is normally used with connectionless sockets (UDP sockets).
Notes
-
datamust be initialized to the length ofsize. -
addressmust be initialized to 46 in length.
-
proc recvFrom(socket: AsyncSocket; size: int; flags = {SafeDisconn}): owned( Future[tuple[data: string, address: string, port: Port]]) {...}{. raises: [Exception, ValueError], tags: [RootEffect].}-
Receives a datagram data from
socket, which must be at least of sizesize. Returned future will complete once one datagram has been received and will return tuple with: data of packet received; and address and port of datagram's sender.If an error occurs an OSError exception will be raised.
This proc is normally used with connectionless sockets (UDP sockets).
Source Edit
Exports
© 2006–2021 Andreas Rumpf
Licensed under the MIT License.
https://nim-lang.org/docs/asyncnet.html