SimulationShock
  • Home
  • About
  • Experience
  • Projects
  • bLog
  • Contact

© 2026 Simulation Shock

  • LinkedIn
  • Github

On this page

  • ▸Introduction
  • ▸Communication Models: Who Talks to Whom
    • ▸Client–Server
      • ▸Website Example: A browser loading a webpage
      • ▸REST API Example: Fetching user data from an API
      • ▸Multiplayer Game Example: Networked multiplayer game or VR experience
    • ▸Peer-to-Peer
      • ▸Real-Time Communication Example: WebRTC (voice/video/data channels)
  • ▸Getting your Message Across: How the signal travels through space using TCP/IP
    • ▸IPv4
    • ▸CIDR
    • ▸Subnets
      • ▸2 Subnet Example
      • ▸4 Subnet Example
      • ▸8 Subnet Example
    • ▸Internet Service Providers
    • ▸NAT, CGNAT, and Double NAT
    • ▸Default Gateway
    • ▸Port Numbers
    • ▸TCP/UDP
    • ▸Port Forwarding
    • ▸Signaling, STUN, and TURN Servers
    • ▸Domain Names
    • ▸DNS Records
    • ▸Reverse Proxy
    • ▸TLS/HTTPS
    • ▸Multicast DNS (mDNS)
    • ▸Virtual Private Server (VPS)
    • ▸Self-Hosting
    • ▸IPv6
    • ▸Security / Firewalls
    • ▸Physical Bottlenecks
      • ▸Wired Ethernet Connection Speeds
      • ▸Wi-Fi Connection Speeds
  • ▸Poke Around and Find Out: Essential tools for probing
    • ▸To test your download/upload speed to the internet
    • ▸To test your download/upload between two machines on your network.
    • ▸curl
    • ▸ip
    • ▸ping
    • ▸traceroute
    • ▸netcat
    • ▸netmap
    • ▸tcpdump
    • ▸netstat
    • ▸dig
  • ▸The Protocols of Conversation: Writing & Reading Messages
    • ▸Data Serialization: Packaging Your Messages
    • ▸JSON
    • ▸Protobuf
    • ▸HTTP
    • ▸REST
      • ▸Client making a Request to a REST API
        • ▸Server Responding to a REST API Request
    • ▸GraphQL
    • ▸WebSockets
    • ▸OSC
    • ▸WebRTC
    • ▸gRPC
    • ▸SSE
      • ▸Example: Webpage and SSE Endpoint
    • ▸Webhook
    • ▸MQTT

On this page

  • ▸Introduction
  • ▸Communication Models: Who Talks to Whom
    • ▸Client–Server
      • ▸Website Example: A browser loading a webpage
      • ▸REST API Example: Fetching user data from an API
      • ▸Multiplayer Game Example: Networked multiplayer game or VR experience
    • ▸Peer-to-Peer
      • ▸Real-Time Communication Example: WebRTC (voice/video/data channels)
  • ▸Getting your Message Across: How the signal travels through space using TCP/IP
    • ▸IPv4
    • ▸CIDR
    • ▸Subnets
      • ▸2 Subnet Example
      • ▸4 Subnet Example
      • ▸8 Subnet Example
    • ▸Internet Service Providers
    • ▸NAT, CGNAT, and Double NAT
    • ▸Default Gateway
    • ▸Port Numbers
    • ▸TCP/UDP
    • ▸Port Forwarding
    • ▸Signaling, STUN, and TURN Servers
    • ▸Domain Names
    • ▸DNS Records
    • ▸Reverse Proxy
    • ▸TLS/HTTPS
    • ▸Multicast DNS (mDNS)
    • ▸Virtual Private Server (VPS)
    • ▸Self-Hosting
    • ▸IPv6
    • ▸Security / Firewalls
    • ▸Physical Bottlenecks
      • ▸Wired Ethernet Connection Speeds
      • ▸Wi-Fi Connection Speeds
  • ▸Poke Around and Find Out: Essential tools for probing
    • ▸To test your download/upload speed to the internet
    • ▸To test your download/upload between two machines on your network.
    • ▸curl
    • ▸ip
    • ▸ping
    • ▸traceroute
    • ▸netcat
    • ▸netmap
    • ▸tcpdump
    • ▸netstat
    • ▸dig
  • ▸The Protocols of Conversation: Writing & Reading Messages
    • ▸Data Serialization: Packaging Your Messages
    • ▸JSON
    • ▸Protobuf
    • ▸HTTP
    • ▸REST
      • ▸Client making a Request to a REST API
        • ▸Server Responding to a REST API Request
    • ▸GraphQL
    • ▸WebSockets
    • ▸OSC
    • ▸WebRTC
    • ▸gRPC
    • ▸SSE
      • ▸Example: Webpage and SSE Endpoint
    • ▸Webhook
    • ▸MQTT
Cover image for Networking Principles for DIY and Interactive Systems
networking

Networking Principles for DIY and Interactive Systems

Exploring networks and protocols for creative practice

January 24, 2026·Updated June 16, 2026
Loading...

Introduction

In my experience as someone who mostly started off working with Digital Content Creation tools like 3dsMax, Substance Painter and game engines like Unreal, Unity. I wanted to branch out and learn more about networking. During the XR Jockey prototype I struggled to link communication between unity and ableton. Eventually I was able to demonstrate my vision after much trial and error. After much more hands on experience troubleshooting multiplayer games, web services, databases, and linux servers. I present a broad compilation that goes way beyond my troubles in the XR Jockey Project and discuss what has been useful in bridging the gaps between creative software development and the practical plumbing required to wrangle your own distributed network of machines. This knowledge has been useful for modifying Websites, REST APIs, Real-time Telemetry, OSC/MIDI Orchestration, Multiplayer Backends, Observability Dashboards, Camera Feeds, and much more. Interactive Systems can be anything you want if you do it yourself.

Communication Models: Who Talks to Whom

A Host is any device (e.g. a smart device, laptop, or sensor) connected to a network that can send or receive data and is identified by an IP address. It can take on different roles at any given time.

What Role is your device taking in a conversation?
Is it a Listener, waiting for incoming connections?
Is it an Initiator, reaching out to known endpoints?
Is it a Coordinator, helping other machines discover, route, and synchronize?

Most networked applications can be understood through two foundational models: Client–Server and Peer-to-Peer. Each makes different tradeoffs around control, latency, reliability, and complexity.

Client–Server

In a client-server model we have hosts that are a Server and hosts that are a Client.
Servers listen for incoming connections on known addresses and ports.
Clients initiate requests and wait for responses.

Website Example: A browser loading a webpage

text
Client: Web browser (Chrome, Firefox, Safari)
Server: Web server (e.g. Nginx, Apache, Node.js)
 
Flow:
1. The browser (client) initiates an HTTP request to a server’s URL.
2. The server listens on a known port (80 / 443).
3. The server responds with HTML, CSS, JavaScript, and assets.
4. The client renders the page locally.

REST API Example: Fetching user data from an API

text
Client: Frontend app, mobile app, or script
Server: API service + database
 
Flow:
1. The client sends an HTTP request (GET / POST / PUT / DELETE).
2. The server validates the request (auth, permissions, schema).
3. The server performs logic (read/write database, compute response).
4. The server returns structured data (usually JSON).

Multiplayer Game Example: Networked multiplayer game or VR experience

text
Client: Player’s game instance (Unity / Unreal)
Server: Dedicated game server (authoritative)
 
Flow:
1. Clients send inputs (movement, actions).
2. The server simulates the authoritative world state.
3. The server resolves conflicts (collisions, scoring, physics).
4. The server broadcasts state updates to all clients.
5. Clients render the result locally.

