12 · Video streaming, CDNs, socket programming
Slides 2-80 → 2-104 · no HW problems, so concepts only
Part 1 · Video streaming
The challenges
- Streaming video is the biggest consumer of Internet bandwidth (Netflix, YouTube, Amazon Prime ≈ 80% of residential ISP traffic in 2020).
- Scale: how do you reach ~1 billion users? One mega-server won't work.
- Heterogeneity: users have different capabilities (wired vs mobile, lots of bandwidth vs little).
- Solution: a distributed, application-level infrastructure (DASH + CDNs).
Video encoding
- Video = a sequence of images shown at a constant rate (e.g. 24 or 30 per second). Each image = an array of pixels, each pixel = bits.
- Coding uses redundancy to send fewer bits:
- Spatial (within one image): instead of N purple pixels in a row, send "purple" and "N".
- Temporal (between images): instead of all of frame i+1, send only what changed from frame i.
| CBR (constant bit rate) | VBR (variable bit rate) |
|---|---|
| Encoding rate is fixed | Encoding rate changes as the amount of spatial/temporal coding changes |
Examples: MPEG-1 (CD-ROM) 1.5 Mbps, MPEG-2 (DVD) 3–6 Mbps, MPEG-4 (Internet) 64 Kbps–12 Mbps.
Streaming stored video
- Streaming = the client plays the early part of the video while the server is still sending later parts.
- Problems: server-to-client bandwidth varies with congestion, and loss and delay can stall playback or lower quality.
- Continuous playout constraint: once playback starts, it must match the original timing. But network delay varies (jitter).
- Other challenges: client interactivity (pause, fast-forward, rewind, jump), and lost or retransmitted packets.
DASH: Dynamic, Adaptive Streaming over HTTP
| Server | Client |
|---|---|
| Splits the video into chunks | Periodically measures server-to-client bandwidth |
| Stores each chunk encoded at several rates | Reads the manifest and requests one chunk at a time |
| Provides a manifest file with the URLs of every chunk version | Picks the highest rate the current bandwidth can sustain, and can switch rates from chunk to chunk |
The intelligence is at the client. It decides:
- When to request a chunk (so the buffer doesn't run empty or overflow)
- What encoding rate to request (higher quality when there's more bandwidth)
- Where to request it from (a server that's close or has high bandwidth)
Content distribution networks (CDNs)
Problem: stream content chosen from millions of videos to hundreds of thousands of users at once.
Option 1: one huge mega-server. Doesn't scale:
- Single point of failure
- Point of network congestion
- Long path to distant clients
- Many copies of the same video sent over the same outgoing link
Option 2: CDN. Store copies at many geographically spread sites.
| Enter deep | Bring home |
|---|---|
| Push CDN servers deep into many access networks, close to users | A smaller number (tens) of larger clusters in POPs near, but not inside, access networks |
| Akamai (240,000 servers, 120+ countries) | Limelight |
How it works: e.g. Netflix stores copies of a show on CDN nodes. A subscriber asks for it, gets the manifest, and is directed to a nearby copy. If that path is congested, it can switch to a different copy.
OTT ("over the top"): the CDN runs on top of ordinary Internet host-to-host service, so it has to cope with a congested Internet. Its decisions: which CDN node to fetch from, how viewers behave during congestion, and which content to place on which node.
Part 2 · Socket programming
Socket = the door between the app process and the transport protocol (topic 7). Two socket types for the two transport services:
| UDP socket | TCP socket | |
|---|---|---|
| Service | Unreliable datagrams: data may be lost or arrive out of order | Reliable, in-order byte stream (a "pipe") |
| Connection? | No handshake before sending | Client must connect first. TCP sets up the connection |
| Addressing | Sender attaches the destination IP + port to every packet. Receiver extracts the sender's IP + port from each packet | Address given once at connect time. After that, just send |
| Python type | SOCK_DGRAM | SOCK_STREAM |
| Send / receive | sendto(msg, (name, port)) / recvfrom() (returns data and address) | send(msg) / recv() (data only) |
| Server sockets | One socket for all clients | A welcoming socket plus a new socket per client |
The example app for both: the client reads a line from the keyboard and sends it. The server converts it to uppercase and sends it back. The client displays it.
UDP
UDPClient
from socket import *
serverName = 'hostname'
serverPort = 12000
clientSocket = socket(AF_INET, SOCK_DGRAM) # UDP socket
message = raw_input('Input lowercase sentence:')
clientSocket.sendto(message.encode(), (serverName, serverPort)) # attach address
modifiedMessage, serverAddress = clientSocket.recvfrom(2048)
print modifiedMessage.decode()
clientSocket.close()
UDPServer
from socket import *
serverPort = 12000
serverSocket = socket(AF_INET, SOCK_DGRAM)
serverSocket.bind(('', serverPort)) # bind to port 12000
while True:
message, clientAddress = serverSocket.recvfrom(2048) # get client's IP + port
modifiedMessage = message.decode().upper()
serverSocket.sendto(modifiedMessage.encode(), clientAddress)
TCP
- The server must already be running and must have created a welcoming socket (the door clients knock on).
- The client creates a TCP socket with the server's IP and port. That makes the client's TCP establish a connection to the server's TCP.
- When contacted, the server's TCP creates a new socket just for that client. That's how a server talks to many clients at once. Source port numbers tell the clients apart (more in Ch3, topic 13).
TCPClient
from socket import *
serverName = 'servername'
serverPort = 12000
clientSocket = socket(AF_INET, SOCK_STREAM) # TCP socket
clientSocket.connect((serverName, serverPort)) # set up the connection
sentence = raw_input('Input lowercase sentence:')
clientSocket.send(sentence.encode()) # no address needed
modifiedSentence = clientSocket.recv(1024)
print ('From Server:', modifiedSentence.decode())
clientSocket.close()
TCPServer
from socket import *
serverPort = 12000
serverSocket = socket(AF_INET, SOCK_STREAM) # welcoming socket
serverSocket.bind(('', serverPort))
serverSocket.listen(1) # listen for TCP requests
while True:
connectionSocket, addr = serverSocket.accept() # NEW socket per client
sentence = connectionSocket.recv(1024).decode() # bytes only, no address
capitalizedSentence = sentence.upper()
connectionSocket.send(capitalizedSentence.encode())
connectionSocket.close() # close this client, not the welcoming socket
•
SOCK_DGRAM = UDP. SOCK_STREAM = TCP.• Only TCP has
connect() (client) and listen() / accept() (server).• UDP uses
sendto/recvfrom because every packet carries an address. TCP uses send/recv because the connection already knows who's on the other end.• Both servers call
bind() to a known port (12000). Clients don't need to.•
accept() returns a new socket. The server closes that one per client, and keeps the welcoming socket open.Chapter 2 big themes
- Request/reply: the client requests, the server responds with data and a status code.
- Message formats: headers (info about the data) + data (payload).
- Centralized vs decentralized · stateless vs stateful · scalability · reliable vs unreliable transfer · "complexity at the network edge".
Quick check
1. What are spatial and temporal coding?
Spatial: compress within one frame (send "purple × N" instead of N purple pixels). Temporal: send only the differences between consecutive frames.2. CBR vs VBR?
CBR: fixed encoding rate. VBR: encoding rate changes as the amount of spatial/temporal coding changes.3. Why does a streaming client buffer before playing?
Network delay varies (jitter), but playback must be continuous at the original timing. A playout delay plus a client buffer absorbs the variation.4. In DASH, what does the server do, and what does the client decide?
Server: splits the video into chunks, encodes each at several rates, and gives a manifest of URLs. Client: measures bandwidth and decides when to request a chunk, what rate to request, and where to get it from.5. Give 3 reasons a single mega-server doesn't work for video.
Any three: single point of failure, point of network congestion, long path to distant clients, many copies of the same video sent over one outgoing link.6. Enter deep vs bring home?
Enter deep: many CDN servers placed inside access networks, close to users (Akamai). Bring home: fewer, larger clusters in POPs near but not inside access networks (Limelight).7. Why does the UDP client use sendto with an address, but the TCP client just uses send?
UDP has no connection, so every datagram must carry the destination IP + port. TCP set up a connection with connect(), so the socket already knows the destination.8. How does one TCP server handle many clients at once?
accept() creates a new connection socket for each client. The welcoming socket stays open for new clients.9. Which socket type is SOCK_DGRAM? SOCK_STREAM?
SOCK_DGRAM = UDP. SOCK_STREAM = TCP.