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
  client.handleRead = ...

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


sockets, os, winlean


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
Delegate = ref DelegateObj
Dispatcher = ref DispatcherObj
AsyncSocket = ref AsyncSocketObj
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
SocketStatus = enum
  SockIdle, SockConnecting, SockConnected, SockListening, SockClosed, SockUDPBound


proc newDelegate(): Delegate {.raises: [], tags: [].}
Creates a new delegate.
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.
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!!!

Value Meaning
SockIdle Socket has only just been initialised, not connected or closed.
SockConnected Socket is connected to a server.
SockConnecting Socket is in the process of connecting to a server.
SockListening Socket is a server socket and is listening for connections.
SockClosed Socket has been closed.
SockUDPBound Socket 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.

proc connect(sock: AsyncSocket; name: string; port = Port(0); af: Domain = AF_INET) {.
    raises: [OSError], tags: [ReadIOEffect].}
Begins connecting sock to name:port.
proc close(sock: AsyncSocket) {.gcsafe, raises: [], tags: [].}
Closes sock. Terminates any current connections.
proc bindAddr(sock: AsyncSocket; port = Port(0); address = "") {.raises: [OSError],
    tags: [ReadIOEffect].}
Equivalent to sockets.bindAddr.
proc listen(sock: AsyncSocket) {.raises: [OSError], tags: [ReadIOEffect].}
Equivalent to sockets.listen.
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.

proc accept(server: AsyncSocket; client: var AsyncSocket) {.raises: [OSError],
    tags: [ReadIOEffect].}
Equivalent to sockets.accept.
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.

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.

proc newDispatcher(): Dispatcher {.raises: [], tags: [].}
proc register(d: Dispatcher; deleg: Delegate) {.raises: [], tags: [].}
Registers delegate deleg with dispatcher d.
proc register(d: Dispatcher; sock: AsyncSocket): Delegate {.discardable, raises: [],
    tags: [].}
Registers async socket sock with dispatcher d.
proc unregister(d: Dispatcher; deleg: Delegate) {.raises: [IndexError], tags: [].}
Unregisters deleg deleg from dispatcher d.
proc isWriteable(s: AsyncSocket): bool {.raises: [], tags: [ReadIOEffect].}
Determines whether socket s is ready to be written to.
proc isConnected(s: AsyncSocket): bool {.raises: [], tags: [].}
Determines whether s is connected.
proc isListening(s: AsyncSocket): bool {.raises: [], tags: [].}
Determines whether s is listening for incoming connections.
proc isConnecting(s: AsyncSocket): bool {.raises: [], tags: [].}
Determines whether s is connecting.
proc isClosed(s: AsyncSocket): bool {.raises: [], tags: [].}
Determines whether s has been closed.
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.
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.

proc delHandleWrite(s: AsyncSocket) {.raises: [], tags: [].}
Removes the handleWrite event handler on s.
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.

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.

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.

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.

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


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

© 2006–2017 Andreas Rumpf
Licensed under the MIT License.