So I saw a thread recently where a developer got marked down in a course for suggesting WebSockets and TCP as the foundation for a chat application. The instructor insisted that PHP was the only valid approach for web development.
Look, I'm not here to bash PHP — I've shipped production PHP code and it pays bills. But if someone tells you WebSockets aren't a legitimate approach for real-time chat, that's a fundamental misunderstanding of how the modern web works. Let me walk you through exactly why WebSockets exist, how they solve the real-time problem, and how to build a working chat app with them.
The Core Problem: HTTP Wasn't Built for Real-Time
HTTP follows a request-response model. The client asks, the server answers. That's it. If you want to build a chat app on pure HTTP, you're stuck with one of these workarounds:
- Polling: Hit the server every few seconds asking "any new messages?" Wasteful, laggy, and brutal on your server at scale.
- Long polling: Hold the connection open until there's data, then reconnect. Better, but still hacky and resource-heavy.
- Server-Sent Events (SSE): One-way stream from server to client. Closer, but chat needs two-way communication.
I spent a painful weekend in 2019 trying to make polling work for a collaborative editing tool. The lag was unbearable and my server was fielding thousands of empty responses per minute. That's when I actually sat down and learned WebSockets properly.
Why WebSockets Are the Right Tool
WebSockets (defined in RFC 6455) establish a persistent, full-duplex connection between client and server. After an initial HTTP handshake, the connection upgrades to the WebSocket protocol and stays open. Both sides can send messages at any time without the overhead of new HTTP requests.
Here's what that handshake looks like at the protocol level:
GET /chat HTTP/1.1
Host: example.com
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==
Sec-WebSocket-Version: 13
The server responds with a 101 Switching Protocols, and now you've got a bidirectional pipe. No more polling. No more wasted requests. Messages arrive the instant they're sent.
Step-by-Step: Building the Chat Server
Let's build a minimal but functional chat server using Node.js and the ws library. You could do this in Python, Go, Rust, Java — the concept is identical.
1. Set Up the Server
import { WebSocketServer } from 'ws';
const wss = new WebSocketServer({ port: 8080 });
// Track all connected clients
const clients = new Set();
wss.on('connection', (ws) => {
clients.add(ws);
console.log(`Client connected. Total: ${clients.size}`);
ws.on('message', (data) => {
const message = data.toString();
// Broadcast to every client except the sender
for (const client of clients) {
if (client !== ws && client.readyState === 1) {
client.send(message);
}
}
});
ws.on('close', () => {
clients.delete(ws);
console.log(`Client disconnected. Total: ${clients.size}`);
});
});
console.log('WebSocket server running on ws://localhost:8080');
That's a working broadcast chat server in about 20 lines. Every message one client sends gets forwarded to all other connected clients instantly.
2. Build the Client
<!DOCTYPE html>
<html>
<body>
<div id="messages"></div>
<input id="input" placeholder="Type a message..." />
<button onclick="send()">Send</button>
<script>
const ws = new WebSocket('ws://localhost:8080');
const messagesDiv = document.getElementById('messages');
const input = document.getElementById('input');
ws.onmessage = (event) => {
const el = document.createElement('p');
el.textContent = event.data;
messagesDiv.appendChild(el);
};
// Handle unexpected disconnects gracefully
ws.onclose = () => {
const el = document.createElement('p');
el.textContent = '⚠ Connection lost. Refresh to reconnect.';
messagesDiv.appendChild(el);
};
function send() {
if (input.value.trim()) {
ws.send(input.value);
input.value = '';
}
}
// Allow sending with Enter key
input.addEventListener('keydown', (e) => {
if (e.key === 'Enter') send();
});
</script>
</body>
</html>
Open that HTML file in two browser tabs, type in one, and watch the message appear in the other tab instantly. No polling. No page refreshes. This is how real-time communication is supposed to work.
Where TCP Fits In
Here's a point worth clarifying since it trips people up. WebSockets run on top of TCP. Every WebSocket connection is a TCP connection under the hood. TCP handles the reliable, ordered delivery of bytes. WebSockets add the framing, the upgrade handshake, and the message-based abstraction on top of that.
So saying "use WebSockets and TCP for a chat app" isn't naming two competing technologies — it's describing the actual protocol stack. That's like saying "I used HTTP and TCP for a REST API." Yeah, you did. That's how it works.
Making It Production-Ready
The example above works, but it won't survive production traffic. Here's what you'd need to add:
- Reconnection logic: Connections drop. Your client should automatically reconnect with exponential backoff. Don't just show an error and give up.
- Message persistence: Store messages in a database so users see history when they reconnect. WebSockets are for transport, not storage.
- Authentication: Validate tokens during the handshake. Don't let anonymous connections flood your server.
- Rate limiting: Without it, one misbehaving client can spam everyone. Add a message-per-second cap.
- Horizontal scaling: A single server's WebSocket connections don't know about another server's connections. Use a pub/sub layer like Redis to broadcast across multiple server instances.
// Simple reconnection with exponential backoff
function connectWithRetry(url, attempt = 0) {
const ws = new WebSocket(url);
ws.onopen = () => {
console.log('Connected');
attempt = 0; // Reset on successful connection
};
ws.onclose = () => {
const delay = Math.min(1000 * Math.pow(2, attempt), 30000);
console.log(`Reconnecting in ${delay}ms...`);
setTimeout(() => connectWithRetry(url, attempt + 1), delay);
};
return ws;
}
But What About PHP?
PHP can handle WebSockets. Projects like Ratchet and Swoole exist for exactly this reason. The traditional PHP execution model — spin up a process per request, run the script, die — doesn't naturally fit persistent connections. But the ecosystem has evolved.
The real issue isn't PHP vs. Node vs. anything else. It's understanding that different problems call for different tools. A CRUD app serving HTML forms? PHP is perfectly fine. A real-time chat system that needs persistent bidirectional connections? WebSockets are the industry-standard approach, regardless of what backend language you choose.
The Takeaway
If someone tells you there's only one valid technology for web development, that's a red flag. The web is built on layers of protocols working together — TCP, HTTP, WebSockets, and others — each solving specific problems.
For real-time chat, WebSockets aren't just a valid approach. They're the approach that every major chat platform uses in some form. Discord, Slack, and every live chat widget you've ever used — they're all running on persistent connections, not polling an endpoint every two seconds.
Build the thing. Use the right tool. Don't let anyone tell you the entire internet runs on one technology.
Top comments (0)