Peer-to-Peer

In a peer-to-peer model each device can act as both a client and a server. Both devices communicate directly instead of communicating through a middleman.

Communication happens directly between peers with minimal central coordination.

Real-Time Communication Example: WebRTC (voice/video/data channels)

code
Client / Server: Every peer plays both roles
Supporting servers: Signalling, STUN / TURN (discovery + fallback)

Flow:
1. Peers discover each other via a signaling service.
2. Connection details are exchanged.
3. Media or data flows directly between peers.
4. Servers drop out once the connection is established.

Whether it's client-server or peer-to-peer the same basic plumbing moves your messages from one host to another. Understanding how IP addresses, ports, and transport protocols carry your signals should be explored.

Getting your Message Across: How the signal travels through space using TCP/IP

Networks and protocols are just ways of moving data efficiently and providing the infrastructure and language required for others to do so as well. Whether your intentions are to support or provoke, machines don't care about what you say but how you say it. With just a few pieces of information and a few humble steps, you can demystify these systems and begin your journey into creative connectivity.

Introductory computer networking courses will often take a bottom-up approach by first discussing the mechanisms that move signals through space. Alternatively, the top-down approach begins where user interaction with applications actually occurs. Arguably the most impactful networking technology has been the TCP/IP model used for internet communication.

text
TCP/IP STACK
---------------------------
  APPLICATION LAYER        ← The story you want to tell
---------------------------
  TRANSPORT LAYER          ← The envelope keeping it safe
---------------------------
  INTERNET LAYER           ← The post office
---------------------------
  NETWORK ACCESS LAYER     ← The streets and bridges
---------------------------

IPv4

An Internet Protocol Version 4 (IPv4) Address can be any number between 0-4,294,967,295 roughly speaking (i.e 32 bits). We just choose to represent these numbers more conveniently for readability. So for IPv4 we group them into 4 octets separated by .

So the representations end up ranging from ~0.0.0.0-255.255.255.255 with some special cases.

The following IPv4 Addresses are reserved for special purposes and used for
The wildcard address 0.0.0.0 - “When setting up a web service on this machine/listen on all available network interfaces"
The localhost address 127.0.0.1 - "When communicating with another web service on this machine"
The broadcast address 255.255.255.255 - "When communicating with every machine on this network"

The following CIDR blocks of IPs are reserved for private networks and are not routable on the public internet. These address ranges are commonly used inside home, office, and data center networks.
10.0.0.0/8 = 10.0.0.0 - 10.255.255.255
192.168.0.0/16 = 192.168.0.0 - 192.168.255.255
172.16.0.0/12 = 172.16.0.0 - 172.31.255.255

CIDR

Classless Inter-Domain Routing (CIDR) notation defines a network’s starting address, size, and range.

  • The Network address Identifies the network itself and represents all addresses within the block.
  • The Subnet mask (prefix length) determines how many IP addresses belong to the network.

IPv4 Address block (CIDR block) Example

text
CIDR block 192.168.0.0/24
 
The 192.168.0.0 represents the network address
The /24 represents the subnet mask (prefix length) and translates to 11111111 11111111 11111111 00000000 or 255.255.255.0
The network occupies IPs ranging from [192.168.0.0 - 192.168.0.255]
The broadcast address is typically the max value, in this case 192.168.0.255
Devices connected to this network will be assigned an address between 192.168.0.1 - 192.168.0.254

CIDR is a generalization of classful routing, which is no longer practiced.

Before CIDR, IPv4 networks were divided into 3 fixed-size classes.
Class A: Large, /8 with ~16 million Hosts
Class B: Medium /16 with ~65 thousand Hosts
Class C: Small /24 with ~254 Hosts

With the adoption of CIDR, networks could be logically divided into smaller sub-networks.

Subnets

A Network can be segmented into several subnets to manage congestion, security, and organization. When 2 Machines are on separate subnets they cannot communicate directly and must go through the Default Gateway.

To understand why adjusting the prefix length (i.e subnet mask) changes the amount of hosts involves familiarity with the binary number system and bitwise math.

The Network Portion of 192.168.0.0/24 is 192.168.0.X while the Host portion is the final digit.

We have 24 bits reserved for the Network Address and 8 bits reserved for the Host Address.

Max of 8-bit binary number 11111111 = 1 + 2 + 4 + 8 + 16 + 32 + 64 + 128 = 255

We have 254 possible host addresses for the 192.168.0.0/24 subnet because .0 and .255 are reserved for the network address, and broadcast address.

We can split this Network into 2 Subnets, 192.168.0.0/25 and 192.168.0.128/25

2 Subnet Example

We have 25 bits reserved for the Network Address and 7 bits reserved for the Host Address.

Max of 7-bit 1111111 = 1 + 2 + 4 + 8 + 16 + 32 + 64 = 127

We have 126 possible host addresses for the 192.168.0.0/25 subnet because .0 and .127 are reserved for the network address, and broadcast address.

We have 126 possible host addresses for the 192.168.0.128/25 subnet because .128 and .255 are reserved for the network address, and broadcast address.

text
| Subnet | CIDR Range | Usable IP Range | Broadcast |
| Subnet A | 192.168.0.0/25 | .1 to .126 | .127 |
| Subnet B | 192.168.0.128/25 | .129 to .254 | .255 |

4 Subnet Example

text
| Subnet | CIDR Range | Usable IP Range | Broadcast |
| Subnet 1 | 192.168.0.0/26 | .1 to .62 | .63 |
| Subnet 2 | 192.168.0.64/26 | .65 to .126 | .127 |
| Subnet 3 | 192.168.0.128/26 | .129 to .190 | .191 |
| Subnet 4 | 192.168.0.192/26 | .193 to .254 | .255 |

8 Subnet Example

text
| Subnet | CIDR Range | Usable IP Range | Broadcast |
| Subnet 1 | 192.168.0.0/27 | .1 to .30 | .31 |
| Subnet 2 | 192.168.0.32/27 | .33 to .62 | .63 |
| Subnet 3 | 192.168.0.64/27 | .65 to .94 | .95 |
| Subnet 4 | 192.168.0.96/27 | .97 to .126 | .127 |
| Subnet 5 | 192.168.0.128/27 | .129 to .158 | .159 |
| Subnet 6 | 192.168.0.160/27 | .161 to .190 | .191 |
| Subnet 7 | 192.168.0.192/27 | .193 to .222 | .223 |
| Subnet 8 | 192.168.0.224/27 | .225 to .254 | .255 |

Internet Service Providers

As time went on the ~4.3 billion possible addresses became scarce and another abstraction was needed to support many more devices than that by Network Address Translation (NAT). NAT is a mechanism that often resolves your public internet traffic to local devices.

Internet Service Providers (ISP) typically provide a 4-in-1 box that acts as a modem, router, switch, and wireless access point. Each of these can be separate devices and not all of them are required depending on whats being built.

The simplified roles of each are as follows:

  • Modem translates between your ISP’s network and your local network.
  • Router decides where traffic should go.
  • Switch provides wired connectivity.
  • Wireless Access Point provides wireless connectivity.

NAT, CGNAT, and Double NAT

Your ISP will also lease your 4-in-1 box with a public IPv4 address, but because IPv4 addresses are running out, they often use Carrier-Grade NAT (CGNAT), and thus creates a Double NAT scenario. This means your connection may actually share a public IP with other customers, with the ISP handling the CGNAT behind the scenes - separate from the NAT occurring on you're Local Area Network (LAN).

To the world outside your LAN, all traffic traveling out will share this public IPv4 address.
To the world inside your LAN, the router uses Network Address Translation (NAT) to forward incoming traffic to the correct device using its private IP address.

