Skip to content

Instantly share code, notes, and snippets.

@floitsch
Created January 29, 2023 02:48
Show Gist options
  • Save floitsch/6be678bef8ed9a2870da57d69c4a9844 to your computer and use it in GitHub Desktop.
Save floitsch/6be678bef8ed9a2870da57d69c4a9844 to your computer and use it in GitHub Desktop.
Example for how to use UDP broadcast to communicate between devices.
/*
Zero-Clause BSD License
Copyright (C) 2023 Toitware ApS
Permission to use, copy, modify, and/or distribute this software for any
purpose with or without fee is hereby granted.
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
PERFORMANCE OF THIS SOFTWARE.
*/
import monitor
import net
import net.udp
import net.tcp
import writer show Writer
/**
Example, demonstrating how UDP can be used to communicate with multiple
devices on the same network.
This program (the "broadcaster") creates a local TCP server, and then
broadcasts the address of that server on the local network. Other devices
(the "listeners") can then connect to the broadcaster by receiving the
UDP broadcast, and then connecting to the TCP server.
*/
BROADCAST_ADDRESS ::= net.IpAddress.parse "255.255.255.255"
ANNOUNCEMENT_PORT ::= 12345
main:
network/net.Interface? := null
server_task/Task? := null
broadcast_task/Task? := null
error := null
socket/tcp.ServerSocket? := null
try:
network = net.open
// Open a socket for listening.
// We are going to handle requests to it in a separate task.
socket = network.tcp_listen 0
print "listening on $network.address.stringify:$socket.local_address.port"
// We are using two tasks, one for the server, and one for the broadcaster.
// The semaphore is used to wait for both of them to finish and correctly
// clean everything.
done := monitor.Semaphore
server_task = task::
try:
run_server socket
finally:
server_task = null
if broadcast_task: broadcast_task.cancel
critical_do: done.up
broadcast_task = task::
try:
run_broadcaster network socket.local_address.port
finally:
broadcast_task = null
if server_task: server_task.cancel
critical_do: done.up
// Wait for the server and the broadcaster to finish.
2.repeat: done.down
finally:
if socket: socket.close
if network: network.close
if error: throw error
run_server socket/tcp.ServerSocket:
// Here it's also possible to use the http package to
// serve an HTTP server:
// server := http.Server
// server.listen socket:: | request/http.Request writer/http.ResponseWriter |
// <handle request>
//
while true:
accepted := socket.accept
if not accepted: continue
print "accepted connection from $accepted.peer_address"
data := #[]
while chunk := accepted.read:
data += chunk
print "received $data.to_string"
// Send it back. As such we are implementing a simple echo server.
writer := Writer accepted
writer.write data
accepted.close
run_broadcaster network/net.Interface port/int:
payload := """
{
"ip": "$network.address.stringify",
"port": $port
}
""".to_byte_array
datagram := udp.Datagram
payload
net.SocketAddress BROADCAST_ADDRESS ANNOUNCEMENT_PORT
socket := network.udp_open
try:
socket.broadcast = true
while not network.is_closed:
socket.send datagram
sleep --ms=200
finally:
socket.close
/*
Zero-Clause BSD License
Copyright (C) 2023 Toitware ApS
Permission to use, copy, modify, and/or distribute this software for any
purpose with or without fee is hereby granted.
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
PERFORMANCE OF THIS SOFTWARE.
*/
import encoding.json
import net
import writer show Writer
main:
network := net.open
udp_socket := network.udp_open --port=12345
datagram := udp_socket.receive
data := datagram.data
decoded := json.decode data
udp_socket.close
// We now know the TCP address and port. Use it to connect with TCP.
tcp_socket := network.tcp_connect decoded["ip"] decoded["port"]
writer := Writer tcp_socket
writer.write "Hello, world!"
writer.close_write
response := #[]
while chunk := tcp_socket.read:
response += chunk
print "Received response: $response.to_string"
tcp_socket.close
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment