378 lines
25 KiB
HTML
378 lines
25 KiB
HTML
<!doctype html>
|
|
<html lang="en" dir="ltr">
|
|
<head>
|
|
<meta charset="utf-8" />
|
|
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
|
<title>WebSocket Protocol</title>
|
|
<link rel="stylesheet" href="styles.css" />
|
|
</head>
|
|
<body>
|
|
<main class="deck" id="deck">
|
|
<section class="slide active center-slide" data-title="Title">
|
|
<div class="center-content">
|
|
<h1>Websocket Protocol</h1>
|
|
<p class="subtitle">Socket Programming with a FastAPI real-time demo chat app</p>
|
|
<p class="members">Amirhossein Khalili • Morteza Khanbabaie • Alireza Khosravi</p>
|
|
</div>
|
|
<aside class="notes">Open with the project name and explain that the presentation focuses on WebSocket concepts through a simple chatroom implementation.</aside>
|
|
</section>
|
|
|
|
<section class="slide" data-title="Timeline">
|
|
<h2>WebSocket History Timeline</h2>
|
|
<p class="subtitle">The communication model evolved from simple request/response to persistent real-time channels.</p>
|
|
<div class="timeline-only glass-panel">
|
|
<div class="timeline-method"><span>Classic HTTP</span><small>1991</small></div>
|
|
<div class="timeline-method"><span>Polling</span><small>1995+</small></div>
|
|
<div class="timeline-method"><span>Long Polling</span><small>2006</small></div>
|
|
<div class="timeline-method"><span>WebSocket</span><small>2011</small></div>
|
|
<div class="timeline-method"><span>Real-time Web</span><small>2010s+</small></div>
|
|
</div>
|
|
<aside class="notes">Use this slide to name the major approaches only. The next slides explain how each mechanism works and what it costs.</aside>
|
|
</section>
|
|
|
|
<section class="slide protocol-slide" data-title="HTTP">
|
|
<h2>HTTP</h2>
|
|
<p class="subtitle">A short-lived request/response cycle with no native live push.</p>
|
|
<div class="protocol-layout">
|
|
<div class="protocol-visual glass-panel">
|
|
<img src="assets/http-mechanism.svg" alt="HTTP request response mechanism" />
|
|
</div>
|
|
<div class="protocol-points glass-panel">
|
|
<article><b>Mechanism</b><ul><li>One request returns one response</li><li>The connection closes after the exchange</li></ul></article>
|
|
<article><b>Cost</b><ul><li>Every update needs a new HTTP cycle</li><li>The server cannot push live data by itself</li></ul></article>
|
|
</div>
|
|
</div>
|
|
<aside class="notes">HTTP is excellent for normal pages, forms, and APIs. Its basic model is not optimized for continuous server-to-client updates.</aside>
|
|
</section>
|
|
|
|
<section class="slide protocol-slide" data-title="Polling">
|
|
<h2>Polling</h2>
|
|
<p class="subtitle">The browser repeatedly asks the server for new data.</p>
|
|
<div class="protocol-layout">
|
|
<div class="protocol-visual glass-panel">
|
|
<img src="assets/polling-mechanism.svg" alt="Polling repeated request mechanism" />
|
|
</div>
|
|
<div class="protocol-points glass-panel">
|
|
<article><b>Mechanism</b><ul><li>The client checks on a fixed timer</li><li>The server answers even when nothing changed</li></ul></article>
|
|
<article><b>Cost</b><ul><li>Many empty requests waste network work</li><li>Latency depends on the timer interval</li></ul></article>
|
|
</div>
|
|
</div>
|
|
<aside class="notes">Polling is simple but wasteful. Short intervals improve latency but increase server and network cost.</aside>
|
|
</section>
|
|
|
|
<section class="slide protocol-slide" data-title="Long Polling">
|
|
<h2>Long Polling</h2>
|
|
<p class="subtitle">The server holds the request until an update is available.</p>
|
|
<div class="protocol-layout">
|
|
<div class="protocol-visual glass-panel">
|
|
<img src="assets/long-polling-mechanism.svg" alt="Long Polling held request mechanism" />
|
|
</div>
|
|
<div class="protocol-points glass-panel">
|
|
<article><b>Mechanism</b><ul><li>The server keeps the request open</li><li>The client reconnects after each response</li></ul></article>
|
|
<article><b>Cost</b><ul><li>Open requests consume server state</li><li>Reconnect and timeout handling is required</li></ul></article>
|
|
</div>
|
|
</div>
|
|
<aside class="notes">Long Polling reduces empty responses compared with Polling, but it still works through repeated HTTP request cycles.</aside>
|
|
</section>
|
|
|
|
<section class="slide protocol-slide" data-title="WebSocket">
|
|
<h2>WebSocket</h2>
|
|
<p class="subtitle">An HTTP Upgrade creates one persistent Full-duplex channel.</p>
|
|
<div class="protocol-layout">
|
|
<div class="protocol-visual glass-panel">
|
|
<img src="assets/websocket-handshake.svg" alt="HTTP Upgrade Handshake" />
|
|
</div>
|
|
<div class="protocol-points glass-panel">
|
|
<article><b>Mechanism</b><ul><li>HTTP Upgrade switches the protocol</li><li>Frames move both ways on one socket</li></ul></article>
|
|
<article><b>Cost</b><ul><li>Each client keeps a live connection</li><li>Scaling needs connection-aware design</li></ul></article>
|
|
</div>
|
|
</div>
|
|
<aside class="notes">This is the key WebSocket slide. The browser starts with HTTP, upgrades the connection, and then both client and server can send frames whenever needed.</aside>
|
|
</section>
|
|
|
|
<section class="slide image-slide" data-title="OSI">
|
|
<h2>WebSocket in the OSI Model</h2>
|
|
<p class="subtitle">Application semantics at Layer 7, reliable byte delivery through TCP/IP.</p>
|
|
<div class="single-visual glass-panel">
|
|
<img src="assets/websocket-osi.svg" alt="WebSocket in OSI model" />
|
|
</div>
|
|
<aside class="notes">WebSocket is an application-layer protocol. It uses TCP for reliable transport and IP for routing. The lower layers do not know what a chat message or room is.</aside>
|
|
</section>
|
|
|
|
<section class="slide" data-title="HTTP Versions">
|
|
<h2>HTTP Versions in WebSocket</h2>
|
|
<p class="subtitle">Different HTTP versions start WebSocket connections differently.</p>
|
|
<div class="version-row point-cards detailed-version-row">
|
|
<article><span>HTTP/1.1</span><b>Upgrade + 101 Switching Protocols</b><ul><li>The browser sends a normal GET request with <code>Upgrade: websocket</code>.</li><li>The server replies <code>101 Switching Protocols</code> if it accepts.</li><li>After that, the same TCP connection carries WebSocket frames both ways.</li><li>This is the classic and easiest model to understand.</li></ul></article>
|
|
<article><span>HTTP/2</span><b>Extended CONNECT over a Stream</b><ul><li>HTTP/2 does not use the old connection-level Upgrade flow.</li><li>It uses Extended CONNECT, so WebSocket traffic lives inside one HTTP/2 stream.</li><li>Other HTTP/2 streams can share the same underlying connection.</li><li>Support depends more on servers, clients, and proxies.</li></ul></article>
|
|
<article><span>HTTP/3</span><b>CONNECT over QUIC</b><ul><li>HTTP/3 runs on QUIC over UDP instead of TCP.</li><li>WebSocket uses a CONNECT-based mapping similar in idea to HTTP/2.</li><li>QUIC streams reduce transport-level head-of-line blocking.</li><li>It is modern, but the demo is easier to explain with HTTP/1.1.</li></ul></article>
|
|
</div>
|
|
<aside class="notes">The project is best explained with HTTP/1.1 Upgrade. HTTP/2 and HTTP/3 support WebSocket through different mechanisms.</aside>
|
|
</section>
|
|
|
|
<section class="slide image-slide" data-title="FastAPI Architecture">
|
|
<h2>Project Architecture</h2>
|
|
<p class="subtitle">Browsers connect to one WebSocket endpoint; ConnectionManager groups sockets by room.</p>
|
|
<div class="single-visual glass-panel">
|
|
<img src="assets/chatroom-architecture.svg" alt="WebSocket chatroom architecture" />
|
|
</div>
|
|
<aside class="notes">Explain the code structure: FastAPI serves the frontend, the WebSocket endpoint accepts connections, and the manager stores active clients by room before broadcasting messages.</aside>
|
|
</section>
|
|
|
|
<section class="slide code-slide" data-title="Backend Router">
|
|
<h2>Backend Codes</h2>
|
|
<p class="subtitle">WebSocket router setup in FastAPI.</p>
|
|
<div class="code-window">
|
|
<div class="code-titlebar"><span></span><span></span><span></span><strong>app/websocket/chat.py</strong></div>
|
|
<pre><code><span class="line"><span class="kw">import</span> json</span>
|
|
<span class="line"><span class="kw">from</span> datetime <span class="kw">import</span> datetime, timezone</span>
|
|
<span class="line"></span>
|
|
<span class="line"><span class="kw">from</span> fastapi <span class="kw">import</span> APIRouter, WebSocket, WebSocketDisconnect</span>
|
|
<span class="line"></span>
|
|
<span class="line"><span class="kw">from</span> app.websocket.manager <span class="kw">import</span> manager</span>
|
|
<span class="line"></span>
|
|
<span class="line">router = APIRouter()</span></code></pre>
|
|
</div>
|
|
<aside class="notes">You can start by explaining that this module owns the WebSocket endpoint and delegates connection storage to the shared manager.</aside>
|
|
</section>
|
|
|
|
<section class="slide code-slide" data-title="Backend Payload">
|
|
<h2>Message Payload Shape</h2>
|
|
<p class="subtitle">Every server message uses one small JSON structure.</p>
|
|
<div class="code-window">
|
|
<div class="code-titlebar"><span></span><span></span><span></span><strong>app/websocket/chat.py</strong></div>
|
|
<pre><code><span class="line"><span class="kw">def</span> <span class="fn">now_iso</span>() -> str:</span>
|
|
<span class="line"> <span class="kw">return</span> datetime.now(timezone.utc).isoformat()</span>
|
|
<span class="line"></span>
|
|
<span class="line"><span class="kw">def</span> <span class="fn">event</span>(event_type, room_id, client_id, content):</span>
|
|
<span class="line"> <span class="kw">return</span> {</span>
|
|
<span class="line"> <span class="str">"type"</span>: event_type,</span>
|
|
<span class="line"> <span class="str">"room_id"</span>: room_id,</span>
|
|
<span class="line"> <span class="str">"client_id"</span>: client_id,</span>
|
|
<span class="line"> <span class="str">"content"</span>: content,</span>
|
|
<span class="line"> <span class="str">"timestamp"</span>: now_iso(),</span>
|
|
<span class="line"> }</span></code></pre>
|
|
</div>
|
|
<aside class="notes">This slide shows the server-to-client contract: message type, room, sender, content, and timestamp.</aside>
|
|
</section>
|
|
|
|
<section class="slide code-slide" data-title="Backend Endpoint">
|
|
<h2>WebSocket Endpoint</h2>
|
|
<p class="subtitle">The route parameters select the room and the user identity.</p>
|
|
<div class="code-window">
|
|
<div class="code-titlebar"><span></span><span></span><span></span><strong>app/websocket/chat.py</strong></div>
|
|
<pre><code><span class="line">@router.websocket(<span class="str">"/ws/{room_id}/{client_id}"</span>)</span>
|
|
<span class="line"><span class="kw">async def</span> <span class="fn">websocket_endpoint</span>(</span>
|
|
<span class="line"> websocket: WebSocket,</span>
|
|
<span class="line"> room_id: str,</span>
|
|
<span class="line"> client_id: str,</span>
|
|
<span class="line">):</span>
|
|
<span class="line"> room_id = clean_value(room_id, <span class="str">"general"</span>)</span>
|
|
<span class="line"> client_id = clean_value(client_id, <span class="str">"anonymous"</span>)</span>
|
|
<span class="line"></span>
|
|
<span class="line"> <span class="kw">await</span> manager.connect(websocket, room_id, client_id)</span></code></pre>
|
|
</div>
|
|
<aside class="notes">The browser URL maps directly to this route. FastAPI receives the HTTP Upgrade request and the manager accepts the socket.</aside>
|
|
</section>
|
|
|
|
<section class="slide code-slide" data-title="Backend Presence">
|
|
<h2>Join and Presence Events</h2>
|
|
<p class="subtitle">After connection, the server informs the room and refreshes online users.</p>
|
|
<div class="code-window">
|
|
<div class="code-titlebar"><span></span><span></span><span></span><strong>app/websocket/chat.py</strong></div>
|
|
<pre><code><span class="line"><span class="kw">await</span> manager.broadcast_json(</span>
|
|
<span class="line"> event(<span class="str">"system"</span>, room_id, <span class="str">"system"</span>, <span class="str">f"{client_id} joined {room_id}."</span>),</span>
|
|
<span class="line"> room_id,</span>
|
|
<span class="line">)</span>
|
|
<span class="line"></span>
|
|
<span class="line"><span class="kw">await</span> manager.broadcast_json({</span>
|
|
<span class="line"> <span class="str">"type"</span>: <span class="str">"presence"</span>,</span>
|
|
<span class="line"> <span class="str">"users"</span>: manager.room_users(room_id),</span>
|
|
<span class="line"> <span class="str">"timestamp"</span>: now_iso(),</span>
|
|
<span class="line">}, room_id)</span></code></pre>
|
|
</div>
|
|
<aside class="notes">Presence is separate from normal chat messages, so the frontend can update the sidebar without adding a chat bubble.</aside>
|
|
</section>
|
|
|
|
<section class="slide code-slide" data-title="Backend Receive Loop">
|
|
<h2>Receive Loop and Broadcast</h2>
|
|
<p class="subtitle">The socket stays open and every valid message is broadcast to the room.</p>
|
|
<div class="code-window">
|
|
<div class="code-titlebar"><span></span><span></span><span></span><strong>app/websocket/chat.py</strong></div>
|
|
<pre><code><span class="line"><span class="kw">while</span> True:</span>
|
|
<span class="line"> raw_data = <span class="kw">await</span> websocket.receive_text()</span>
|
|
<span class="line"></span>
|
|
<span class="line"> <span class="kw">try</span>:</span>
|
|
<span class="line"> data = json.loads(raw_data)</span>
|
|
<span class="line"> message = str(data.get(<span class="str">"content"</span>, <span class="str">""</span>)).strip()</span>
|
|
<span class="line"> <span class="kw">except</span> json.JSONDecodeError:</span>
|
|
<span class="line"> message = raw_data.strip()</span>
|
|
<span class="line"></span>
|
|
<span class="line"> <span class="kw">if</span> message:</span>
|
|
<span class="line"> <span class="kw">await</span> manager.broadcast_json(event(<span class="str">"message"</span>, room_id, client_id, message), room_id)</span></code></pre>
|
|
</div>
|
|
<aside class="notes">This is the main real-time loop: receive text, parse JSON if possible, ignore empty messages, then broadcast.</aside>
|
|
</section>
|
|
|
|
<section class="slide code-slide" data-title="Backend Manager">
|
|
<h2>ConnectionManager</h2>
|
|
<p class="subtitle">The manager groups sockets by room and removes dead connections.</p>
|
|
<div class="code-window">
|
|
<div class="code-titlebar"><span></span><span></span><span></span><strong>app/websocket/manager.py</strong></div>
|
|
<pre><code><span class="line"><span class="kw">class</span> <span class="fn">ConnectionManager</span>:</span>
|
|
<span class="line"> <span class="kw">def</span> <span class="fn">__init__</span>(self):</span>
|
|
<span class="line"> self.active_connections = {}</span>
|
|
<span class="line"></span>
|
|
<span class="line"> <span class="kw">async def</span> <span class="fn">connect</span>(self, websocket, room_id, client_id):</span>
|
|
<span class="line"> <span class="kw">await</span> websocket.accept()</span>
|
|
<span class="line"> self.active_connections.setdefault(room_id, [])</span>
|
|
<span class="line"> self.active_connections[room_id].append(ClientConnection(websocket, client_id))</span>
|
|
<span class="line"></span>
|
|
<span class="line"> <span class="kw">async def</span> <span class="fn">broadcast_json</span>(self, payload, room_id):</span>
|
|
<span class="line"> <span class="kw">for</span> connection <span class="kw">in</span> list(self.active_connections.get(room_id, [])):</span>
|
|
<span class="line"> <span class="kw">await</span> connection.websocket.send_json(payload)</span></code></pre>
|
|
</div>
|
|
<aside class="notes">This slide explains the backend data structure: each room points to a list of active client sockets.</aside>
|
|
</section>
|
|
|
|
<section class="slide code-slide" data-title="Frontend State">
|
|
<h2>Frontend Codes</h2>
|
|
<p class="subtitle">browser state and DOM references.</p>
|
|
<div class="code-window">
|
|
<div class="code-titlebar"><span></span><span></span><span></span><strong>app/static/app.js</strong></div>
|
|
<pre><code><span class="line"><span class="kw">const</span> usernameInput = document.querySelector(<span class="str">'#username'</span>);</span>
|
|
<span class="line"><span class="kw">const</span> roomInput = document.querySelector(<span class="str">'#room'</span>);</span>
|
|
<span class="line"><span class="kw">const</span> connectBtn = document.querySelector(<span class="str">'#connectBtn'</span>);</span>
|
|
<span class="line"><span class="kw">const</span> messageForm = document.querySelector(<span class="str">'#messageForm'</span>);</span>
|
|
<span class="line"><span class="kw">const</span> messages = document.querySelector(<span class="str">'#messages'</span>);</span>
|
|
<span class="line"><span class="kw">const</span> usersList = document.querySelector(<span class="str">'#usersList'</span>);</span>
|
|
<span class="line"></span>
|
|
<span class="line"><span class="kw">let</span> socket = <span class="num">null</span>;</span>
|
|
<span class="line"><span class="kw">let</span> currentUser = <span class="str">''</span>;</span>
|
|
<span class="line"><span class="kw">let</span> currentRoom = <span class="str">''</span>;</span></code></pre>
|
|
</div>
|
|
<aside class="notes">You can introduce the frontend file by showing that the UI stores one WebSocket object and the current room/user state.</aside>
|
|
</section>
|
|
|
|
<section class="slide code-slide" data-title="Frontend URL">
|
|
<h2>Building the WebSocket URL</h2>
|
|
<p class="subtitle">The browser automatically switches between ws and wss.</p>
|
|
<div class="code-window">
|
|
<div class="code-titlebar"><span></span><span></span><span></span><strong>app/static/app.js</strong></div>
|
|
<pre><code><span class="line"><span class="kw">function</span> <span class="fn">connect</span>() {</span>
|
|
<span class="line"> currentUser = safeName(usernameInput.value, <span class="str">'anonymous'</span>);</span>
|
|
<span class="line"> currentRoom = safeName(roomInput.value, <span class="str">'general'</span>);</span>
|
|
<span class="line"></span>
|
|
<span class="line"> <span class="kw">const</span> wsProtocol = window.location.protocol === <span class="str">'https:'</span> ? <span class="str">'wss'</span> : <span class="str">'ws'</span>;</span>
|
|
<span class="line"> <span class="kw">const</span> room = encodeURIComponent(currentRoom);</span>
|
|
<span class="line"> <span class="kw">const</span> user = encodeURIComponent(currentUser);</span>
|
|
<span class="line"> <span class="kw">const</span> wsUrl = <span class="str">`${wsProtocol}://${window.location.host}/ws/${room}/${user}`</span>;</span>
|
|
<span class="line"></span>
|
|
<span class="line"> socket = <span class="kw">new</span> WebSocket(wsUrl);</span>
|
|
<span class="line">}</span></code></pre>
|
|
</div>
|
|
<aside class="notes">This is why the same frontend works locally and behind HTTPS on the server.</aside>
|
|
</section>
|
|
|
|
<section class="slide code-slide" data-title="Frontend Open Event">
|
|
<h2>Connection Open Handler</h2>
|
|
<p class="subtitle">When the socket opens, the UI moves into connected mode.</p>
|
|
<div class="code-window">
|
|
<div class="code-titlebar"><span></span><span></span><span></span><strong>app/static/app.js</strong></div>
|
|
<pre><code><span class="line">socket.addEventListener(<span class="str">'open'</span>, () => {</span>
|
|
<span class="line"> setConnectedState(<span class="num">true</span>);</span>
|
|
<span class="line"></span>
|
|
<span class="line"> roomTitle.textContent = <span class="str">`Room: ${currentRoom}`</span>;</span>
|
|
<span class="line"> roomSubtitle.textContent = <span class="str">`You are chatting as ${currentUser}.`</span>;</span>
|
|
<span class="line"></span>
|
|
<span class="line"> messageInput.focus();</span>
|
|
<span class="line">});</span></code></pre>
|
|
</div>
|
|
<aside class="notes">This slide connects WebSocket state to UI state: buttons, labels, and the message input change after the connection opens.</aside>
|
|
</section>
|
|
|
|
<section class="slide code-slide" data-title="Frontend Message Event">
|
|
<h2>Handling Incoming Messages</h2>
|
|
<p class="subtitle">Presence updates and chat messages are separated by type.</p>
|
|
<div class="code-window">
|
|
<div class="code-titlebar"><span></span><span></span><span></span><strong>app/static/app.js</strong></div>
|
|
<pre><code><span class="line">socket.addEventListener(<span class="str">'message'</span>, (event) => {</span>
|
|
<span class="line"> <span class="kw">const</span> payload = JSON.parse(event.data);</span>
|
|
<span class="line"></span>
|
|
<span class="line"> <span class="kw">if</span> (payload.type === <span class="str">'presence'</span>) {</span>
|
|
<span class="line"> updateUsers(payload.users || []);</span>
|
|
<span class="line"> <span class="kw">return</span>;</span>
|
|
<span class="line"> }</span>
|
|
<span class="line"></span>
|
|
<span class="line"> addMessage(payload);</span>
|
|
<span class="line">});</span></code></pre>
|
|
</div>
|
|
<aside class="notes">The frontend treats presence as sidebar data and normal messages as chat feed data.</aside>
|
|
</section>
|
|
|
|
<section class="slide code-slide" data-title="Frontend Render">
|
|
<h2>Rendering a Chat Message</h2>
|
|
<p class="subtitle">The browser creates message elements without reloading the page.</p>
|
|
<div class="code-window">
|
|
<div class="code-titlebar"><span></span><span></span><span></span><strong>app/static/app.js</strong></div>
|
|
<pre><code><span class="line"><span class="kw">function</span> <span class="fn">addMessage</span>(payload) {</span>
|
|
<span class="line"> clearEmptyState();</span>
|
|
<span class="line"></span>
|
|
<span class="line"> <span class="kw">const</span> card = document.createElement(<span class="str">'article'</span>);</span>
|
|
<span class="line"> card.className = <span class="str">'message'</span>;</span>
|
|
<span class="line"></span>
|
|
<span class="line"> <span class="kw">if</span> (payload.client_id === currentUser) card.classList.add(<span class="str">'mine'</span>);</span>
|
|
<span class="line"></span>
|
|
<span class="line"> content.textContent = payload.content || <span class="str">''</span>;</span>
|
|
<span class="line"> card.append(meta, content);</span>
|
|
<span class="line"> messages.appendChild(card);</span>
|
|
<span class="line"> messages.scrollTop = messages.scrollHeight;</span>
|
|
<span class="line">}</span></code></pre>
|
|
</div>
|
|
<aside class="notes">This demonstrates DOM rendering and auto-scrolling. The UI changes because WebSocket data arrives, not because the page refreshes.</aside>
|
|
</section>
|
|
|
|
<section class="slide code-slide" data-title="Frontend Send">
|
|
<h2>Sending Messages</h2>
|
|
<p class="subtitle">The form sends JSON only when the socket is open.</p>
|
|
<div class="code-window">
|
|
<div class="code-titlebar"><span></span><span></span><span></span><strong>app/static/app.js</strong></div>
|
|
<pre><code><span class="line">messageForm.addEventListener(<span class="str">'submit'</span>, (event) => {</span>
|
|
<span class="line"> event.preventDefault();</span>
|
|
<span class="line"></span>
|
|
<span class="line"> <span class="kw">const</span> content = messageInput.value.trim();</span>
|
|
<span class="line"> <span class="kw">if</span> (!content || !socket || socket.readyState !== WebSocket.OPEN) <span class="kw">return</span>;</span>
|
|
<span class="line"></span>
|
|
<span class="line"> socket.send(JSON.stringify({ content }));</span>
|
|
<span class="line"> messageInput.value = <span class="str">''</span>;</span>
|
|
<span class="line"> messageInput.focus();</span>
|
|
<span class="line">});</span></code></pre>
|
|
</div>
|
|
<aside class="notes">This is the browser-to-server direction. The server receives this JSON and broadcasts the content to the room.</aside>
|
|
</section>
|
|
|
|
<section class="slide" data-title="Takeaways">
|
|
<h2>Keynotes and Takeaways</h2>
|
|
<p class="subtitle">The essential points to remember from the project.</p>
|
|
<div class="takeaway-grid point-cards">
|
|
<article><b>HTTP is request/response</b><ul><li>Simple</li><li>Stateless</li><li>Not ideal for live push</li></ul></article>
|
|
<article><b>Polling simulates real-time</b><ul><li>Easy</li><li>Wasteful</li><li>Interval-based latency</li></ul></article>
|
|
<article><b>WebSocket keeps a channel</b><ul><li>Persistent</li><li>Full-duplex</li><li>Low latency</li></ul></article>
|
|
<article><b>Server design matters</b><ul><li>Connection state</li><li>Disconnect handling</li><li>Scaling strategy</li></ul></article>
|
|
</div>
|
|
<aside class="notes">End with the central idea: WebSocket is useful when real-time bidirectional communication matters, but it moves responsibility to the server to manage long-lived connections.</aside>
|
|
</section>
|
|
</main>
|
|
|
|
<div class="controls" aria-hidden="true">
|
|
<button id="prev" title="Previous"><</button>
|
|
<span id="counter">1 / 10</span>
|
|
<button id="next" title="Next">></button>
|
|
</div>
|
|
<div class="hint">Left / Right to move - N for notes - F for fullscreen</div>
|
|
<div class="progress"><span id="progressBar"></span></div>
|
|
<div class="speaker-notes" id="speakerNotes"></div>
|
|
<script src="script.js"></script>
|
|
</body>
|
|
</html>
|