When connecting a new device to your network it will typically be assigned a Private IP from the CIDR Block 192.168.0.0/24

The router will lease a semi-permanent Private IPv4 Address by Dynamic Host Configuration Protocol (DHCP), "a network management protocol that automatically assigns IP addresses, subnet masks, default gateways, and DNS servers to devices, eliminating manual configuration".

Default Gateway

When a device wants to communicate with another device, it includes its own IP address as the source so the receiver knows where to send a response. But what if the destination IP address is outside your LAN? Any traffic for unknown or non-local IP addresses is sent to the default gateway, which is usually your router’s IP address. The router maintains a routing table and continuously updates it, learning the best paths to forward your messages toward their destinations.

The IP Address of your default gateway for example might be 192.168.0.1 You can access and configure more settings by navigating to http://192.168.0.1 in you're web browser. The Provided ISP's 4-in-1 will be limited compared to using a dedicated router for this.

IP addresses are logical and flexible. They can change depending on the network you connect to and are used to route traffic across networks, helping data find its way from one network to another. At some point this logical address needs to resolve to a physical address. The MAC Address is a permanent and unique hardware identifier assigned by a manufacturer that is bound to a device.

The Address Resolution Protocol (ARP) acts as the bridge between these two systems by broadcasting a question on the local network asking, "Who has this specific IP address? Another device needs to communicate with it”.

Traffic can potentially flow from network to network until it reaches its destination device, but how does it know which app or service to go to?

Port Numbers

A Port Number can be between 0 – 65535 and is also used when configuring communication between devices.

text
Well-Known Ports (0–1023) are reserved for standard services and protocols.
80 – HTTP: standard web traffic, used by browsers or web-based controllers
53 – DNS: translating names like example.com into IP addresses
Registered Ports (1024–49151) are assigned for user or vendor applications.
3306 – MySQL: database communication
1883 – MQTT: sending messages in real time between devices or apps
Ephemeral Ports (49152–65535) are assigned temporarily by the OS for outgoing connections.
Your OS may use port 49153 to request data from another device fulfilling request at their IP Address on port 80.

Most apps and services are accessed by addressing a specific IP address, domain, port, and protocol.
URL schemes commonly resolve to default ports.

text
http://127.0.0.1 = http://127.0.0.1:80
ws://127.0.0.1 = ws://127.0.0.1:80
https://127.0.0.1 = https://127.0.0.1:443
wss://127.0.0.1 = wss://127.0.0.1:443
mqtt://127.0.0.1 = mqtt://127.0.0.1:1883
rtsp://127.0.0.1 = rtsp://127.0.0.1:554

When the port numbers are not the default for that protocol, you must specify which one to use.

text
http://127.0.0.1:8080
rtsp://127.0.0.1:1554

TCP/UDP

Each application or Service has different communication requirements. Maybe they need to prioritize speed and low latency or guarantee reliability and correctness. These tradeoffs are handled by transport-layer protocols like Transmission Control Protocol (TCP) and User Datagram Protocol (UDP).

A port number is always paired with a transport-layer protocol when setting up a service or communicating with a service.

text
192.168.0.10:443/TCP and 192.168.0.10:443/UDP are two different endpoints

Port Forwarding

Port Forwarding is often mistaken as a mechanism of the Firewall but they serve different purposes.
When you're running apps or services behind NAT and must make these available to the outside world, then we must specify what internal machine should receive traffic on a specific port number. This way the router knows where to forward incoming requests.

