Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add UDP echo example #65

Merged
merged 4 commits into from
May 31, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
book
zig-out/
zig-cache/
.zig-cache/
.DS_Store
.tool-versions
18 changes: 18 additions & 0 deletions book-src/04-03-udp-echo.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
## UDP Echo

Similar to the TCP server example, this program will listen on the specified
IP address and port, but for UDP datagrams this time. If data is received,
it will be echoed back to the sender's address.

Although `std.net` is mostly focused on abstractions for TCP (so far), we can still
make use of socket programming to communicate via UDP.

```zig
{{#include ../src/04-03.zig }}
```

After starting the program, test as follows with `nc`, using the `-u` flag for UDP:

```bash
echo "hello zig" | nc -u localhost <port>
```
1 change: 1 addition & 0 deletions book-src/SUMMARY.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@

- [Listen on unused port TCP/IP](./04-01-tcp-server.md)
- [TCP Client](./04-02-tcp-client.md)
- [UDP Echo](./04-03-udp-echo.md)

- [Web Programming]()

Expand Down
6 changes: 5 additions & 1 deletion build.zig
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,11 @@ fn addExample(b: *std.Build, run_all: *std.Build.Step) !void {

// 04-01 start tcp server, and won't stop so we skip it here
// 04-02 is the server's client.
if (std.mem.eql(u8, "04-01", name) or std.mem.eql(u8, "04-02", name)) {
// 04-03 starts udp listener.
if (std.mem.eql(u8, "04-01", name) or
std.mem.eql(u8, "04-02", name) or
std.mem.eql(u8, "04-03", name))
{
continue;
}
run_all.dependOn(run_step);
Expand Down
60 changes: 60 additions & 0 deletions src/04-03.zig
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
//! Start a UDP echo on an unused port.
//!
//! Test with
//! echo "hello zig" | nc -u localhost <port>

const std = @import("std");
const net = std.net;
const posix = std.posix;
const print = std.debug.print;

pub fn main() !void {
// adjust the ip/port here as needed
const addr = try net.Address.parseIp("127.0.0.1", 32100);

// get a socket and set domain, type and protocol flags
const sock = try posix.socket(
posix.AF.INET,
posix.SOCK.DGRAM,
posix.IPPROTO.UDP,
);

// for completeness, we defer closing the socket. In practice, if this is
// a one-shot program, we could omit this and let the OS do the cleanup
defer posix.close(sock);

try posix.bind(sock, &addr.any, addr.getOsSockLen());

var other_addr: posix.sockaddr = undefined;
var other_addrlen: posix.socklen_t = @sizeOf(posix.sockaddr);

var buf: [1024]u8 = undefined;

print("Listen on {any}...\n", .{addr});

// we did not set the NONBLOCK flag (socket type flag),
// so the program will wait until data is received
const n_recv = try posix.recvfrom(
sock,
buf[0..],
0,
&other_addr,
&other_addrlen,
);
print(
"received {d} byte(s) from {any};\n string: {s}\n",
.{ n_recv, other_addr, buf[0..n_recv] },
);

// we could extract the source address of the received data by
// parsing the other_addr.data field

const n_sent = try posix.sendto(
sock,
buf[0..n_recv],
0,
&other_addr,
other_addrlen,
);
print("echoed {d} byte(s) back\n", .{n_sent});
}
Loading