Players connect to names, not IPs, and sensitive traffic must be encrypted. Learn DNS resolution, IPv4 vs IPv6 addressing, and wrapping a socket in TLS so logins and payments are secure.
Players connect to a hostname like play.mygame.com, not a raw IP, so the first step of any connection is DNS — translating the name to an address. Your code resolves the name (getaddrinfo) then connects to the resulting IP. DNS also enables operational flexibility: you can move servers or load-balance across many machines by changing DNS records, without clients knowing the IP changed.
import socket
# resolve a hostname to address(es) — the first step before connecting
infos = socket.getaddrinfo("example.com", 443, proto=socket.IPPROTO_TCP)
family, socktype, proto, canonname, sockaddr = infos[0]
print(sockaddr) # e.g. ('93.184.216.34', 443)
# then connect to the resolved address:
s = socket.socket(family, socktype, proto)
s.connect(sockaddr)
# players use NAMES (play.mygame.com); DNS -> IP. changing DNS records lets you
# move/scale/load-balance servers without clients knowing the IP changed.There are two address formats, and a modern server must handle both. IPv4 (32-bit, like 203.0.113.5) is nearly exhausted; IPv6 (128-bit, like 2001:db8::1) is the successor with a vast address space. Writing address-family-agnostic code (using getaddrinfo and dual-stack sockets) means your server reaches players on either — increasingly important as IPv6 adoption grows, especially on mobile networks.
Two address formats — support both:
IPv4 32-bit, dotted: 203.0.113.5 (~4 billion addresses — exhausted)
IPv6 128-bit, hex: 2001:db8::1 (vast space, the successor)
Write address-family-agnostic code:
- use getaddrinfo() (returns IPv4 AND IPv6 results) instead of hardcoding
- bind a "dual-stack" socket (AF_INET6 with IPV6_V6ONLY off) to accept both
- never assume 4-byte addresses in your code
Many mobile carriers are IPv6-first. An IPv4-only game server misses players.Any traffic carrying credentials, payments, or personal data must be encrypted with TLS, which wraps a plain TCP socket so bytes are confidential and tamper-evident and the server’s identity is verified by a certificate. In code you wrap the socket in a TLS context; the handshake and encryption are handled for you. This is the same TLS/PKI from the Applied Cryptography course, applied to your game’s login and store traffic.
import socket, ssl
# wrap a TCP socket in TLS — encrypts + authenticates the server via its cert
context = ssl.create_default_context() # verifies the server certificate
raw = socket.create_connection(("api.mygame.com", 443))
tls = context.wrap_socket(raw, server_hostname="api.mygame.com")
tls.sendall(b"POST /login ...") # now confidential + tamper-evident
print(tls.version()) # e.g. TLSv1.3
# use TLS for anything sensitive: login/auth, payments, personal data.
# real-time UDP game traffic uses DTLS (TLS for datagrams) when it needs it.
# (how TLS + certificates work: the Applied Cryptography & PKI course.)A real client connection composes these pieces: resolve the hostname via DNS, connect over TCP (or set up UDP), upgrade to TLS if the data is sensitive, then speak your protocol with proper framing. Knowing where each layer fits — name resolution, transport, encryption, application protocol — lets you reason about failures ("is it DNS? the handshake? the protocol?") and design the connection flow for your game’s services.
The full connection flow for a game's backend service:
1. RESOLVE play.mygame.com -> IP (DNS)
2. CONNECT TCP handshake to (IP, port) (or set up a UDP socket)
3. SECURE TLS handshake (verify cert, agree keys) [if sensitive]
4. PROTOCOL speak your app protocol, with length-framed messages
Failure diagnosis maps to the layers:
can't resolve -> DNS handshake fails -> TCP/firewall/TLS
garbled data -> framing/serialization reset -> server closed it
Real-time gameplay skips 1-3 after matchmaking: the matchmaker hands the client
a server IP+port+token, and the client opens a UDP session directly to it.