add a second asyncnet.recvFrom (#14237)
* add a second asyncnet.recvFrom * fixes * pre-allocating address * add a new nativesockets.getAddrString()
This commit is contained in:
parent
bc66788b3f
commit
252f668829
2 changed files with 98 additions and 31 deletions
|
|
@ -853,6 +853,60 @@ proc sendTo*(socket: AsyncSocket, address: string, port: Port, data: string,
|
||||||
else:
|
else:
|
||||||
raise newException(IOError, "Couldn't resolve address: " & address)
|
raise newException(IOError, "Couldn't resolve address: " & address)
|
||||||
|
|
||||||
|
proc recvFrom*(socket: AsyncSocket, data: FutureVar[string], size: int,
|
||||||
|
address: FutureVar[string], port: FutureVar[Port],
|
||||||
|
flags = {SocketFlag.SafeDisconn}): owned(Future[int])
|
||||||
|
{.async, since: (1, 3).} =
|
||||||
|
## Receives a datagram data from ``socket`` into ``data``, which must be at
|
||||||
|
## least of size ``size``. The address and port of datagram's sender will be
|
||||||
|
## stored into ``address`` and ``port``, respectively. Returned future will
|
||||||
|
## complete once one datagram has been received, and will return size of
|
||||||
|
## packet received.
|
||||||
|
##
|
||||||
|
## If an error occurs an OSError exception will be raised.
|
||||||
|
##
|
||||||
|
## This proc is normally used with connectionless sockets (UDP sockets).
|
||||||
|
##
|
||||||
|
## **Notes**
|
||||||
|
## * ``data`` must be initialized to the length of ``size``.
|
||||||
|
## * ``address`` must be initialized to 46 in length.
|
||||||
|
template adaptRecvFromToDomain(domain: Domain) =
|
||||||
|
var lAddr = sizeof(sAddr).SockLen
|
||||||
|
|
||||||
|
result = await recvFromInto(AsyncFD(getFd(socket)), cstring(data.mget()), size,
|
||||||
|
cast[ptr SockAddr](addr sAddr), addr lAddr,
|
||||||
|
flags)
|
||||||
|
|
||||||
|
data.mget().setLen(result)
|
||||||
|
data.complete()
|
||||||
|
|
||||||
|
getAddrString(cast[ptr SockAddr](addr sAddr), address.mget())
|
||||||
|
|
||||||
|
address.complete()
|
||||||
|
|
||||||
|
when domain == AF_INET6:
|
||||||
|
port.complete(ntohs(sAddr.sin6_port).Port)
|
||||||
|
else:
|
||||||
|
port.complete(ntohs(sAddr.sin_port).Port)
|
||||||
|
|
||||||
|
assert(socket.protocol != IPPROTO_TCP,
|
||||||
|
"Cannot `recvFrom` on a TCP socket. Use `recv` or `recvInto` instead")
|
||||||
|
assert(not socket.closed, "Cannot `recvFrom` on a closed socket")
|
||||||
|
assert(size == len(data.mget()),
|
||||||
|
"`date` was not initialized correctly. `size` != `len(data.mget())`")
|
||||||
|
assert(46 == len(address.mget()),
|
||||||
|
"`address` was not initialized correctly. 46 != `len(address.mget())`")
|
||||||
|
|
||||||
|
case socket.domain
|
||||||
|
of AF_INET6:
|
||||||
|
var sAddr: Sockaddr_in6
|
||||||
|
adaptRecvFromToDomain(AF_INET6)
|
||||||
|
of AF_INET:
|
||||||
|
var sAddr: Sockaddr_in
|
||||||
|
adaptRecvFromToDomain(AF_INET)
|
||||||
|
else:
|
||||||
|
raise newException(ValueError, "Unknown socket address family")
|
||||||
|
|
||||||
proc recvFrom*(socket: AsyncSocket, size: int,
|
proc recvFrom*(socket: AsyncSocket, size: int,
|
||||||
flags = {SocketFlag.SafeDisconn}):
|
flags = {SocketFlag.SafeDisconn}):
|
||||||
owned(Future[tuple[data: string, address: string, port: Port]])
|
owned(Future[tuple[data: string, address: string, port: Port]])
|
||||||
|
|
@ -865,38 +919,17 @@ proc recvFrom*(socket: AsyncSocket, size: int,
|
||||||
## If an error occurs an OSError exception will be raised.
|
## If an error occurs an OSError exception will be raised.
|
||||||
##
|
##
|
||||||
## This proc is normally used with connectionless sockets (UDP sockets).
|
## This proc is normally used with connectionless sockets (UDP sockets).
|
||||||
template adaptRecvFromToDomain(domain: Domain) =
|
var
|
||||||
var lAddr = sizeof(sAddr).SockLen
|
data = newFutureVar[string]()
|
||||||
|
address = newFutureVar[string]()
|
||||||
let fut = await recvFromInto(AsyncFD(getFd(socket)), cstring(data), size,
|
port = newFutureVar[Port]()
|
||||||
cast[ptr SockAddr](addr sAddr), addr lAddr,
|
|
||||||
flags)
|
|
||||||
|
|
||||||
data.setLen(fut)
|
|
||||||
|
|
||||||
result.data = data
|
|
||||||
result.address = getAddrString(cast[ptr SockAddr](addr sAddr))
|
|
||||||
|
|
||||||
when domain == AF_INET6:
|
|
||||||
result.port = ntohs(sAddr.sin6_port).Port
|
|
||||||
else:
|
|
||||||
result.port = ntohs(sAddr.sin_port).Port
|
|
||||||
|
|
||||||
assert(socket.protocol != IPPROTO_TCP,
|
|
||||||
"Cannot `recvFrom` on a TCP socket. Use `recv` or `recvInto` instead")
|
|
||||||
assert(not socket.closed, "Cannot `recvFrom` on a closed socket")
|
|
||||||
|
|
||||||
var data = newString(size)
|
|
||||||
|
|
||||||
case socket.domain
|
data.mget().setLen(size)
|
||||||
of AF_INET6:
|
address.mget().setLen(46)
|
||||||
var sAddr: Sockaddr_in6
|
|
||||||
adaptRecvFromToDomain(AF_INET6)
|
let read = await recvFrom(socket, data, size, address, port, flags)
|
||||||
of AF_INET:
|
|
||||||
var sAddr: Sockaddr_in
|
result = (data.mget(), address.mget(), port.mget())
|
||||||
adaptRecvFromToDomain(AF_INET)
|
|
||||||
else:
|
|
||||||
raise newException(ValueError, "Unknown socket address family")
|
|
||||||
|
|
||||||
when not defined(testing) and isMainModule:
|
when not defined(testing) and isMainModule:
|
||||||
type
|
type
|
||||||
|
|
|
||||||
|
|
@ -461,6 +461,40 @@ proc getAddrString*(sockAddr: ptr SockAddr): string =
|
||||||
return "unix"
|
return "unix"
|
||||||
raise newException(IOError, "Unknown socket family in getAddrString")
|
raise newException(IOError, "Unknown socket family in getAddrString")
|
||||||
|
|
||||||
|
proc getAddrString*(sockAddr: ptr SockAddr, strAddress: var string) =
|
||||||
|
## Stores in ``strAddress`` the string representation of the address inside
|
||||||
|
## ``sockAddr``
|
||||||
|
##
|
||||||
|
## **Note**
|
||||||
|
## * ``strAddress`` must be initialized to 46 in length.
|
||||||
|
assert(46 == len(strAddress),
|
||||||
|
"`strAddress` was not initialized correctly. 46 != `len(strAddress)`")
|
||||||
|
if sockAddr.sa_family.cint == nativeAfInet:
|
||||||
|
let addr4 = addr cast[ptr Sockaddr_in](sockAddr).sin_addr
|
||||||
|
when not useWinVersion:
|
||||||
|
if posix.inet_ntop(posix.AF_INET, addr4, addr strAddress[0],
|
||||||
|
strAddress.len.int32) == nil:
|
||||||
|
raiseOSError(osLastError())
|
||||||
|
else:
|
||||||
|
if winlean.inet_ntop(winlean.AF_INET, addr4, addr strAddress[0],
|
||||||
|
strAddress.len.int32) == nil:
|
||||||
|
raiseOSError(osLastError())
|
||||||
|
elif sockAddr.sa_family.cint == nativeAfInet6:
|
||||||
|
let addr6 = addr cast[ptr Sockaddr_in6](sockAddr).sin6_addr
|
||||||
|
when not useWinVersion:
|
||||||
|
if posix.inet_ntop(posix.AF_INET6, addr6, addr strAddress[0],
|
||||||
|
strAddress.len.int32) == nil:
|
||||||
|
raiseOSError(osLastError())
|
||||||
|
if posix.IN6_IS_ADDR_V4MAPPED(addr6) != 0:
|
||||||
|
strAddress = strAddress.substr("::ffff:".len)
|
||||||
|
else:
|
||||||
|
if winlean.inet_ntop(winlean.AF_INET6, addr6, addr strAddress[0],
|
||||||
|
strAddress.len.int32) == nil:
|
||||||
|
raiseOSError(osLastError())
|
||||||
|
else:
|
||||||
|
raise newException(IOError, "Unknown socket family in getAddrString")
|
||||||
|
setLen(strAddress, len(cstring(strAddress)))
|
||||||
|
|
||||||
when defined(posix) and not defined(nimdoc):
|
when defined(posix) and not defined(nimdoc):
|
||||||
proc makeUnixAddr*(path: string): Sockaddr_un =
|
proc makeUnixAddr*(path: string): Sockaddr_un =
|
||||||
result.sun_family = AF_UNIX.TSa_Family
|
result.sun_family = AF_UNIX.TSa_Family
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue