This module implements a high-level cross-platform sockets interface. The procedures implemented in this module are primarily for blocking sockets. For asynchronous non-blocking sockets use the asyncnet
module together with the asyncdispatch
module.
The first thing you will always need to do in order to start using sockets, is to create a new instance of the Socket
type using the newSocket
procedure.
In order to use the SSL procedures defined in this module, you will need to compile your application with the -d:ssl
flag.
After you create a socket with the newSocket
procedure, you can easily connect it to a server running at a known hostname (or IP address) and port. To do so over TCP, use the example below.
var socket = newSocket() socket.connect("google.com", Port(80))
UDP is a connectionless protocol, so UDP sockets don't have to explicitly call the connect
procedure. They can simply start sending data immediately.
var socket = newSocket(AF_INET, SOCK_DGRAM, IPPROTO_UDP) socket.sendTo("192.168.0.1", Port(27960), "status\n")
After you create a socket with the newSocket
procedure, you can create a TCP server by calling the bindAddr
and listen
procedures.
var socket = newSocket() socket.bindAddr(Port(1234)) socket.listen()
You can then begin accepting connections using the accept
procedure.
var client: Socket var address = "" while true: socket.acceptAddr(client, address) echo("Client connected from: ", address)
SslError = object of Exception
SslCVerifyMode = enum CVerifyNone, CVerifyPeer
SslProtVersion = enum protSSLv2, protSSLv3, protTLSv1, protSSLv23
SslContext = ref object context*: SslCtx referencedData: HashSet[int] extraInternal: SslContextExtraInternal
SslAcceptResult = enum AcceptNoClient = 0, AcceptNoHandshake, AcceptSuccess
SslHandshakeType = enum handshakeAsClient, handshakeAsServer
SslClientGetPskFunc = proc (hint: string): tuple[identity: string, psk: string]
SslServerGetPskFunc = proc (identity: string): string
SocketImpl = object fd: SocketHandle case isBuffered: bool of true: buffer: array[0 .. BufferSize, char] currPos: int bufLen: int of false: nil when defineSsl: case isSsl: bool of true: sslHandle: SslPtr sslContext: SslContext sslNoHandshake: bool sslHasPeekChar: bool sslPeekChar: char of false: nil lastError: OSErrorCode ## stores the last error on this socket domain: Domain sockType: SockType protocol: Protocol
Socket = ref SocketImpl
SOBool = enum OptAcceptConn, OptBroadcast, OptDebug, OptDontRoute, OptKeepAlive, OptOOBInline, OptReuseAddr, OptReusePort, OptNoDelay
ReadLineResult = enum ReadFullLine, ReadPartialLine, ReadDisconnected, ReadNone
TimeoutError = object of Exception
SocketFlag {...}{.pure.} = enum Peek, SafeDisconn ## Ensures disconnection exceptions (ECONNRESET, EPIPE etc) are not thrown.
IpAddressFamily {...}{.pure.} = enum IPv6, ## IPv6 address IPv4 ## IPv4 address
IpAddress = object case family*: IpAddressFamily ## the type of the IP address (IPv4 or IPv6) of IpAddressFamily.IPv6: address_v6*: array[0 .. 15, uint8] ## Contains the IP address in bytes in ## case of IPv6 of IpAddressFamily.IPv4: address_v4*: array[0 .. 3, uint8] ## Contains the IP address in bytes in ## case of IPv4
BufferSize: int = 4000
MaxLineLength = 1000000
proc isDisconnectionError(flags: set[SocketFlag]; lastError: OSErrorCode): bool {...}{. raises: [], tags: [].}
lastError
is a disconnection error. Only does this if flags contains SafeDisconn
. proc toOSFlags(socketFlags: set[SocketFlag]): cint {...}{.raises: [], tags: [].}
proc newSocket(fd: SocketHandle; domain: Domain = AF_INET; sockType: SockType = SOCK_STREAM; protocol: Protocol = IPPROTO_TCP; buffered = true): Socket {...}{.raises: [], tags: [].}
proc newSocket(domain, sockType, protocol: cint; buffered = true): Socket {...}{. raises: [OSError], tags: [].}
Creates a new socket.
If an error occurs EOS will be raised.
proc newSocket(domain: Domain = AF_INET; sockType: SockType = SOCK_STREAM; protocol: Protocol = IPPROTO_TCP; buffered = true): Socket {...}{. raises: [OSError], tags: [].}
Creates a new socket.
If an error occurs EOS will be raised.
proc parseIpAddress(addressStr: string): IpAddress {...}{.raises: [ValueError], tags: [].}
proc isIpAddress(addressStr: string): bool {...}{.tags: [], raises: [].}
proc toSockAddr(address: IpAddress; port: Port; sa: var Sockaddr_storage; sl: var SockLen) {...}{.raises: [], tags: [].}
proc fromSockAddr(sa: Sockaddr_storage | SockAddr | Sockaddr_in | Sockaddr_in6; sl: SockLen; address: var IpAddress; port: var Port) {...}{.inline.}
proc raiseSSLError(s = "") {...}{.raises: [SslError, OSError], tags: [].}
proc getExtraData(ctx: SslContext; index: int): RootRef {...}{. raises: [IndexError, SslError, OSError], tags: [].}
proc setExtraData(ctx: SslContext; index: int; data: RootRef) {...}{. raises: [IndexError, SslError, OSError], tags: [].}
proc newContext(protVersion = protSSLv23; verifyMode = CVerifyPeer; certFile = ""; keyFile = ""; cipherList = "ALL"): SslContext {...}{. raises: [Exception, SslError, OSError, IOError], tags: [RootEffect, ReadDirEffect].}
Creates an SSL context.
Protocol version specifies the protocol to use. SSLv2, SSLv3, TLSv1 are available with the addition of protSSLv23
which allows for compatibility with all of them.
There are currently only two options for verify mode; one is CVerifyNone
and with it certificates will not be verified the other is CVerifyPeer
and certificates will be verified for it, CVerifyPeer
is the safest choice.
The last two parameters specify the certificate file path and the key file path, a server socket will most likely not work without these. Certificates can be generated using the following command: openssl req -x509 -nodes -days 365 -newkey rsa:1024 -keyout mycert.pem -out mycert.pem
.
proc destroyContext(ctx: SslContext) {...}{.raises: [IndexError, SslError, OSError], tags: [].}
proc pskIdentityHint=(ctx: SslContext; hint: string) {...}{.raises: [SslError, OSError], tags: [].}
Sets the identity hint passed to server.
Only used in PSK ciphersuites.
proc clientGetPskFunc(ctx: SslContext): SslClientGetPskFunc {...}{.raises: [], tags: [].}
proc clientGetPskFunc=(ctx: SslContext; fun: SslClientGetPskFunc) {...}{. raises: [Exception], tags: [RootEffect].}
Sets function that returns the client identity and the PSK based on identity hint from the server.
Only used in PSK ciphersuites.
proc serverGetPskFunc(ctx: SslContext): SslServerGetPskFunc {...}{.raises: [], tags: [].}
proc serverGetPskFunc=(ctx: SslContext; fun: SslServerGetPskFunc) {...}{. raises: [Exception], tags: [RootEffect].}
Sets function that returns PSK based on the client identity.
Only used in PSK ciphersuites.
proc getPskIdentity(socket: Socket): string {...}{.raises: [], tags: [].}
proc wrapSocket(ctx: SslContext; socket: Socket) {...}{.raises: [SslError, OSError], tags: [].}
Wraps a socket in an SSL context. This function effectively turns socket
into an SSL socket.
This must be called on an unconnected socket; an SSL session will be started when the socket is connected.
Disclaimer: This code is not well tested, may be very unsafe and prone to security vulnerabilities.
proc wrapConnectedSocket(ctx: SslContext; socket: Socket; handshake: SslHandshakeType; hostname: string = "") {...}{. raises: [SslError, OSError, Exception], tags: [RootEffect].}
Wraps a connected socket in an SSL context. This function effectively turns socket
into an SSL socket. hostname
should 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.
proc getSocketError(socket: Socket): OSErrorCode {...}{.raises: [OSError], tags: [].}
osLastError
for a valid error. If it has been reset it uses the last error stored in the socket object. proc socketError(socket: Socket; err: int = -1; async = false; lastError = -1.OSErrorCode): void {...}{. gcsafe, raises: [SslError, OSError], tags: [].}
Raises an OSError based on the error code returned by SSLGetError
(for SSL sockets) and osLastError
otherwise.
If async
is true
no error will be thrown in the case when the error was caused by no data being available to be read.
If err
is not lower than 0 no exception will be raised.
proc listen(socket: Socket; backlog = SOMAXCONN) {...}{.tags: [ReadIOEffect], raises: [OSError].}
Marks socket
as accepting connections. Backlog
specifies the maximum length of the queue of pending connections.
Raises an EOS error upon failure.
proc bindAddr(socket: Socket; port = Port(0); address = "") {...}{.tags: [ReadIOEffect], raises: [OSError].}
Binds address
:port
to the socket.
If address
is "" then ADDR_ANY will be bound.
proc acceptAddr(server: Socket; client: var Socket; address: var string; flags = {SafeDisconn}) {...}{.tags: [ReadIOEffect], gcsafe, locks: 0, raises: [OSError, IOError, SslError].}
Blocks until a connection is being made from a client. When a connection is made sets client
to the client socket and address
to the address of the connecting client. This function will raise EOS if an error occurs.
The resulting client will inherit any properties of the server socket. For example: whether the socket is buffered or not.
The accept
call may result in an error if the connecting socket disconnects during the duration of the accept
. If the SafeDisconn
flag is specified then this error will not be raised and instead accept will be called again.
proc accept(server: Socket; client: var Socket; flags = {SafeDisconn}) {...}{. tags: [ReadIOEffect], raises: [OSError, IOError, SslError].}
Equivalent to acceptAddr
but doesn't return the address, only the socket.
The accept
call may result in an error if the connecting socket disconnects during the duration of the accept
. If the SafeDisconn
flag is specified then this error will not be raised and instead accept will be called again.
proc close(socket: Socket) {...}{.raises: [SslError, OSError], tags: [].}
proc toCInt(opt: SOBool): cint {...}{.raises: [], tags: [].}
SOBool
into its Socket Option cint representation. proc getSockOpt(socket: Socket; opt: SOBool; level = SOL_SOCKET): bool {...}{. tags: [ReadIOEffect], raises: [OSError].}
opt
as a boolean value. proc getLocalAddr(socket: Socket): (string, Port) {...}{.raises: [OSError, Exception], tags: [RootEffect].}
Get the socket's local address and port number.
This is high-level interface for getsockname.
proc getPeerAddr(socket: Socket): (string, Port) {...}{.raises: [OSError, Exception], tags: [RootEffect].}
Get the socket's peer address and port number.
This is high-level interface for getpeername.
proc setSockOpt(socket: Socket; opt: SOBool; value: bool; level = SOL_SOCKET) {...}{. tags: [WriteIOEffect], raises: [OSError].}
opt
to a boolean value specified by value
.var socket = newSocket() socket.setSockOpt(OptReusePort, true) socket.setSockOpt(OptNoDelay, true, level=IPPROTO_TCP.toInt)
proc connectUnix(socket: Socket; path: string) {...}{.raises: [], tags: [].}
proc bindUnix(socket: Socket; path: string) {...}{.raises: [], tags: [].}
proc hasDataBuffered(s: Socket): bool {...}{.raises: [], tags: [].}
proc recv(socket: Socket; data: pointer; size: int): int {...}{.tags: [ReadIOEffect], raises: [].}
Receives data from a socket.
Note: This is a low-level function, you may be interested in the higher level versions of this function which are also named recv
.
proc recv(socket: Socket; data: pointer; size: int; timeout: int): int {...}{. tags: [ReadIOEffect, TimeEffect], raises: [TimeoutError, OSError].}
timeout
parameter in milliseconds. proc recv(socket: Socket; data: var string; size: int; timeout = -1; flags = {SafeDisconn}): int {...}{. raises: [TimeoutError, OSError, SslError], tags: [ReadIOEffect, TimeEffect].}
Higher-level version of recv
.
When 0 is returned the socket's connection has been closed.
This function will throw an OSError exception when an error occurs. A value lower than 0 is never returned.
A timeout may be specified in milliseconds, if enough data is not received within the time specified an TimeoutError exception will be raised.
Note: data
must be initialised.
Warning: Only the SafeDisconn
flag is currently supported.
proc recv(socket: Socket; size: int; timeout = -1; flags = {SafeDisconn}): string {...}{.inline, raises: [TimeoutError, OSError, SslError], tags: [ReadIOEffect, TimeEffect].}
Higher-level version of recv
which returns a string.
When ""
is returned the socket's connection has been closed.
This function will throw an EOS exception when an error occurs.
A timeout may be specified in milliseconds, if enough data is not received within the time specified an ETimeout exception will be raised.
Warning: Only the SafeDisconn
flag is currently supported.
proc readLine(socket: Socket; line: var TaintedString; timeout = -1; flags = {SafeDisconn}; maxLength = MaxLineLength) {...}{. tags: [ReadIOEffect, TimeEffect], raises: [TimeoutError, OSError, SslError].}
Reads a line of data from socket
.
If a full line is read \r\L
is not added to line
, however if solely \r\L
is read then line
will be set to it.
If the socket is disconnected, line
will be set to ""
.
An EOS exception will be raised in the case of a socket error.
A timeout can be specified in milliseconds, if data is not received within the specified time an ETimeout exception will be raised.
The maxLength
parameter determines the maximum amount of characters that can be read. The result is truncated after that.
Warning: Only the SafeDisconn
flag is currently supported.
proc recvLine(socket: Socket; timeout = -1; flags = {SafeDisconn}; maxLength = MaxLineLength): TaintedString {...}{. raises: [TimeoutError, OSError, SslError], tags: [ReadIOEffect, TimeEffect].}
Reads a line of data from socket
.
If a full line is read \r\L
is not added to the result, however if solely \r\L
is read then the result will be set to it.
If the socket is disconnected, the result will be set to ""
.
An EOS exception will be raised in the case of a socket error.
A timeout can be specified in milliseconds, if data is not received within the specified time an ETimeout exception will be raised.
The maxLength
parameter determines the maximum amount of characters that can be read. The result is truncated after that.
Warning: Only the SafeDisconn
flag is currently supported.
proc recvFrom(socket: Socket; data: var string; length: int; address: var string; port: var Port; flags = 0'i32): int {...}{.tags: [ReadIOEffect], raises: [OSError].}
Receives data from socket
. This function should normally be used with connection-less sockets (UDP sockets).
If an error occurs an EOS exception will be raised. Otherwise the return value will be the length of data received.
Warning: This function does not yet have a buffered implementation, so when socket
is buffered the non-buffered implementation will be used. Therefore if socket
contains something in its buffer this function will make no effort to return it.
proc skip(socket: Socket; size: int; timeout = -1) {...}{. raises: [Exception, TimeoutError, OSError], tags: [TimeEffect, ReadIOEffect].}
Skips size
amount of bytes.
An optional timeout can be specified in milliseconds, if skipping the bytes takes longer than specified an ETimeout exception will be raised.
Returns the number of skipped bytes.
proc send(socket: Socket; data: pointer; size: int): int {...}{.tags: [WriteIOEffect], raises: [].}
Sends data to a socket.
Note: This is a low-level version of send
. You likely should use the version below.
proc send(socket: Socket; data: string; flags = {SafeDisconn}) {...}{.tags: [WriteIOEffect], raises: [SslError, OSError].}
proc trySend(socket: Socket; data: string): bool {...}{.tags: [WriteIOEffect], raises: [].}
send
. Does not raise an EOS when an error occurs, and instead returns false
on failure. proc sendTo(socket: Socket; address: string; port: Port; data: pointer; size: int; af: Domain = AF_INET; flags = 0'i32) {...}{.tags: [WriteIOEffect], raises: [OSError].}
This proc sends data
to the specified address
, which may be an IP address or a hostname, if a hostname is specified this function will try each IP of that hostname.
If an error occurs an OSError exception will be raised.
Note: You may wish to use the high-level version of this function which is defined below.
Note: This proc is not available for SSL sockets.
proc sendTo(socket: Socket; address: string; port: Port; data: string) {...}{. tags: [WriteIOEffect], raises: [OSError].}
This proc sends data
to the specified address
, which may be an IP address or a hostname, if a hostname is specified this function will try each IP of that hostname.
If an error occurs an OSError exception will be raised.
This is the high-level version of the above sendTo
function.
proc isSsl(socket: Socket): bool {...}{.raises: [], tags: [].}
socket
is a SSL socket. proc getFd(socket: Socket): SocketHandle {...}{.raises: [], tags: [].}
proc IPv4_any(): IpAddress {...}{.raises: [], tags: [].}
proc IPv4_loopback(): IpAddress {...}{.raises: [], tags: [].}
proc IPv4_broadcast(): IpAddress {...}{.raises: [], tags: [].}
proc IPv6_any(): IpAddress {...}{.raises: [], tags: [].}
proc IPv6_loopback(): IpAddress {...}{.raises: [], tags: [].}
proc `==`(lhs, rhs: IpAddress): bool {...}{.raises: [], tags: [].}
proc `$`(address: IpAddress): string {...}{.raises: [], tags: [].}
proc dial(address: string; port: Port; protocol = IPPROTO_TCP; buffered = true): Socket {...}{. tags: [ReadIOEffect, WriteIOEffect], raises: [OSError, IOError].}
address
:port
pair via the specified protocol. The procedure iterates through possible resolutions of the address
until it succeeds, meaning that it seamlessly works with both IPv4 and IPv6. Returns Socket ready to send or receive data. proc connect(socket: Socket; address: string; port = Port(0)) {...}{.tags: [ReadIOEffect], raises: [OSError, SslError].}
Connects socket to address
:port
. Address
can be an IP address or a host name. If address
is a host name, this function will try each IP of that host name. htons
is already performed on port
so you must not do it.
If socket
is an SSL socket a handshake will be automatically performed.
proc connect(socket: Socket; address: string; port = Port(0); timeout: int) {...}{. tags: [ReadIOEffect, WriteIOEffect], raises: [OSError, TimeoutError].}
Connects to server as specified by address
on port specified by port
.
The timeout
paremeter specifies the time in milliseconds to allow for the connection to the server to be made.
template `&=`(socket: Socket; data: typed)
© 2006–2018 Andreas Rumpf
Licensed under the MIT License.
https://nim-lang.org/docs/net.html