From 3cf2ac91e54630e72e488e9f568777030cfb2b97 Mon Sep 17 00:00:00 2001 From: Isaiah Norton Date: Sat, 20 Dec 2014 00:51:48 -0500 Subject: [PATCH 1/3] socket: match _bind signature --- base/socket.jl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/base/socket.jl b/base/socket.jl index fecd25c7999ac..7b07dac076943 100644 --- a/base/socket.jl +++ b/base/socket.jl @@ -427,7 +427,7 @@ _bind(sock::UDPSocket, host::IPv6, port::UInt16, flags::UInt32 = uint32(0)) = cc function bind(sock::UDPSocket, host::IPv6, port::UInt16; ipv6only = false) @assert sock.status == StatusInit - err = _bind(sock,host,ipv6only ? UV_UDP_IPV6ONLY : 0) + err = _bind(sock,host,port, uint32(ipv6only ? UV_UDP_IPV6ONLY : 0)) if err < 0 if err != UV_EADDRINUSE && err != UV_EACCES error(UVError("bind",err)) From 8c3cea061b1c5157f79696e74ce6e4f794f98deb Mon Sep 17 00:00:00 2001 From: Isaiah Norton Date: Sat, 20 Dec 2014 00:54:48 -0500 Subject: [PATCH 2/3] socket: add udp recvfrom support --- base/exports.jl | 1 + base/socket.jl | 22 ++++++++++++++-- doc/stdlib/io-network.rst | 4 +++ test/socket.jl | 54 ++++++++++++++++++++++++++++----------- 4 files changed, 64 insertions(+), 17 deletions(-) diff --git a/base/exports.jl b/base/exports.jl index 3c61332c34e56..594c65866ee03 100644 --- a/base/exports.jl +++ b/base/exports.jl @@ -1177,6 +1177,7 @@ export redirect_stdin, redirect_stdout, recv, + recvfrom, reset, seek, seekend, diff --git a/base/socket.jl b/base/socket.jl index 7b07dac076943..07b82d5916d03 100644 --- a/base/socket.jl +++ b/base/socket.jl @@ -470,22 +470,40 @@ end _recv_stop(sock::UDPSocket) = uv_error("recv_stop",ccall(:uv_udp_recv_stop,Cint,(Ptr{Void},),sock.handle)) function recv(sock::UDPSocket) + addr, data = recvfrom(sock) + data +end + +function recvfrom(sock::UDPSocket) # If the socket has not been bound, it will be bound implicitly to ::0 and a random port if sock.status != StatusInit && sock.status != StatusOpen error("Invalid socket state") end _recv_start(sock) - stream_wait(sock,sock.recvnotify)::Vector{UInt8} + stream_wait(sock,sock.recvnotify)::(Union(IPv4, IPv6), Vector{UInt8}) end + function _uv_hook_recv(sock::UDPSocket, nread::Int, buf_addr::Ptr{Void}, buf_size::UInt, addr::Ptr{Void}, flags::Int32) + # C signature documented as (*uv_udp_recv_cb)(...) if flags & UV_UDP_PARTIAL > 0 # TODO: Decide what to do in this case. For now throw an error c_free(buf_addr) notify_error(sock.recvnotify,"Partial message received") end + + # need to check the address type in order to convert to a Julia IPAddr + addrout = if (addr == C_NULL) + IPv4(0) + elseif ccall(:jl_sockaddr_in_is_ip4, Cint, (Ptr{Void},), addr) == 1 + IPv4(ntoh(ccall(:jl_sockaddr_host4, Uint32, (Ptr{Void},), addr))) + else + tmp = [uint128(0)] + ccall(:jl_sockaddr_host6, Uint32, (Ptr{Void}, Ptr{Uint8}), addr, pointer(tmp)) + IPv6(ntoh(tmp[1])) + end buf = pointer_to_array(convert(Ptr{UInt8},buf_addr),int(buf_size),true) - notify(sock.recvnotify,buf[1:nread]) + notify(sock.recvnotify,(addrout,buf[1:nread])) end function _send(sock::UDPSocket,ipaddr::IPv4,port::UInt16,buf) diff --git a/doc/stdlib/io-network.rst b/doc/stdlib/io-network.rst index 4fa39bcf73a67..a9f93aaefc357 100644 --- a/doc/stdlib/io-network.rst +++ b/doc/stdlib/io-network.rst @@ -765,6 +765,10 @@ Network I/O Read a UDP packet from the specified socket, and return the bytes received. This call blocks. +.. function:: recvfrom(socket::UDPSocket) -> (address, data) + + Read a UDP packet from the specified socket, returning a tuple of (address, data), where address will be either IPv4 or IPv6 as appropriate. + .. function:: setopt(sock::UDPSocket; multicast_loop = nothing, multicast_ttl=nothing, enable_broadcast=nothing, ttl=nothing) Set UDP socket options. ``multicast_loop``: loopback for multicast packets (default: true). ``multicast_ttl``: TTL for multicast packets. ``enable_broadcast``: flag must be set to true if socket will be used for broadcast messages, or else the UDP system will return an access error (default: false). ``ttl``: Time-to-live of packets sent on the socket. diff --git a/test/socket.jl b/test/socket.jl index df16f515fc57d..c53a3b42bebc3 100644 --- a/test/socket.jl +++ b/test/socket.jl @@ -73,25 +73,49 @@ close(server) @test_throws Base.UVError connect(".invalid",80) -a = UDPSocket() -b = UDPSocket() -bind(a,ip"127.0.0.1",port) -bind(b,ip"127.0.0.1",port+1) +begin + a = UDPSocket() + b = UDPSocket() + bind(a,ip"127.0.0.1",port) + bind(b,ip"127.0.0.1",port+1) -c = Condition() -@async begin - @test bytestring(recv(a)) == "Hello World" - # Issue 6505 + c = Condition() @async begin @test bytestring(recv(a)) == "Hello World" - notify(c) + # Issue 6505 + @async begin + @test bytestring(recv(a)) == "Hello World" + notify(c) + end + send(b,ip"127.0.0.1",port,"Hello World") end send(b,ip"127.0.0.1",port,"Hello World") -end -send(b,ip"127.0.0.1",port,"Hello World") -wait(c) + wait(c) -@test_throws MethodError bind(UDPSocket(),port) + @async begin + @test begin + (addr,data) = recvfrom(a) + addr == ip"127.0.0.1" && bytestring(data) == "Hello World" + end + end + send(b, ip"127.0.0.1",port,"Hello World") -close(a) -close(b) + @test_throws MethodError bind(UDPSocket(),port) + + close(a) + close(b) +end +begin + a = UDPSocket() + b = UDPSocket() + bind(a, ip"::1", uint16(port)) + bind(b, ip"::1", uint16(port+1)) + + @async begin + @test begin + (addr, data) = recvfrom(a) + addr == ip"::1" && bytestring(data) == "Hello World" + end + end + send(b, ip"::1", port, "Hello World") +end From c360a8ad697e78d07a2709885f68ef1f7708e913 Mon Sep 17 00:00:00 2001 From: Isaiah Norton Date: Mon, 5 Jan 2015 23:37:54 -0500 Subject: [PATCH 3/3] NEWS for udp recvfrom [ci skip] --- NEWS.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/NEWS.md b/NEWS.md index 8b4d720ec2037..4c976a14783cc 100644 --- a/NEWS.md +++ b/NEWS.md @@ -145,6 +145,8 @@ Library improvements * A new `Val{T}` type allows one to dispatch on bits-type values ([#9452]) + * Added `recvfrom` to get source address of UDP packets ([#9418]) + Deprecated or removed ---------------------