Module asyncio

Warning: This module is deprecated since version 0.10.2. Use the brand new asyncdispatch module together with the asyncnet module.

This module implements an asynchronous event loop together with asynchronous sockets which use this event loop. It is akin to Python's asyncore module. Many modules that use sockets have an implementation for this module, those modules should all have a register function which you should use to add the desired objects to a dispatcher which you created so that you can receive the events associated with that module's object.

Once everything is registered in a dispatcher, you need to call the poll function in a while loop.

Note: Most modules have tasks which need to be ran regularly, this is why you should not call poll with a infinite timeout, or even a very long one. In most cases the default timeout is fine.

Note: This module currently only supports select(), this is limited by FD_SETSIZE, which is usually 1024. So you may only be able to use 1024 sockets at a time.

Most (if not all) modules that use asyncio provide a userArg which is passed on with the events. The type that you set userArg to must be inheriting from RootObj!

Note: If you want to provide async ability to your module please do not use the Delegate object, instead use AsyncSocket. It is possible that in the future this type's fields will not be exported therefore breaking your code.

Warning: The API of this module is unstable, and therefore is subject to change.

Asynchronous sockets

For most purposes you do not need to worry about the Delegate type. The AsyncSocket is what you are after. It's a reference to the AsyncSocketObj object. This object defines events which you should overwrite by your own procedures.

For server sockets the only event you need to worry about is the handleAccept event, in your handleAccept proc you should call accept on the server socket which will give you the client which is connecting. You should then set any events that you want to use on that client and add it to your dispatcher using the register procedure.

An example handleAccept follows:

var disp = newDispatcher()
...
proc handleAccept(s: AsyncSocket) =
  echo("Accepted client.")
  var client: AsyncSocket
  new(client)
  s.accept(client)
  client.handleRead = ...
  disp.register(client)
...

For client sockets you should only be interested in the handleRead and handleConnect events. The former gets called whenever the socket has received messages and can be read from and the latter gets called whenever the socket has established a connection to a server socket; from that point it can be safely written to.

Getting a blocking client from an AsyncSocket

If you need a asynchronous server socket but you wish to process the clients synchronously then you can use the getSocket converter to get a Socket from the AsyncSocket object, this can then be combined with accept like so:

proc handleAccept(s: AsyncSocket) =
  var client: Socket
  getSocket(s).accept(client)

Types

DelegateObj = object
  fd*: SocketHandle
  deleVal*: RootRef
  handleRead*: proc (h: RootRef) {.
nimcall, gcsafe
.} handleWrite*: proc (h: RootRef) {.
nimcall, gcsafe
.} handleError*: proc (h: RootRef) {.
nimcall, gcsafe
.} hasDataBuffered*: proc (h: RootRef): bool {.
nimcall, gcsafe
.} open*: bool task*: proc (h: RootRef) {.
nimcall, gcsafe
.} mode*: FileMode
  Source Edit
Delegate = ref DelegateObj
  Source Edit
Dispatcher = ref DispatcherObj
  Source Edit
AsyncSocket = ref AsyncSocketObj
  Source Edit
AsyncSocketObj = object of RootObj
  socket: Socket
  info: SocketStatus
  handleRead*: proc (s: AsyncSocket) {.
closure, gcsafe
.} handleWrite: proc (s: AsyncSocket) {.
closure, gcsafe
.} handleConnect*: proc (s: AsyncSocket) {.
closure, gcsafe
.} handleAccept*: proc (s: AsyncSocket) {.
closure, gcsafe
.} handleTask*: proc (s: AsyncSocket) {.
closure, gcsafe
.} lineBuffer: TaintedString sendBuffer: string ## Temporary storage for ``send`` sslNeedAccept: bool proto: Protocol deleg: Delegate
Temporary storage for readLine   Source Edit
SocketStatus = enum
  SockIdle, SockConnecting, SockConnected, SockListening, SockClosed, SockUDPBound
  Source Edit

Procs

proc newDelegate(): Delegate {.
raises: [], tags: []
.}
Creates a new delegate.   Source Edit
proc asyncSocket(domain: Domain = AF_INET; typ: SockType = SOCK_STREAM;
                protocol: Protocol = IPPROTO_TCP; buffered = true): AsyncSocket {.
raises: [OSError], tags: []
.}
Initialises an AsyncSocket object. If a socket cannot be initialised EOS is raised.   Source Edit
proc toAsyncSocket(sock: Socket; state: SocketStatus = SockConnected): AsyncSocket {.
raises: [OSError], tags: []
.}

Wraps an already initialized Socket into a AsyncSocket. This is useful if you want to use an already connected Socket as an asynchronous AsyncSocket in asyncio's event loop.

state may be overriden, i.e. if sock is not connected it should be adjusted properly. By default it will be assumed that the socket is connected. Please note this is only applicable to TCP client sockets, if sock is a different type of socket state needs to be adjusted!!!

ValueMeaning
SockIdleSocket has only just been initialised, not connected or closed.
SockConnectedSocket is connected to a server.
SockConnectingSocket is in the process of connecting to a server.
SockListeningSocket is a server socket and is listening for connections.
SockClosedSocket has been closed.
SockUDPBoundSocket is a UDP socket which is listening for data.

Warning: If state is set incorrectly the resulting AsyncSocket object may not work properly.

Note: This will set sock to be non-blocking.

  Source Edit
proc connect(sock: AsyncSocket; name: string; port = Port(0); af: Domain = AF_INET) {.
raises: [OSError], tags: [ReadIOEffect]
.}
Begins connecting sock to name:port.   Source Edit
proc close(sock: AsyncSocket) {.
gcsafe, raises: [], tags: []
.}
Closes sock. Terminates any current connections.   Source Edit
proc bindAddr(sock: AsyncSocket; port = Port(0); address = "") {.
raises: [OSError], tags: [ReadIOEffect]
.}
Equivalent to sockets.bindAddr.   Source Edit
proc listen(sock: AsyncSocket) {.
raises: [OSError], tags: [ReadIOEffect]
.}
Equivalent to sockets.listen.   Source Edit
proc acceptAddr(server: AsyncSocket; client: var AsyncSocket; address: var string) {.
raises: [OSError], tags: [ReadIOEffect]
.}

Equivalent to sockets.acceptAddr. This procedure should be called in a handleAccept event handler only once.

Note: client needs to be initialised.

  Source Edit
proc accept(server: AsyncSocket; client: var AsyncSocket) {.
raises: [OSError], tags: [ReadIOEffect]
.}
Equivalent to sockets.accept.   Source Edit
proc acceptAddr(server: AsyncSocket): tuple[sock: AsyncSocket, address: string] {.
deprecated, raises: [OSError], tags: [ReadIOEffect]
.}

Equivalent to sockets.acceptAddr.

Deprecated since version 0.9.0: Please use the function above.

  Source Edit
proc accept(server: AsyncSocket): AsyncSocket {.
deprecated, raises: [OSError], tags: [ReadIOEffect]
.}

Equivalent to sockets.accept.

Deprecated since version 0.9.0: Please use the function above.

  Source Edit
proc newDispatcher(): Dispatcher {.
raises: [], tags: []
.}
  Source Edit
proc register(d: Dispatcher; deleg: Delegate) {.
raises: [], tags: []
.}
Registers delegate deleg with dispatcher d.   Source Edit
proc register(d: Dispatcher; sock: AsyncSocket): Delegate {.
discardable, raises: [], tags: []
.}
Registers async socket sock with dispatcher d.   Source Edit
proc unregister(d: Dispatcher; deleg: Delegate) {.
raises: [IndexError], tags: []
.}
Unregisters deleg deleg from dispatcher d.   Source Edit
proc isWriteable(s: AsyncSocket): bool {.
raises: [], tags: [ReadIOEffect]
.}
Determines whether socket s is ready to be written to.   Source Edit
proc isConnected(s: AsyncSocket): bool {.
raises: [], tags: []
.}
Determines whether s is connected.   Source Edit
proc isListening(s: AsyncSocket): bool {.
raises: [], tags: []
.}
Determines whether s is listening for incoming connections.   Source Edit
proc isConnecting(s: AsyncSocket): bool {.
raises: [], tags: []
.}
Determines whether s is connecting.   Source Edit
proc isClosed(s: AsyncSocket): bool {.
raises: [], tags: []
.}
Determines whether s has been closed.   Source Edit
proc isSendDataBuffered(s: AsyncSocket): bool {.
raises: [], tags: []
.}
Determines whether s has data waiting to be sent, i.e. whether this socket's sendBuffer contains data.   Source Edit
proc setHandleWrite(s: AsyncSocket;
                   handleWrite: proc (s: AsyncSocket) {.
closure, gcsafe
.}) {.
raises: [], tags: []
.}

Setter for the handleWrite event.

To remove this event you should use the delHandleWrite function. It is advised to use that function instead of just setting the event to proc (s: AsyncSocket) = nil as that would mean that that function would be called constantly.

  Source Edit
proc delHandleWrite(s: AsyncSocket) {.
raises: [], tags: []
.}
Removes the handleWrite event handler on s.   Source Edit
proc recvLine(s: AsyncSocket; line: var TaintedString): bool {.
deprecated, raises: [OSError], tags: [ReadIOEffect]
.}

Behaves similar to sockets.recvLine, however it handles non-blocking sockets properly. This function guarantees that line is a full line, if this function can only retrieve some data; it will save this data and add it to the result when a full line is retrieved.

Unlike sockets.recvLine this function will raise an EOS or ESSL exception if an error occurs.

Deprecated since version 0.9.2: This function has been deprecated in favour of readLine.

  Source Edit
proc readLine(s: AsyncSocket; line: var TaintedString): bool {.
raises: [OSError], tags: [ReadIOEffect]
.}

Behaves similar to sockets.readLine, however it handles non-blocking sockets properly. This function guarantees that line is a full line, if this function can only retrieve some data; it will save this data and add it to the result when a full line is retrieved, when this happens False will be returned. True will only be returned if a full line has been retrieved or the socket has been disconnected in which case line will be set to "".

This function will raise an EOS exception when a socket error occurs.

  Source Edit
proc send(sock: AsyncSocket; data: string) {.
raises: [OSError], tags: [WriteIOEffect]
.}

Sends data to socket sock. This is basically a nicer implementation of sockets.sendAsync.

If data cannot be sent immediately it will be buffered and sent when sock becomes writeable (during the handleWrite event). It's possible that only a part of data will be sent immediately, while the rest of it will be buffered and sent later.

  Source Edit
proc poll(d: Dispatcher; timeout: int = 500): bool {.
raises: [Exception], tags: [RootEffect]
.}

This function checks for events on all the delegates in the PDispatcher. It then proceeds to call the correct event handler.

This function returns True if there are file descriptors that are still open, otherwise False. File descriptors that have been closed are immediately removed from the dispatcher automatically.

Note: Each delegate has a task associated with it. This gets called after each select() call, if you set timeout to -1 the tasks will only be executed after one or more file descriptors becomes readable or writeable.

  Source Edit
proc len(disp: Dispatcher): int {.
raises: [], tags: []
.}
Retrieves the amount of delegates in disp.   Source Edit

Converters

converter getSocket(s: AsyncSocket): Socket {.
raises: [], tags: []
.}
  Source Edit