New version, without any piece of code duplication.

This commit is contained in:
cheatfate 2016-06-17 13:37:42 +03:00
commit b53f13cbe7
3 changed files with 265 additions and 2 deletions

View file

@ -862,6 +862,101 @@ when defined(windows) or defined(nimdoc):
# free ``ol``.
return retFuture
proc sendTo*(socket: AsyncFD, data: pointer, size: int, saddr: ptr SockAddr,
saddrLen: Socklen,
flags = {SocketFlag.SafeDisconn}): Future[void] =
## Sends ``data`` to specified destination ``saddr``, using
## socket ``socket``. The returned future will complete once all data
## has been sent.
verifyPresence(socket)
var retFuture = newFuture[void]("sendTo")
var dataBuf: TWSABuf
dataBuf.buf = cast[cstring](data)
dataBuf.len = size.ULONG
var bytesSent = 0.Dword
var lowFlags = 0.Dword
# we will preserve address in our stack
var staddr: array[128, char] # SOCKADDR_STORAGE size is 128 bytes
var stalen: cint = cint(saddrLen)
zeroMem(addr(staddr[0]), 128)
copyMem(addr(staddr[0]), saddr, saddrLen)
var ol = PCustomOverlapped()
GC_ref(ol)
ol.data = CompletionData(fd: socket, cb:
proc (fd: AsyncFD, bytesCount: Dword, errcode: OSErrorCode) =
if not retFuture.finished:
if errcode == OSErrorCode(-1):
retFuture.complete()
else:
retFuture.fail(newException(OSError, osErrorMsg(errcode)))
)
let ret = WSASendTo(socket.SocketHandle, addr dataBuf, 1, addr bytesSent,
lowFlags, cast[ptr SockAddr](addr(staddr[0])),
stalen, cast[POVERLAPPED](ol), nil)
if ret == -1:
let err = osLastError()
if err.int32 != ERROR_IO_PENDING:
GC_unref(ol)
retFuture.fail(newException(OSError, osErrorMsg(err)))
else:
retFuture.complete()
# We don't deallocate ``ol`` here because even though this completed
# immediately poll will still be notified about its completion and it will
# free ``ol``.
return retFuture
proc recvFromInto*(socket: AsyncFD, buf: pointer, size: int,
saddr: ptr SockAddr, saddrLen: ptr SockLen,
flags = {SocketFlag.SafeDisconn}): Future[int] =
## Receives a datagram data from ``socket`` into ``buf``, which must
## be at least of size ``size``, address of datagram's sender will be
## stored into ``saddr`` and ``saddrLen``. Returned future will complete
## once one datagram has been received, and will return size of packet
## received.
verifyPresence(socket)
var retFuture = newFuture[int]("recvFromInto")
var dataBuf = TWSABuf(buf: cast[cstring](buf), len: size.ULONG)
var bytesReceived = 0.Dword
var lowFlags = 0.Dword
var ol = PCustomOverlapped()
GC_ref(ol)
ol.data = CompletionData(fd: socket, cb:
proc (fd: AsyncFD, bytesCount: Dword, errcode: OSErrorCode) =
if not retFuture.finished:
if errcode == OSErrorCode(-1):
assert bytesCount <= size
retFuture.complete(bytesCount)
else:
# datagram sockets don't have disconnection,
# so we can just raise an exception
retFuture.fail(newException(OSError, osErrorMsg(errcode)))
)
let res = WSARecvFrom(socket.SocketHandle, addr dataBuf, 1,
addr bytesReceived, addr lowFlags,
saddr, cast[ptr cint](saddrLen),
cast[POVERLAPPED](ol), nil)
if res == -1:
let err = osLastError()
if err.int32 != ERROR_IO_PENDING:
GC_unref(ol)
retFuture.fail(newException(OSError, osErrorMsg(err)))
else:
# Request completed immediately.
if bytesReceived != 0:
assert bytesReceived <= size
retFuture.complete(bytesReceived)
else:
if hasOverlappedIoCompleted(cast[POVERLAPPED](ol)):
retFuture.complete(bytesReceived)
return retFuture
proc acceptAddr*(socket: AsyncFD, flags = {SocketFlag.SafeDisconn}):
Future[tuple[address: string, client: AsyncFD]] =
## Accepts a new connection. Returns a future containing the client socket
@ -1359,6 +1454,60 @@ else:
addWrite(socket, cb)
return retFuture
proc sendTo*(socket: AsyncFD, data: pointer, size: int, saddr: ptr SockAddr,
saddrLen: SockLen,
flags = {SocketFlag.SafeDisconn}): Future[void] =
## Sends ``data`` of size ``size`` in bytes to specified destination
## (``saddr`` of size ``saddrLen`` in bytes, using socket ``socket``.
## The returned future will complete once all data has been sent.
var retFuture = newFuture[void]("sendTo")
# we will preserve address in our stack
var staddr: array[128, char] # SOCKADDR_STORAGE size is 128 bytes
var stalen = saddrLen
zeroMem(addr(staddr[0]), 128)
copyMem(addr(staddr[0]), saddr, saddrLen)
proc cb(sock: AsyncFD): bool =
result = true
let res = sendto(sock.SocketHandle, data, size, MSG_NOSIGNAL,
cast[ptr SockAddr](addr(staddr[0])), stalen)
if res < 0:
let lastError = osLastError()
if lastError.int32 notin {EINTR, EWOULDBLOCK, EAGAIN}:
retFuture.fail(newException(OSError, osErrorMsg(lastError)))
else:
result = false # We still want this callback to be called.
else:
retFuture.complete()
addWrite(socket, cb)
return retFuture
proc recvFromInto*(socket: AsyncFD, data: pointer, size: int,
saddr: ptr SockAddr, saddrLen: ptr SockLen,
flags = {SocketFlag.SafeDisconn}): Future[int] =
## Receives a datagram data from ``socket`` into ``data``, which must
## be at least of size ``size`` in bytes, address of datagram's sender
## will be stored into ``saddr`` and ``saddrLen``. Returned future will
## complete once one datagram has been received, and will return size
## of packet received.
var retFuture = newFuture[int]("recvFromInto")
proc cb(sock: AsyncFD): bool =
result = true
let res = recvfrom(sock.SocketHandle, data, size.cint, flags.toOSFlags(),
saddr, saddrLen)
if res < 0:
let lastError = osLastError()
if lastError.int32 notin {EINTR, EWOULDBLOCK, EAGAIN}:
retFuture.fail(newException(OSError, osErrorMsg(lastError)))
else:
result = false
else:
retFuture.complete(res)
addRead(socket, cb)
return retFuture
proc acceptAddr*(socket: AsyncFD, flags = {SocketFlag.SafeDisconn}):
Future[tuple[address: string, client: AsyncFD]] =
var retFuture = newFuture[tuple[address: string,

View file

@ -455,7 +455,7 @@ type
sin_zero*: array[0..7, char]
In6_addr* {.importc: "IN6_ADDR", header: "winsock2.h".} = object
bytes*: array[0..15, char]
bytes* {.importc: "u.Byte".}: array[0..15, char]
Sockaddr_in6* {.importc: "SOCKADDR_IN6",
header: "ws2tcpip.h".} = object
@ -825,11 +825,23 @@ proc WSARecv*(s: SocketHandle, buf: ptr TWSABuf, bufCount: DWORD,
completionProc: POVERLAPPED_COMPLETION_ROUTINE): cint {.
stdcall, importc: "WSARecv", dynlib: "Ws2_32.dll".}
proc WSARecvFrom*(s: SocketHandle, buf: ptr TWSABuf, bufCount: DWORD,
bytesReceived: PDWORD, flags: PDWORD, name: ptr SockAddr,
namelen: ptr cint, lpOverlapped: POVERLAPPED,
completionProc: POVERLAPPED_COMPLETION_ROUTINE): cint {.
stdcall, importc: "WSARecvFrom", dynlib: "Ws2_32.dll".}
proc WSASend*(s: SocketHandle, buf: ptr TWSABuf, bufCount: DWORD,
bytesSent: PDWORD, flags: DWORD, lpOverlapped: POVERLAPPED,
completionProc: POVERLAPPED_COMPLETION_ROUTINE): cint {.
stdcall, importc: "WSASend", dynlib: "Ws2_32.dll".}
proc WSASendTo*(s: SocketHandle, buf: ptr TWSABuf, bufCount: DWORD,
bytesSent: PDWORD, flags: DWORD, name: ptr SockAddr,
namelen: cint, lpOverlapped: POVERLAPPED,
completionProc: POVERLAPPED_COMPLETION_ROUTINE): cint {.
stdcall, importc: "WSASendTo", dynlib: "Ws2_32.dll".}
proc get_osfhandle*(fd:FileHandle): Handle {.
importc: "_get_osfhandle", header:"<io.h>".}
@ -994,7 +1006,7 @@ const
FD_ALL_EVENTS* = 0x000003FF'i32
proc wsaEventSelect*(s: SocketHandle, hEventObject: Handle,
lNetworkEvents: clong): cint
lNetworkEvents: clong): cint
{.stdcall, importc: "WSAEventSelect", dynlib: "ws2_32.dll".}
proc wsaCreateEvent*(): Handle