diff options
author | rumpf_a@web.de <> | 2010-01-03 12:31:21 +0100 |
---|---|---|
committer | rumpf_a@web.de <> | 2010-01-03 12:31:21 +0100 |
commit | a58a2f3823c33104992dc0e4129fa53e66a18f44 (patch) | |
tree | af97f1c6634d7ef2d4468c70607c20731e6c1512 /lib/pure/sockets.nim | |
parent | 2169fd63bdf9caf539ca7ca5b661ee703206500c (diff) | |
download | Nim-a58a2f3823c33104992dc0e4129fa53e66a18f44.tar.gz |
better subscript overloading
Diffstat (limited to 'lib/pure/sockets.nim')
-rw-r--r-- | lib/pure/sockets.nim | 206 |
1 files changed, 206 insertions, 0 deletions
diff --git a/lib/pure/sockets.nim b/lib/pure/sockets.nim new file mode 100644 index 000000000..0f9221b37 --- /dev/null +++ b/lib/pure/sockets.nim @@ -0,0 +1,206 @@ +# +# +# Nimrod's Runtime Library +# (c) Copyright 2010 Andreas Rumpf +# +# See the file "copying.txt", included in this +# distribution, for details about the copyright. +# + +## This module implements a simple portable type-safe sockets layer. **Note**: +## This module is incomplete and probably buggy. It does not work on Windows +## yet. Help if you are interested. + +# TODO: +# getservbyname(name, proto) +# getservbyport(port, proto) +# gethostbyname(name) +# gethostbyaddr(addr) +# shutdown(sock, how) +# connect(sock, address, port) +# select({ socket, ... }, timeout) + +# sendto +# recvfrom + +# bind(socket, address, port) + +# getsockopt(socket, level, optname) +# setsockopt(socket, level, optname, value) + +import os + +when defined(Windows): + import winlean +else: + import posix + +type + TSocket* = distinct cint ## socket type + TPort* = distinct int16 ## port type + + TDomain* = enum ## domain, which specifies the protocol family of the + ## created socket. Other domains than those that are listed + ## here are unsupported. + AF_UNIX, ## for local socket (using a file). + AF_INET, ## for network protocol IPv4 or + AF_INET6 ## for network protocol IPv6. + + TType* = enum ## second argument to `socket` proc + SOCK_STREAM, ## reliable stream-oriented service or Stream Sockets + SOCK_DGRAM, ## datagram service or Datagram Sockets + SOCK_SEQPACKET, ## reliable sequenced packet service, or + SOCK_RAW ## raw protocols atop the network layer. + + TProtocol* = enum ## third argument to `socket` proc + IPPROTO_TCP, ## Transmission control protocol. + IPPROTO_UDP, ## User datagram protocol. + IPPROTO_IP, ## Internet protocol. + IPPROTO_IPV6, ## Internet Protocol Version 6. + IPPROTO_RAW, ## Raw IP Packets Protocol. + IPPROTO_ICMP ## Control message protocol. + +const + InvalidSocket* = TSocket(-1'i32) ## invalid socket number + +proc `==`*(a, b: TSocket): bool {.borrow.} +proc `==`*(a, b: TPort): bool {.borrow.} + +proc ToInt(domain: TDomain): cint = + case domain + of AF_UNIX: result = posix.AF_UNIX + of AF_INET: result = posix.AF_INET + of AF_INET6: result = posix.AF_INET6 + +proc ToInt(typ: TType): cint = + case typ + of SOCK_STREAM: result = posix.SOCK_STREAM + of SOCK_DGRAM: result = posix.SOCK_DGRAM + of SOCK_SEQPACKET: result = posix.SOCK_SEQPACKET + of SOCK_RAW: result = posix.SOCK_RAW + +proc ToInt(p: TProtocol): cint = + case p + of IPPROTO_TCP: result = posix.IPPROTO_TCP + of IPPROTO_UDP: result = posix.IPPROTO_UDP + of IPPROTO_IP: result = posix.IPPROTO_IP + of IPPROTO_IPV6: result = posix.IPPROTO_IPV6 + of IPPROTO_RAW: result = posix.IPPROTO_RAW + of IPPROTO_ICMP: result = posix.IPPROTO_ICMP + +proc socket*(domain: TDomain = AF_INET6, typ: TType = SOCK_STREAM, + protocol: TProtocol = IPPROTO_TCP): TSocket = + ## creates a new socket; returns `InvalidSocket` if an error occurs. + result = TSocket(posix.socket(ToInt(domain), ToInt(typ), ToInt(protocol))) + +proc listen*(socket: TSocket, attempts = 5) = + ## listens to socket. + if posix.listen(cint(socket), cint(attempts)) < 0'i32: OSError() + +proc bindAddr*(socket: TSocket, port = TPort(0)) = + var name: Tsockaddr_in + name.sin_family = posix.AF_INET + name.sin_port = htons(int16(port)) + name.sin_addr.s_addr = htonl(INADDR_ANY) + if bindSocket(cint(socket), cast[ptr TSockAddr](addr(name)), + sizeof(name)) < 0'i32: + OSError() + +proc getSockName*(socket: TSocket): TPort = + var name: Tsockaddr_in + name.sin_family = posix.AF_INET + #name.sin_port = htons(cint16(port)) + #name.sin_addr.s_addr = htonl(INADDR_ANY) + var namelen: cint = sizeof(name) + if getsockname(cint(socket), cast[ptr TSockAddr](addr(name)), + addr(namelen)) == -1'i32: + OSError() + result = TPort(ntohs(name.sin_port)) + +proc accept*(server: TSocket): TSocket = + ## waits for a client and returns its socket + var client: Tsockaddr_in + var clientLen: TsockLen = sizeof(client) + result = TSocket(accept(cint(server), cast[ptr TSockAddr](addr(client)), + addr(clientLen))) + +proc close*(socket: TSocket) = + ## closes a socket. + when defined(windows): + discard winlean.closeSocket(cint(socket)) + else: + discard posix.close(cint(socket)) + +proc recvLine*(socket: TSocket, line: var string): bool = + ## returns false if no further data is available. + setLen(line, 0) + while true: + var c: char + var n = recv(cint(socket), addr(c), 1, 0'i32) + if n <= 0: return + if c == '\r': + n = recv(cint(socket), addr(c), 1, MSG_PEEK) + if n > 0 and c == '\L': + discard recv(cint(socket), addr(c), 1, 0'i32) + elif n <= 0: return false + return true + elif c == '\L': return true + add(line, c) + +proc recv*(socket: TSocket, data: pointer, size: int): int = + ## receive data from a socket + result = posix.recv(cint(socket), data, size, 0'i32) + +proc recv*(socket: TSocket): string = + ## receive all the data from the socket + const bufSize = 200 + var buf = newString(bufSize) + result = "" + while true: + var bytesRead = recv(socket, cstring(buf), bufSize-1) + buf[bytesRead] = '\0' # might not be necessary + setLen(buf, bytesRead) + add(result, buf) + if bytesRead != bufSize-1: break + +proc skip*(socket: TSocket) = + ## skips all the data that is pending for the socket + const bufSize = 200 + var buf = alloc(bufSize) + while recv(socket, buf, bufSize) == bufSize: nil + dealloc(buf) + +proc send*(socket: TSocket, data: pointer, size: int): int = + result = posix.send(cint(socket), data, size, 0'i32) + +proc send*(socket: TSocket, data: string) = + if send(socket, cstring(data), data.len) != data.len: OSError() + +proc ntohl*(x: int32): int32 = + ## Convert 32-bit integers from network to host byte order. + ## On machines where the host byte order is the same as network byte order, + ## this is a no-op; otherwise, it performs a 4-byte swap operation. + when cpuEndian == bigEndian: result = x + else: result = (x shr 24'i32) or + (x shr 8'i32 and 0xff00'i32) or + (x shl 8'i32 and 0xff0000'i32) or + (x shl 24'i32) + +proc ntohs*(x: int16): int16 = + ## Convert 16-bit integers from network to host byte order. On machines + ## where the host byte order is the same as network byte order, this is + ## a no-op; otherwise, it performs a 2-byte swap operation. + when cpuEndian == bigEndian: result = x + else: result = (x shr 8'i16) or (x shl 8'i16) + +proc htonl*(x: int32): int32 = + ## Convert 32-bit integers from host to network byte order. On machines + ## where the host byte order is the same as network byte order, this is + ## a no-op; otherwise, it performs a 4-byte swap operation. + result = sockets.ntohl(x) + +proc htons*(x: int16): int16 = + ## Convert 16-bit positive integers from host to network byte order. + ## On machines where the host byte order is the same as network byte + ## order, this is a no-op; otherwise, it performs a 2-byte swap operation. + result = sockets.ntohs(x) |