Even for your ISPs provided router (4-in-1 Box) you can navigate to the Default Gateway IP (e.g http://192.168.0.1) and set this up in your Routers Admin Dashboard. You will also need to look into how to make exceptions inside the Operating Systems firewall of your device.

Your ISP may also block certain ports beyond your control.

Signaling, STUN, and TURN Servers

Signaling, STUN, and TURN servers play coordinating roles in facilitating peer-to-peer (P2P) communication. They support popular web applications like anonymous real-time chat platforms (e.g. Monkey, Omegle), and live-streamed interactive apps (Pixel Streaming).

Signaling is responsible for the initial coordination between peers, exchanging connection metadata such as session descriptions and network candidates using a client–server protocol (e.g. HTTP or WebSockets). A signaling server often coordinates with STUN and TURN servers to help peers connect across NATs and firewalls.

STUN (Session Traversal Utilities for NAT) helps peers discover their public-facing IP address and port so they can attempt a direct P2P connection. Public STUN servers are freely available online (e.g stun.l.google.com:19302).

TURN (Traversal Using Relays around NAT) acts as a fallback relay server when direct peer-to-peer communication fails. TURN servers can be expensive to operate because all media traffic is relayed through the server, which increases bandwidth and resource usage.

CoTurn is an open-source solution that allows you to self-host your own STUN and TURN servers, giving you full control over peer-to-peer connection handling, authentication, and bandwidth usage.

Domain Names

Instead of memorizing numerical addresses of our favorite websites and services, the Domain Name Service (DNS) is used to resolve human-friendly names into their actual IP Addresses. Therefore the application can depend on a stable name while its physical servers or IP Address change.

You pay yearly to have domain registrars like Porkbun, Namecheap, or Cloudflare secure usage for you and must renew to keep ownership. Top-Level Domains (TLDs) are the last part of the domain like (.com, .ai, .games, .xyz). TLDs don’t change how anything works, but they absolutely change how your project is perceived and how much you pay per year.

While many .com domains are a minimum $20/year after the first year. By contrast, the set of 1.111B class domains are .xyz domains made up of 1.111 billion possible 6-digit, 7-digit, 8-digit, and 9-digit numeric combinations, between ‘000000.xyz’ through ‘999999999.xyz only cost $1/year.

However its cheap price has attracted malicious actors like botnets and spammers so other network firewalls may prefer not to deal with traffic originating from .xyz TLDs.

DNS Records

DNS setup for your domain requires logging into your domain registrar's dashboard.

  • creating an A Record (Address Record) maps your domain (e.g simulationshock.com) to an IPv4 Address (e.g 104.21.88.214).
  • creating an AAAA Record maps your domain to an IPv6 Address.
  • creating a CNAME Record redirects a subdomain (e.g blog.simulationshock.com to simulationshock.com)

DNS Records take time to propagate across the internet before your domain becomes fully accessible. This delay can range from a few minutes to a few hours. A website like https://www.whatsmydns.net/ will test DNS servers worldwide. There are thousands of DNS servers, and by default your system usually uses those assigned by your ISP, device, or web browser.

Common Public DNS Servers

text
Google = 8.8.8.8 
Cloudflare = 1.1.1.1
Quad9 = 9.9.9.9

Reverse Proxy

A Reverse Proxy is a server (e.g Nginx Proxy Manager) that sits in front of your other servers so that traffic can pass through a single point of entry and have their requests forwarded to the appropriate destination. They are commonly used to enhance security by consolidating port access and provide another layer of obfuscation through encryption by providing a mechanism to easily enable Transport Layer Security (TLS) for HTTPS.

A Host taking the role of a server providing multiple services on numerous ports (e.g 3000/tcp, 8080/tcp, 8888/tcp) may require a client to request data from multiple endpoints (e.g PUBLIC_IP:3000, PUBLIC_IP:8080, and PUBLIC_IP:8888).

Instead the Host can run Nginx Proxy Manager (NPM) at PUBLIC_IP:443 and use a domain like 123456789.xyz with an DNS Address Record pointing 123456789.xyz -> PUBLIC_IP. I recommend NPM because it provides a simple WebUI to easily support HTTPS and Websockets.

Reverse proxies commonly route traffic using:

  • Host-based routing (subdomains)
    • https://webapi01.123456789.xyz:443 to an internal host at http://localhost:3000
    • https://webapi02.123456789.xyz:443 to an internal host at http://localhost:8080
    • https://webapi03.123456789.xyz:443 to an internal host at http://localhost:8888
  • Path-based routing (/webapi01, /webapi02, /webapi03)
    • https://123456789.xyz:443/webapi01 to an internal host at http://localhost:3000
    • https://123456789.xyz:443/webapi02 to an internal host at http://localhost:8080
    • https://123456789.xyz:443/webapi03 to an internal host at http://localhost:8888

TLS/HTTPS

HTTP is an insecure protocol where data is transported in plain text.
HTTPS is HTTP Secure where data is transported with TLS Encryption Protection.

Although HTTPS adds extra complexity it becomes very necessary if your services interface with public connections. To enable HTTPS, a server presents a TLS certificate issued by a trusted Certificate Authority (CA). Let’s Encrypt is a free, automated CA commonly used to issue certificates for web services.

A single certificate can be issued for multiple subdomains using a wildcard (*).

The certificate for *.123456789.xyz covers

  • webapi01.123456789.xyz
  • webapi02.123456789.xyz
  • webapi03.123456789.xyz

Browsers require HTTPS Certificates for applications trying to access device capabilities like geolocation, microphone, camera, WebXR, etc.

By default traffic is only encrypted using TLS outside the Reverse Proxy. As it forwards requests to the destination_ip:port inside the network its coordinating with, the services it communicates with are often still just running as HTTP (insecure, plain text).

Multicast DNS (mDNS)

While Global DNS relies on external servers to resolve domains to IP Addresses, we don't always need broader internet connectivity. mDNS is a protocol that is also commonly used without knowing it. This is how our smart-devices are able to detect each other and advertise services simply because they exist on the same LAN.

The Hostname is a human-readable label assigned to your device during setup and can be changed. When discovering devices on your network you will often identify them by their hostname (e.g YardCam, Office-PC). Recall by default, most LANs use DHCP to lease out semi-permanent IPs for your newly connected devices. Similarly to how domains work by providing a stable name to use, mDNS does this with zero configuration. While ARP asks "Who has this IP?", mDNS asks "Who has this Name?"

By appending .local to a hostname you can use mDNS to find the device.

You can use rtsp://user:[email protected]:554/cam/realmonitor?channel=1&subtype=1 to access your camera stream.

You can use http://Office-PC.local to access a web server.

Virtual Private Server (VPS)

When purchasing a domain for the first year you will also be given discounted server rates that can often be more costly and overpowered then what you actually need. You can also just rent a Virtual Private Server (VPS) which will provide you with a Public IP Address and an Operating System to accomplish your goals. Because Linux operating systems run efficiently as software, this often translates to lower hosting costs. Learning how to navigate and manage a system remotely through the terminal becomes a valuable skill when working this way.

Self-Hosting

You can also go down the route of self-hosting but comes with more risk and complications. Recall that the devices inside your LAN will often be behind 2 NATs, and your DNS Record requires your domain to have a Static IP Address. A website like https://whatismyipaddress.com/ will show your Public IP Address.

If it is the same as the Wide-Area-Network (WAN) IP displayed in your router gateway (http://192.168.0.1) then you are not behind a CGNAT. Recall that your ISP will lease you a semi-permanent IPv4 Address. They charge you more $$$ for a Permanent IPv4 Address. Instead, you can setup Dynamic DNS (DDNS) to automatically update your Domains DNS Record when it detects a new Public IPv4 Address has been issued. For example, if you paid $5 to own the domain name 123456789.xyz for 5 years then you can configure DDNS in your router to monitor your WAN IP and propagate changes to your A Record to all DNS servers worldwide. Now you have a stable name to communicate with your DIY Interactive System anywhere in the world.

If it's not the same as the WAN IP displayed in your router gateway (http://192.168.0.1) then you are behind a CGNAT and have to rely on tunneling or relay methods like a Virtual Private Network (VPN) using software like Tailscale, Wireguard, or OpenVPN. But if your ISP and router supports IPv6, your device may have a globally routable address, allowing it to be reached directly from the internet without relying on NAT or tunneling.

IPv6

Internet Protocol Version 4 (IPv4) is still very common but the Next-Generation Internet Protocol known as IPv6 is increasingly being used.

IPv6 Addresses are simply a number between 0 and 340,282,366,920,938,463,463,374,607,431,768,211,455 (i.e 128 bits).
Again We just choose to represent these numbers more conveniently for readability by using 8 groups of 4 hexadecimal digits, separated by colons (:).

A few more shortcuts for condensing IPv6 Addresses are also used.

  1. Leading zeros can be dropped in any 16-bit group.
    • 2001:0db8:0000:0000:0000:0000:0000:1a2b becomes 2001:db8:0:0:0:0:0:1a2b
  2. Consecutive groups of zeros can be replaced with double colon (::), but only once per address
    • 2001:0db8:0000:0000:0000:0000:0000:1a2b becomes 2001:db8::1a2b
  3. Single zero groups don’t require the full four digits
    • 000a becomes a
    • 0001 becomes 1

IPv6 Address block (CIDR block) Example

text
CIDR block 2001:db8:abcd:0012::/64
 
The 2001:db8:abcd:0012:: represents the network address
The /64 represents the prefix length, meaning the first 64 bits identify the network
The remaining 64 bits are used for device addresses
The network occupies IPs ranging from [2001:db8:abcd:0012:0000:0000:0000:0000 - 2001:db8:abcd:0012:ffff:ffff:ffff:ffff]

IPv6 also provides Unique Local Addresses (ULA) similar to IPv4 Private Addresses (e.g 192.168.0.0/16)
The full ULA range is fc00::/7, but in practice only fd00::/8 is used. The IPv6 CIDR Block fd00::/8 provides IP Addresses from fd00:0000:0000:0000:0000:0000:0000:0000 to fdff:ffff:ffff:ffff:ffff:ffff:ffff:ffff which is more than you will probably ever need.

The following IPv6 Addresses are reserved for special purposes and used for
The wildcard address :: - “When setting up a web service on this machine”
The localhost address ::1 - "When communicating with another web service on this machine"

Security / Firewalls

Another barrier that can interfere with communication is the Firewall. Firewalls exist on the Default Gateway or Router. They are by default configured to be very protective and block network traffic based on IP addresses, port numbers, and protocols. You definitely want this behavior but you may need to make exceptions based on your goals.

To access your router’s firewall, your ISP-provided 4-in-1 box may have limited functionality. You can navigate to the default gateway IP (e.g http://192.168.0.1) in a web browser to explore its web dashboard and see what options are available.

In addition to local firewalls, firewalls can also exist upstream, such as those managed by an ISP or a network administrator. These firewalls filter traffic before it even reaches your network.

Firewalls can also exist on the operating system.

Windows will by default block all traffic in and out

Windows Defender Firewall > Allow an app through Windows Firewall

Linux will by default for most distributions allow all traffic in and out unless configured otherwise.

Installing the Uncomplicated Firewall (ufw) package will quickly setup a firewall and allow you to Block/Allow traffic based on certain rules.

Physical Bottlenecks

text
---------------------------
  NETWORK ACCESS LAYER     ← The streets and bridges
---------------------------

Most internet speeds are now approaching 100 Mbps minimum for many users, but the actual speed you experience depends on your networking equipment. Devices like Network Interface Cards (NICs), routers, switches, modems, access points, and even Ethernet cables are each rated for a maximum speed

Your data transfer speed will always be limited to the slowest component in the path.

They will frequently fall into the following categories:

  • Fast Ethernet ~ 100 Mbps ~ Older home and office networks
  • Gigabit Ethernet ~ 1 Gbps ~ (1000 Mbps) Standard for modern home and business devices
  • 10 Gigabit Ethernet ~ 10 Gbps ~ High-performance workstations, servers, and enterprise networks
  • Beyond 10G (25G / 40G / 100G) ~ Mostly in data centers, not common in consumer devices

Wired Ethernet Connection Speeds

text
| Category | Max Data Rate | Max Distance
| Cat 5 | 100 Mbps | 100m |
| Cat 5e | 1 Gbps | 100m | 
| Cat 6 | 1 Gbps (10 Gbps*) | 100m (55m*) |
| Cat 6a | 10 Gbps | 100m |

Wi-Fi Connection Speeds

text
| Generation | Technical Name | Common Frequency | Expected Data Rate | Typical Range
| WiFi 4 | 802.11n | 2.4 GHz | 20–50 Mbps | ~45m |
| WiFi 5 | 802.11ac | 5 GHz | 200–500 Mbps | ~15m | 
| WiFi 6 | 802.11ax | 2.4 / 5 GHz | 600–900 Mbps | ~15m–45m |
| WiFi 6E/7 | 802.11be | 6 GHz | 1–2 Gbps | ~9m |

Poke Around and Find Out: Essential tools for probing

The TCP/IP Networking Model provides all the infrastructure needed to communicate from one host to another host. Everything underneath usually just works but it helps to know what to look for and where to look when it doesn't. We have to be able to touch, see, and listen to what’s actually happening. That begins by checking what’s reachable, what’s responding, and what’s moving. This often occurs on a logical plane rather than physically checking if a wire is disconnected. Your lens and hands are a set of network utilities that allow you to verify addresses, messages, and delivery.

text
Application Layer: Is my data formatted correctly?
Transport Layer: Is the right Port Number being used?
Security: Is the Network or OS Firewall blocking the Port Number or Protocol (e.g udp/tcp)
Internet Layer: Is the IP Address actually the one I need?
Network Access Layer: Is the Host on a different Subnet?

I'll be using Linux Terminal Commands to demonstrate this for simplicity, but these tools can be found for your OS.

To test your download/upload speed to the internet

Navigate to https://www.speedtest.net/

To test your download/upload between two machines on your network.

Install and run iperf3 on machine as a server

bash
iperf3 -s

Install and run iperf3 on another machine as a client

bash
iperf3 -c SERVER_IP

curl

send requests to web servers and receive responses, acting as a "command-line web browser" for data.

bash
curl -X POST http://localhost:5000/webhook \
  -H "Content-Type: application/json" \
  -d '{"type":"intensity","value":0.8}'

ip

see your device’s assigned address, network assignment, and default gateway.

bash
ip a

ping

check if a host is reachable

bash
ping simulationshock.com -c 4

traceroute

see the path your packets take

bash
traceroute simulationshock.com

netcat

check if a specific port is open

bash
nc -vz simulationshock.com 443

netmap

discover which devices are on your network

bash
nmap -sn 192.168.1.0/24

tcpdump

capture and inspect the actual traffic

bash
tcpdump -i <network interface>

netstat

check what ports are "listening" on your machine.

bash
netstat -tulpn

dig

query a DNS Record for a domain at a specific nameserver

bash
dig @8.8.8.8 simulationshock.com A

The Protocols of Conversation: Writing & Reading Messages

Data Serialization: Packaging Your Messages

What you actually end up designing with are higher-level, more human-friendly protocols that shape and send messages on your behalf. Machines don't care what you say but how you say it. A common schema is agreed upon so that both sender and receiver can speak the same language. Serialization is the process of translating your data into a format that is ready to be transmitted. Deserialization occurs once the message reaches its destination and is translated back to its original form.

JSON

JSON is more common in modern web applications, APIs, and real-time communication systems.

Messages exchanged between clients and servers are frequently structured in the Javascript Object Notation (JSON) format - a simple text format for representing structured data.

Key points:

  • Objects are wrapped in and contain key-value pairs.
  • Arrays are wrapped in [] and contain ordered values.
  • Keys are always strings ("key").
  • Values can be:
    • String: "hello"
    • Number: 42 or 3.14
    • Boolean: true or false
    • Null: null
    • Object: { "nested": "value" }
    • Array: [1, 2, 3]
json
{
  "sensor_id": "living_room_01",       // string
  "data": {                             // object
    "temperature": 22.5,               // number
    "humidity": 45,                     // number
    "unit": "celsius",                  // string
    "status": null                      // null
  },
  "active": true,                       // boolean
  "tags": ["living", "climate", "sensor"],  // array of strings
  "alerts": [                           // array of objects
    { "type": "temperature", "level": "high" },
    { "type": "humidity", "level": "low" }
  ]
}

Protobuf

Protocol Buffers (Protobuf) is a binary serialization format used to exchange structured data efficiently between clients and servers. Data is defined in a .proto schema, which specifies field names, types, and their numeric tags.

Key points:

  • Schema required, all messages must be defined in advance.
  • Fields have types, string, int32/int64, bool, float/double, nested messages, repeated (arrays).
  • Compact and fast, serialized data is binary.
  • Supports nested messages and repeated fields.
proto
syntax = "proto3";
 
message SensorData {
  float temperature = 1;
  int32 humidity = 2;
  string unit = 3;
  google.protobuf.StringValue status = 4; // optional null equivalent
}
 
message Alert {
  string type = 1;
  string level = 2;
}
 
message Sensor {
  string sensor_id = 1;
  SensorData data = 2;
  bool active = 3;
  repeated string tags = 4;
  repeated Alert alerts = 5;
}
proto
sensor_id: "living_room_01"
data {
  temperature: 22.5
  humidity: 45
  unit: "celsius"
  status {}
}
active: true
tags: "living" "climate" "sensor"
alerts {
  type: "temperature"
  level: "high"
}
alerts {
  type: "humidity"
  level: "low"
}

HTTP

Hypertext Transfer Protocol (HTTP) was originally designed to retrieve documents, not data APIs. Early websites were written in Hypertext Markup Language (HTML) and served as static pages that browsers could request and render. As the web evolved, sites became more interactive by combining HTML to define structure and content, CSS to control layout and visual presentation, and JavaScript to add behavior like responding to user input. In practice, web developers use frameworks like React and platforms such as WordPress to provide higher-level abstractions for building and maintaining content-driven websites without starting from scratch.

Early websites were static, meaning the server simply returned files exactly as they existed on disk. When a browser requested a page, the server did no computation, no database queries, and no personalization.

The Apache HTTP Server is one of the oldest and most widely used web servers. It listens for HTTP requests and returns responses. By default, Apache serves clients the file /var/www/html/index.html when they request the root URL. On Linux, check with sudo ufw allow 80 to open it.

bash
sudo apt install apache2
sudo systemctl enable apache2
sudo systemctl start apache2
sudo ufw allow 80

Your host now has a web server running on port 80 (e.g 192.168.0.10:80). Other devices on the same LAN can access it as a client by navigating to http://<Server_IP>:80 (e.g http://192.168.0.10:80), provided that no firewall is blocking the connection.

REST

REST (Representational State Transfer) is a way of designing web services so different systems can communicate over the internet. It's commonly used to build web APIs by exposing resources through URLs and interacting with them using standard HTTP methods like GET, POST, and PUT. They help your apps retrieve all kinds of information dynamically by making a request or sending data to a server and receiving a structured response, typically in formats like JSON. It can also enable your app to listen for outside requests that trigger events in your app through a webhook.

Client making a Request to a REST API

To interact with a REST API endpoint, you typically send a request with some headers and optionally include data in the request body. Common headers include:

Content-Type - Tells the server the format of the request body (JSON, XML, form data, etc.)

  • -H "Content-Type: application/json"

Accept - Tells the server the format you want in the response

  • -H "Accept: application/json"

Authorization - OAuth2 or JWT token for secure API requests

  • -H "Authorization: Bearer YOUR_ACCESS_TOKEN"

curl is a command-line tool that lets you send HTTP requests directly from your terminal. It’s great for quickly testing REST APIs. In real applications you usually use the HTTP client built into your programming language or platform. For example, Unreal Engine will have a Blueprint Node and python will have the requests library.

-H <HEADER> – Adds a custom header to the request.
-X <METHOD> – Specifies the HTTP method (verb) to use.
-d <DATA> – Sends data in the body of the request.

GET - Retrieve a resource

bash
curl -X GET https://jsonplaceholder.typicode.com/posts/1 \
    -H "Content-Type: application/json"

POST - Create a new resource / Send data to the server

bash
curl -X POST https://jsonplaceholder.typicode.com/posts \
  -H "Content-Type: application/json" \
  -d '{"title":"hello","body":"world","userId":1}'

PUT - Update an existing resource

bash
curl -X PUT https://jsonplaceholder.typicode.com/posts/1 \
  -H "Content-Type: application/json" \
  -d '{"title":"updated","body":"new content","userId":1}'
Server Responding to a REST API Request

When you create a REST API server (e.g Python, JavaScript, Java, etc), the flow is largely the same:

text
1. Serve a Route / Endpoint
- Your server listens for requests at a specific URL (e.g /posts, /users/42, /calculate)
 
2. Receive and Parse Data
- Clients often send data in the request body (JSON, XML, or form data).
- The server deserializes this data so it can work with it in code.
 
3. Process the Request
- Perform some logic (e.g Read, Run Computation, Trigger Event, etc)
 
4. Serialize a Response
- Convert the result into a format the client understands (usually JSON).
- Include relevant status codes (200, 201, 400, 404, etc.) and headers.
 
5. Send the Response
- Return the serialized data back to the client.
python
import random
import time
from datetime import datetime
from flask import Flask, request, jsonify
 
app = Flask(__name__)
 
@app.route('/api/time', methods=['GET'])
def get_server_time():
    now = datetime.now()
 
    timestamp = now.strftime("%Y-%m-%d %H:%M:%S")
    
    timezone = time.tzname[0]
 
    return jsonify({
        "server_time": timestamp,
        "timezone": timezone,
        "unix_timestamp": int(now.timestamp())
    })
 
RESPONSES = [
    "It is certain.", "Reply hazy, try again.", "Don't count on it.",
    "Without a doubt.", "Ask again later.", "My sources say no.",
    "Outlook good.", "Better not tell you now.", "Very doubtful."
]
 
@app.route('/api/speak', methods=['POST'])
def magic_8_ball():
    data = request.get_json()
    
    if not data or 'question' not in data:
        return jsonify({"error": "You must ask a question!"}), 400
 
    question = data['question']
    answer = random.choice(RESPONSES)
 
    return jsonify({
        "answer": answer,
        "question": question
    })
 
if __name__ == '__main__':
    app.run(host="0.0.0.0", port=5000)
bash
curl -X GET http://localhost:5000/api/time
bash
curl -X POST http://192.168.50.134:5000/api/speak -H "Content-Type: application/json" -d '{"question": "Will I become a master coder?"}'

GraphQL

GraphQL is a query language for APIs that lets clients request exactly the data they need without underfetching or overfetching. A GraphQL server exposes one endpoint (e.g /graphql) and lets you read and modify data similar to RESTs HTTP Methods (e.g GET, POST, etc) but through queries and mutations that can unify multiple data sources, including other REST APIs, SQL databases, or local storage.

graphql
query {
  countries {
    code
    name
    emoji
    currency
  }
}
bash
curl -X POST https://countries.trevorblades.com \
  -H "Content-Type: application/json" \
  -d '{"query":"query { countries { code name emoji currency } }"}'

WebSockets

Websockets (ws) provide a way to upgrade a standard HTTP Request/Response conversation into a permanent, "always-open" real-time Bidirectional communication channel where messages are formatted as text or binary data. The WebSocket API is built into all modern browsers and provides and event-driven way to manage a connection’s lifecycle through events such as open, message, error, and close. WebSockets are commonly used in chat applications, multiplayer games, collaborative tools, live dashboards, and real-time streaming systems.

javascript
// ws_server.js
const WebSocket = require('ws');
 
const wss = new WebSocket.Server({ port: 8080 });
 
const clients = new Set();
 
wss.on('connection', (ws) => {
    console.log("Client connected");
    clients.add(ws);
 
    ws.on('message', (message) => {
        console.log("Received:", message.toString());
 
        // Broadcast to all clients
        for (const client of clients) {
            if (client.readyState === WebSocket.OPEN) {
                client.send(`Broadcast: ${message}`);
            }
        }
    });
 
    ws.on('close', () => {
        console.log("Client disconnected");
        clients.delete(ws);
    });
});
 
console.log("WebSocket server running on ws://localhost:8080");
 
javascript
// ws_client.js
const WebSocket = require('ws');
 
const ws = new WebSocket('ws://localhost:8080');
 
ws.on('open', () => {
    console.log("Connected to server");
    ws.send("Hello from Node client!");
});
 
ws.on('message', (data) => {
    console.log("Received:", data.toString());
});

OSC

OSC (Open Sound Control) is a protocol for sending real-time, structured messages between devices. It was created because older protocols like MIDI couldn’t handle networked setups, high-resolution control, or rich data types. Today, OSC is widely used in live music, interactive installations, game engines, and virtual production, with broad support across software applications and programming languages.

Key points:

  • Message - The basic unit of OSC communication (e.g message = address pattern + type tags + arguments).
  • Address Pattern – hierarchical path like (e.g /lights/1/intensity)
  • Type Tag - short description of argument types. prefixed with a comma (e.g ,ifs -> int float string)
  • Arguments – the values associated with the address (e.g int32, float32, strings, binary blobs, and a few others)
  • Bundles - A container for grouping messages, optionally used with a time tag for synchronized execution.
  • Time Tags - A timestamp on a bundle that defines when it executes. Uses NTP time format, but requires external clock sync (e.g. NTP/PTP) for accuracy.

An OSC Server listens for incoming messages on a network port and defines a set of addresses (like /lights/1/intensity) that those messages control.

python
# osc_server_example.py
from pythonosc import dispatcher
from pythonosc import osc_server
 
# Define a handler for messages
def light_handler(address, *args):
    print(f"Received message at: {address}")
    print(f"Received intensity: {args}")
 
def color_handler(address, *args):
    # args is a 4-tuple (r, g, b, a) because you sent 4 separate floats
    r, g, b, a = args
    
    print(f"Received message at: {address}")
    print(f"Received color: R={r}, G={g}, B={b}, A={a}")
 
# Map OSC address patterns to handler functions
disp = dispatcher.Dispatcher()
disp.map("/lights/1/intensity", light_handler)
disp.map("/lights/1/color", color_handler)
 
# Create the server (listening on UDP port 5005)
server = osc_server.ThreadingOSCUDPServer(("0.0.0.0", 5005), disp)
print("OSC Server is listening on 0.0.0.0:5005")
server.serve_forever()

An OSC Client sends messages or bundles to an OSC Server, specifying the addresses and arguments to control endpoints or trigger actions.

python
# osc_client_example.py
from pythonosc import udp_client
import time
 
# Connect to the server
client = udp_client.SimpleUDPClient("127.0.0.1", 5005)
 
# Send a single message
client.send_message("/lights/1/intensity", 0.8)
 
# Send multiple messages sequentially
for i in range(3):
    client.send_message("/lights/1/intensity", i * 0.5)
    time.sleep(0.1)  # simulate slight delay
python
# osc_bundle_example.py
from pythonosc import osc_bundle_builder, osc_message_builder
from pythonosc import udp_client
import time
 
# Connect to the server
client = udp_client.SimpleUDPClient("127.0.0.1", 5005)
 
# Build a bundle
bundle = osc_bundle_builder.OscBundleBuilder(osc_bundle_builder.IMMEDIATELY)
 
# First message
msg1 = osc_message_builder.OscMessageBuilder(address="/lights/1/intensity")
msg1.add_arg(1.0, arg_type='f')
bundle.add_content(msg1.build())
 
# Second message
msg2 = osc_message_builder.OscMessageBuilder(address="/lights/1/color")
 
# Define your color (Standardized to 0.0 - 1.0 range for 'f' type)
r, g, b, a = 1.0, 0.5, 0.25, 1.0 
 
# Add each component as a separate argument
msg2.add_arg(r, arg_type='f')
msg2.add_arg(g, arg_type='f')
msg2.add_arg(b, arg_type='f')
msg2.add_arg(a, arg_type='f')
bundle.add_content(msg2.build())
 
# Send the bundle
client.send(bundle.build())

LibLO is a lightweight, easy to use implementation of the OSC (Open Sound Control) protocol for ubuntu/debian. If our osc-server-example.py is running on LAN Host IP 192.168.0.10 we can test communication from another Host like so

bash
sudo apt-get install liblo-tools
oscsend 192.168.0.10 5005 /lights/1/color ffff 1.0 0.5 0.25 1.0

WebRTC

WebRTC (Web Real-Time Communication) is a protocol and set of APIs that enables peer-to-peer, low-latency audio, video, and data streaming directly between browsers or apps, without requiring a central server to relay the media. I will briefly discuss what it's partially like setting up communication between 2 peers.

The Session Description Protocol (SDP) is used to exchange a set of key-value pairs between peers. A caller sends a SDP Offer describing its media capabilities (audio, video codecs, data channels). A Callee acknowledges the remote callers session description and sends back a SDP Answer describing its own media capabilities. The peers exchange potential network paths through a Protocol/Framework known as Interactive Connectivity Establishment (ICE), which coordinates with STUN and TURN servers to resolve a list of ICE Candidates describing all possible network addresses and ports each peer can use to establish the best possible connection between them. If the peers cannot communicate locally or through NATs then communication between peers will be relayed through a TURN server to ensure connectivity.

A Simple Web application where an arbitrary client can "call" another client will require a Websocket Signalling Server.

Both Clients will navigate their web browser to a url say https://meet-the-strangers.simulationshock.com and receive a response containing HTML and Client Javascript. The HTML will describe the graphical user interface to see and chat but the javascript will connect to the signalling server (e.g ws://localhost:8888) so that peers can coordinate before making a direct connection to each other. Once connected, the client javascript will also update the HTML page with real-time audio, video, and data communication channels.

Through the Web Browsers MediaDevices API, Clients are prompted to select which input devices (microphone, camera) their system will grant permission and use with the WebRTC API to establish a peer-to-peer connection.

javascript
async function startConnection() {
  if (hasUserMedia()) {
        try {
                stream = await navigator.mediaDevices.getUserMedia({
                video: true,
                audio: true
            });
 
            yourVideo.srcObject = stream;
 
            if (hasRTCPeerConnection()) {
                setupPeerConnection(stream);
            } else {
                alert("Sorry, your browser does not support WebRTC.");
            }
 
        } catch (error) {
            console.error("Failed to access camera:", error);
        }
    }
}

A RTCPeerConnection instance is configured with various STUN and TURN Servers to help exchange ice candidates and initialize callback events for media streams.

javascript
function setupPeerConnection(stream) {
    const configuration = {
      iceServers: [
        // STUN servers
        { urls: "stun:stun.l.google.com:19302" },
        { urls: "stun:stun1.l.google.com:19302" },
        {
          urls: "turn:openrelay.metered.ca:443",
          username: "openrelayproject",
          credential: "openrelayproject"
        }
      ]
    };
 
    yourConnection = new RTCPeerConnection(configuration);
 
    stream.getTracks().forEach(track => {
        yourConnection.addTrack(track, stream);
    });
 
    yourConnection.ondatachannel = (event) => {
        dataChannel = event.channel;
        
        dataChannel.onopen = () => {
            console.log("Data channel open");
        };
 
        dataChannel.onmessage = (event) => {
            console.log("Received:", event.data);
        };
 
        dataChannel.onclose = () => {
            console.log("Data channel closed");
        };
 
        dataChannel.onerror = (error) => {
            console.error("Data channel error:", error);
        };
    };
 
    yourConnection.ontrack = (event) => {
        theirVideo.srcObject = event.streams[0];
    };
 
    yourConnection.oniceconnectionstatechange = () => {
    console.log("ICE State:", yourConnection.iceConnectionState);
 
    if (yourConnection.iceConnectionState === "failed") {
        console.log("Restarting ICE...");
        yourConnection.restartIce();
    }
    };
 
    yourConnection.onicecandidate = function (event) {
        if (event.candidate) {
            send({
                type: "candidate",
                candidate: event.candidate
            });
        }
    };
};

gRPC

gRPC (Google Remote Procedure Call) is a high-performance framework that allows one application to call functions running on another machine, even if it was built with a different programming language. It uses a compact binary format called Protocol Buffers (Protobuf) instead of JSON to serialize messages. Communication between clients and servers is defined using a .proto file, which specifies the schema for interaction through request/response, client streaming, server streaming, and bidirectional streaming. In other words, gRPC can behave like REST, SSE, or WebSockets. The Protobuf schema is used to generate client and server stubs (i.e. code) in various languages such as C++, Python, Java, Rust, and more, which developers can import directly into their projects.

A C++ Server may use code generated from the following Protobuf

proto
// sensor.proto
syntax = "proto3";
 
package sensor;
 
// A simple sensor reading
message SensorReading {
  string sensor_id = 1;
  double value = 2;
  int64 timestamp = 3;
}
 
// Request for a single sensor
message SensorRequest {
  string sensor_id = 1;
}
 
// Service definition
service SensorService {
  // Unary RPC
  // Client sends one request, gets one response
  rpc GetLatestReading (SensorRequest) returns (SensorReading);
}

A Python Client may use code generated from the Protobuf

python
import grpc
import time
import sensor_pb2
import sensor_pb2_grpc
 
# Connect to the server
channel = grpc.insecure_channel('localhost:50051')
stub = sensor_pb2_grpc.SensorServiceStub(channel)
 
# Unary RPC: GetLatestReading
print("=== Unary RPC ===")
request = sensor_pb2.SensorRequest(sensor_id="sensor_1")
response = stub.GetLatestReading(request)
print(f"Latest reading: {response}")

SSE

SSE (Server-Sent Events) allows a server to maintain a one-way, continuous stream of data to a browser. Once the client (your browser) makes the initial request, the server keeps the connection open indefinitely, sending new information down to the client whenever it becomes available. It is a lightweight alternative to WebSockets when you don't need the client to talk back on that same connection. SSE is most often used for live feeds, notifications, real-time dashboards, and any situation where the server needs to push updates to many clients.

Example: Webpage and SSE Endpoint

The HTTP endpoint at http://localhost:4000/events is set up to stream the current server time every second to connected clients that request the stream. Similar to REST, the client initiates a request to the server but this endpoint keeps the connection open so the server can push new messages to the client whenever data changes.

The server sets the Content-Type: text/event-stream header so the browser knows to treat the response as a continuous stream.

The client uses the built-in EventSource API to receive updates automatically and can handle reconnections if the connection drops.

In the SSE protocol, Each line starting with data: is treated as a message while \n\n terminates the event so the browser knows it’s a complete message.

Node + Express are used to serve both the HTML page and the SSE endpoint in a single server to test

The HTML page connects to the SSE endpoint using EventSource, listens for incoming messages, and displays them in real time.

javascript
// server.js
const express = require("express");
const app = express();
 
// Serve HTML/JS
app.use(express.static("public"));
 
app.get("/events", (req, res) => {
  res.setHeader("Content-Type", "text/event-stream");
  res.setHeader("Cache-Control", "no-cache");
  res.setHeader("Connection", "keep-alive");
 
  // Send an update every second
  const interval = setInterval(() => {
    const data = {
      time: new Date().toISOString()
    };
 
    res.write(`data: ${JSON.stringify(data)}\n\n`);
  }, 1000);
 
  // Cleanup if the client disconnects
  req.on("close", () => {
    clearInterval(interval);
  });
});
 
app.listen(4000, () => {
  console.log("SSE server running on http://localhost:4000");
});
html
<!doctype html>
<html>
  <body>
    <h1>SSE Demo</h1>
    <pre id="output"></pre>
 
    <script>
      const output = document.getElementById("output");
      const es = new EventSource("http://localhost:4000/events");
 
      es.onmessage = (event) => {
        const data = JSON.parse(event.data);
        output.textContent += `Received: ${data.time}\n`;
      };
 
      es.onerror = () => {
        console.warn("Connection lost. Reconnecting...");
      };
    </script>
  </body>
</html>

Directory structure:

text
sse-demo/
├── server.js
└── public/
    └── index.html

Start the server:

bash
node server.js

Open the page in your browser:

text
http://localhost:4000/index.html

Webhook

A Webhook is a REST POST Method hosted by you, but invoked by someone else. While REST is typically used for requesting or managing data, Webhooks are used for reacting to events that sometimes have information. In a REST API, you are the one knocking on the door. In a Webhook, you are the one waiting for the knock.

Webhooks are commonly used in discord to post messages to channels. For example, When you push code to GitHub (the trigger), GitHub sends a REST POST request containing the commit details to a specific Discord URL. Discord receives this "push" and instantly transforms the raw data into a chat message.

Across all languages, Webhooks follow this pattern:

  1. Expose an endpoint to receive events
  2. Some external system decides when to call you
  3. React when the service POSTs an event to you
python
# webhook_osc_example.py
from flask import Flask, request, jsonify
from pythonosc.udp_client import SimpleUDPClient
 
app = Flask(__name__)
 
# This must match your OSC server
OSC_IP = "127.0.0.1"
OSC_PORT = 5005
 
osc_client = SimpleUDPClient(OSC_IP, OSC_PORT)
 
@app.route("/webhook", methods=["POST"])
def webhook():
    data = request.json
 
    if not data:
        return jsonify({"error": "Invalid JSON"}), 400
 
    try:
        event_type = data.get("type")
 
        if event_type == "intensity":
            value = float(data.get("value", 1))
            osc_client.send_message("/lights/1/intensity", value)
 
        elif event_type == "color":
            r = float(data.get("r", 1))
            g = float(data.get("g", 1))
            b = float(data.get("b", 1))
            a = float(data.get("a", 1))
 
            osc_client.send_message("/lights/1/color", [r, g, b, a])
 
        else:
            return jsonify({"error": "Unknown type"}), 400
 
        return jsonify({"status": "OSC sent"}), 200
 
    except Exception as e:
        return jsonify({"error": str(e)}), 500
 
if __name__ == "__main__":
    app.run(host="0.0.0.0", port=5000)
bash
curl -X POST http://localhost:5000/webhook -H "Content-Type: application/json" -d '{"type":"intensity","value":0.8}'
bash
curl -X POST http://localhost:5000/webhook -H "Content-Type: application/json" -d '{"type":"color","r":1,"g":0,"b":0,"a":1}'

MQTT

MQTT (Message Queuing Telemetry Transport) is a protocol where all devices talk to a central Broker. Devices Publish data to a specific Topic, and any other device Subscribed to that topic receives the update instantly. MQTT was developed for low-bandwidth, high-latency, or unreliable networks and is now widely used in IoT, telemetry, and real-time applications.

Publishers and subscribers never talk directly and follow these delivery patterns:

  • One → Many
    • One sensor publishes temperature → many dashboards receive it
  • Many → One
    • Many devices publish telemetry → one monitoring system aggregates it
  • Many → Many
    • Multiple producers and consumers exchange state via shared topics

MQTT defines 3 levels of delivery guarantee (QoS):

  • QOS 0: The message is delivered at most once, with no acknowledgement. If the network fails, the message may be lost.
  • QOS 1: The message is delivered at least once, with acknowledgement. The broker may resend, so duplicates are possible.
  • QOS 2: The message is delivered exactly once, this is the most reliable but adds overhead.

Across all languages, MQTT follows this pattern:

  1. Connect to a broker
  2. Subscribe to one or more topics
  3. Publish messages to topics
  4. Receive messages routed by the broker

Topics are organized into hierarchical paths, like folders, to structure messages and allow flexible subscriptions:

text
sensors/temperature/living_room
sensors/temperature/bedroom

Subscribers can use wildcards to listen to multiple topics at once:

text
+ -> matches any single level
home/+/light
home/kitchen/light
home/bedroom/light
 
# -> matches all remaining levels
sensors/#
sensors/temp
sensors/temp/room1
sensors/motion/hallway/battery

The broker is the central server that receives published messages and forwards them to all subscribers, handling topic matching, QoS, and message delivery guarantees. By default, it communicates in plain-text over TCP (mqtt://) on port 1883, but it can be upgraded to a secure TLS connection using mqtts:// on port 8883 for encrypted communication. Mosquitto is a popular MQTT broker that can be easily set up on a variety of platforms.

Broker

bash
sudo apt install mosquitto
sudo systemctl enable mosquitto
sudo systemctl start mosquitto

Publisher

python
import paho.mqtt.publish as publish
 
BROKER = "localhost"
PORT = 1883  # or 8883 for TLS/mqtts
 
publish.single(
    topic="sensors/temperature/living_room",
    payload="22.5",
    hostname=BROKER,
    port=PORT
)

Subscriber

python
import paho.mqtt.client as mqtt
 
BROKER = "localhost"
PORT = 1883  # or 8883 for TLS/mqtts
 
def on_message(client, userdata, msg):
    print(f"{msg.topic} -> {msg.payload.decode()}")
 
client = mqtt.Client()
client.connect(BROKER, PORT)
client.subscribe("sensors/temperature/+")
client.on_message = on_message
client.loop_forever()

Web browsers cannot directly use MQTT, but they can open WebSocket connections. MQTT over WebSockets wraps the MQTT protocol inside a browser-friendly WebSocket, allowing web pages and web apps to publish and subscribe to topics in real time. Another port is used for WebSockets (commonly 9001 for unencrypted ws:// or 443 for secure wss://) so that MQTT messages can flow through browsers while maintaining the same topics, QoS, and message semantics as standard MQTT.

MQTT over WebSockets

javascript
const client = mqtt.connect('ws://broker.example.com:9001');
 
client.on('connect', () => {
  console.log('Connected via WebSocket!');
  client.subscribe('sensors/temperature/+');
  client.publish('sensors/temperature/living_room', '22.5');
});
 
client.on('message', (topic, message) => {
  console.log(`${topic} -> ${message.toString()}`);
});