Skip to content

Commit 9628bc5

Browse files
feat: UdpSocket/UdpStream
1 parent b83cf32 commit 9628bc5

10 files changed

Lines changed: 634 additions & 90 deletions

File tree

‎examples/udp_echo_server.rs‎

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
use wstd::io;
2+
use wstd::net::UdpSocket;
3+
4+
#[wstd::main]
5+
async fn main() -> io::Result<()> {
6+
let socket = UdpSocket::bind("127.0.0.1:8080").await?;
7+
println!("Listening on {}", socket.local_addr()?);
8+
println!("type `nc -u localhost 8080` to create a UDP client");
9+
10+
// A datagram carries at most 64 KiB, and truncating an echo would be a
11+
// bug, so make room for the largest payload we could be sent.
12+
let mut buf = vec![0; 65535];
13+
loop {
14+
let (len, peer) = socket.recv_from(&mut buf).await?;
15+
println!("Received {len} bytes from: {peer}");
16+
// If the echo send fails, we can ignore it: one socket serves every
17+
// peer here, so a failure for one must not end the loop for the rest.
18+
let _ = socket.send_to(&buf[..len], peer).await;
19+
}
20+
}

‎examples/udp_stream_client.rs‎

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
use wstd::io;
2+
use wstd::net::{UdpSocket, UdpStream};
3+
4+
async fn ping(stream: &UdpStream) -> io::Result<()> {
5+
assert_eq!(stream.send(b"ping\n").await?, 5);
6+
7+
let mut reply = [0; 5];
8+
let len = stream.recv(&mut reply).await?;
9+
assert_eq!(&reply[..len], b"pong\n");
10+
11+
Ok(())
12+
}
13+
14+
#[wstd::main]
15+
async fn main() -> io::Result<()> {
16+
let mut args = std::env::args();
17+
18+
let _ = args.next();
19+
20+
let addr = args.next().ok_or_else(|| {
21+
io::Error::new(
22+
std::io::ErrorKind::InvalidInput,
23+
"address argument required",
24+
)
25+
})?;
26+
27+
let stream = UdpStream::connect(addr).await?;
28+
ping(&stream).await?;
29+
30+
let peer = stream.peer_addr()?;
31+
drop(stream);
32+
33+
let local_addr = if peer.is_ipv4() {
34+
"0.0.0.0:0"
35+
} else {
36+
"[::]:0"
37+
};
38+
let stream = UdpSocket::bind(local_addr).await?.connect(peer)?;
39+
ping(&stream).await?;
40+
41+
Ok(())
42+
}

‎src/net/mod.rs‎

Lines changed: 52 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,15 @@
11
//! Async network abstractions.
22
33
use std::io::{self, ErrorKind};
4-
use wasip2::sockets::network::ErrorCode;
4+
use wasip2::sockets::network::{ErrorCode, IpSocketAddress, Ipv4SocketAddress};
55

66
mod tcp_listener;
77
mod tcp_stream;
8+
mod udp;
89

910
pub use tcp_listener::*;
1011
pub use tcp_stream::*;
12+
pub use udp::*;
1113

