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.
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.
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)
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
readLine
SocketStatus = enum SockIdle, SockConnecting, SockConnected, SockListening, SockClosed, SockUDPBound
proc newDelegate(): Delegate {...}{.raises: [], tags: [].}
proc asyncSocket(domain: Domain = AF_INET; typ: SockType = SOCK_STREAM; protocol: Protocol = IPPROTO_TCP; buffered = true): AsyncSocket {...}{. raises: [OSError], tags: [].}
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].}
sock
to name
:port
. proc close(sock: AsyncSocket) {...}{.gcsafe, raises: [], tags: [].}
sock
. Terminates any current connections. proc bindAddr(sock: AsyncSocket; port = Port(0); address = "") {...}{.raises: [OSError], tags: [ReadIOEffect].}
sockets.bindAddr
. proc listen(sock: AsyncSocket) {...}{.raises: [OSError], tags: [ReadIOEffect].}
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].}
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: [].}
deleg
with dispatcher d
. proc register(d: Dispatcher; sock: AsyncSocket): Delegate {...}{.discardable, raises: [], tags: [].}
sock
with dispatcher d
. proc unregister(d: Dispatcher; deleg: Delegate) {...}{.raises: [IndexError], tags: [].}
deleg
from dispatcher d
. proc isWriteable(s: AsyncSocket): bool {...}{.raises: [], tags: [ReadIOEffect].}
s
is ready to be written to. proc isConnected(s: AsyncSocket): bool {...}{.raises: [], tags: [].}
s
is connected. proc isListening(s: AsyncSocket): bool {...}{.raises: [], tags: [].}
s
is listening for incoming connections. proc isConnecting(s: AsyncSocket): bool {...}{.raises: [], tags: [].}
s
is connecting. proc isClosed(s: AsyncSocket): bool {...}{.raises: [], tags: [].}
s
has been closed. proc isSendDataBuffered(s: AsyncSocket): bool {...}{.raises: [], tags: [].}
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: [].}
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: [].}
disp
. converter getSocket(s: AsyncSocket): Socket {...}{.raises: [], tags: [].}
© 2006–2018 Andreas Rumpf
Licensed under the MIT License.
https://nim-lang.org/docs/asyncio.html