Created
January 29, 2023 02:48
-
-
Save floitsch/6be678bef8ed9a2870da57d69c4a9844 to your computer and use it in GitHub Desktop.
Example for how to use UDP broadcast to communicate between devices.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* | |
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 |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* | |
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