1214
fn to_io_err(err: ErrorCode) -> io::Error {
1315
match err {
@@ -24,6 +26,55 @@ fn to_io_err(err: ErrorCode) -> io::Error {
2426
ErrorCode::ConnectionReset => ErrorKind::ConnectionReset.into(),
2527
ErrorCode::ConnectionAborted => ErrorKind::ConnectionAborted.into(),
2628
ErrorCode::ConcurrencyConflict => ErrorKind::AlreadyExists.into(),
29+
ErrorCode::DatagramTooLarge => ErrorKind::InvalidInput.into(),
2730
_ => ErrorKind::Other.into(),
2831
}
2932
}
33+
34+
fn sockaddr_from_wasi(addr: IpSocketAddress) -> std::net::SocketAddr {
35+
use wasip2::sockets::network::Ipv6SocketAddress;
36+
match addr {
37+
IpSocketAddress::Ipv4(Ipv4SocketAddress { address, port }) => {
38+
std::net::SocketAddr::V4(std::net::SocketAddrV4::new(
39+
std::net::Ipv4Addr::new(address.0, address.1, address.2, address.3),
40+
port,
41+
))
42+
}
43+
IpSocketAddress::Ipv6(Ipv6SocketAddress {
44+
address,
45+
port,
46+
flow_info,
47+
scope_id,
48+
}) => std::net::SocketAddr::V6(std::net::SocketAddrV6::new(
49+
std::net::Ipv6Addr::new(
50+
address.0, address.1, address.2, address.3, address.4, address.5, address.6,
51+
address.7,
52+
),
53+
port,
54+
flow_info,
55+
scope_id,
56+
)),
57+
}
58+
}
59+
60+
fn sockaddr_to_wasi(addr: std::net::SocketAddr) -> IpSocketAddress {
61+
use wasip2::sockets::network::Ipv6SocketAddress;
62+
match addr {
63+
std::net::SocketAddr::V4(addr) => {
64+
let ip = addr.ip().octets();
65+
IpSocketAddress::Ipv4(Ipv4SocketAddress {
66+
address: (ip[0], ip[1], ip[2], ip[3]),
67+
port: addr.port(),
68+
})
69+
}
70+
std::net::SocketAddr::V6(addr) => {
71+
let ip = addr.ip().segments();
72+
IpSocketAddress::Ipv6(Ipv6SocketAddress {
73+
address: (ip[0], ip[1], ip[2], ip[3], ip[4], ip[5], ip[6], ip[7]),
74+
port: addr.port(),
75+
flow_info: addr.flowinfo(),
76+
scope_id: addr.scope_id(),
77+
})
78+
}
79+
}
80+
}

‎src/net/tcp_listener.rs‎

Lines changed: 2 additions & 51 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,10 @@
1-
use wasip2::sockets::network::Ipv4SocketAddress;
2-
use wasip2::sockets::tcp::{IpAddressFamily, IpSocketAddress, TcpSocket};
1+
use wasip2::sockets::tcp::{IpAddressFamily, TcpSocket};
32

43
use crate::io;
54
use crate::iter::AsyncIterator;
65
use std::net::SocketAddr;
76

8-
use super::{TcpStream, to_io_err};
7+
use super::{TcpStream, sockaddr_from_wasi, sockaddr_to_wasi, to_io_err};
98
use crate::runtime::AsyncPollable;
109

1110
/// A TCP socket server, listening for connections.
@@ -79,51 +78,3 @@ impl<'a> AsyncIterator for Incoming<'a> {
7978
Some(Ok(TcpStream::new(input, output, socket)))
8079
}
8180
}
82-
83-
fn sockaddr_from_wasi(addr: IpSocketAddress) -> std::net::SocketAddr {
84-
use wasip2::sockets::network::Ipv6SocketAddress;
85-
match addr {
86-
IpSocketAddress::Ipv4(Ipv4SocketAddress { address, port }) => {
87-
std::net::SocketAddr::V4(std::net::SocketAddrV4::new(
88-
std::net::Ipv4Addr::new(address.0, address.1, address.2, address.3),
89-
port,
90-
))
91-
}
92-
IpSocketAddress::Ipv6(Ipv6SocketAddress {
93-
address,
94-
port,
95-
flow_info,
96-
scope_id,
97-
}) => std::net::SocketAddr::V6(std::net::SocketAddrV6::new(
98-
std::net::Ipv6Addr::new(
99-
address.0, address.1, address.2, address.3, address.4, address.5, address.6,
100-
address.7,
101-
),
102-
port,
103-
flow_info,
104-
scope_id,
105-
)),
106-
}
107-
}
108-
109-
fn sockaddr_to_wasi(addr: std::net::SocketAddr) -> IpSocketAddress {
110-
use wasip2::sockets::network::Ipv6SocketAddress;
111-
match addr {
112-
std::net::SocketAddr::V4(addr) => {
113-
let ip = addr.ip().octets();
114-
IpSocketAddress::Ipv4(Ipv4SocketAddress {
115-
address: (ip[0], ip[1], ip[2], ip[3]),
116-
port: addr.port(),
117-
})
118-
}
119-
std::net::SocketAddr::V6(addr) => {
120-
let ip = addr.ip().segments();
121-
IpSocketAddress::Ipv6(Ipv6SocketAddress {
122-
address: (ip[0], ip[1], ip[2], ip[3], ip[4], ip[5], ip[6], ip[7]),
123-
port: addr.port(),
124-
flow_info: addr.flowinfo(),
125-
scope_id: addr.scope_id(),
126-
})
127-
}
128-
}
129-
}

0 commit comments

Comments
 (0)