Skillversitas

WebSocket

WebSocket provides two-way (full-duplex) communication between the client and the server over a single TCP connection.

WebSocket Basics

WebSocket Connection

<script>
    const socket = new WebSocket('ws://localhost:8080');

    socket.onopen = function(event) {
        console.log('Connection opened');
        socket.send('Hello Server!');
    };

    socket.onmessage = function(event) {
        console.log('Message from server:', event.data);
    };

    socket.onclose = function(event) {
        console.log('Connection closed');
    };

    socket.onerror = function(error) {
        console.log('Error:', error);
    };
</script>

Sending and Receiving Data

<script>
    const socket = new WebSocket('ws://localhost:8080');

    // Send a message
    function sendMessage(message) {
        if (socket.readyState === WebSocket.OPEN) {
            socket.send(JSON.stringify({
                type: 'chat',
                content: message
            }));
        }
    }

    // Receive a message
    socket.onmessage = function(event) {
        const data = JSON.parse(event.data);
        console.log('Type:', data.type);
        console.log('Content:', data.content);
    };
</script>

Case Study: Campus Real-time Chat

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Campus Chat</title>
    <style>
        * {
            margin: 0;
            padding: 0;
            box-sizing: border-box;
        }

        body {
            font-family: 'Segoe UI', sans-serif;
            background: #f0f2f5;
            height: 100vh;
            display: flex;
            flex-direction: column;
        }

        .header {
            background: #1e3a5f;
            color: white;
            padding: 1rem 1.5rem;
            display: flex;
            justify-content: space-between;
            align-items: center;
        }

        .header h1 {
            font-size: 1.3rem;
        }

        .status {
            display: flex;
            align-items: center;
            gap: 0.5rem;
        }

        .status-dot {
            width: 10px;
            height: 10px;
            border-radius: 50%;
            background: #e74c3c;
        }

        .status-dot.connected {
            background: #2ecc71;
        }

        .chat-container {
            flex: 1;
            display: flex;
            flex-direction: column;
            max-width: 800px;
            width: 100%;
            margin: 0 auto;
            padding: 1rem;
        }

        .messages {
            flex: 1;
            overflow-y: auto;
            padding: 1rem;
            background: white;
            border-radius: 8px 8px 0 0;
            border: 1px solid #ddd;
            border-bottom: none;
        }

        .message {
            margin-bottom: 1rem;
            display: flex;
            flex-direction: column;
        }

        .message.sent {
            align-items: flex-end;
        }

        .message.received {
            align-items: flex-start;
        }

        .message .bubble {
            max-width: 70%;
            padding: 0.8rem 1rem;
            border-radius: 12px;
            word-wrap: break-word;
        }

        .message.sent .bubble {
            background: #3498db;
            color: white;
            border-bottom-right-radius: 4px;
        }

        .message.received .bubble {
            background: #e9ecef;
            color: #333;
            border-bottom-left-radius: 4px;
        }

        .message .sender {
            font-size: 0.75rem;
            color: #666;
            margin-bottom: 0.25rem;
        }

        .message .time {
            font-size: 0.7rem;
            color: #999;
            margin-top: 0.25rem;
        }

        .input-area {
            display: flex;
            gap: 0.5rem;
            padding: 1rem;
            background: white;
            border: 1px solid #ddd;
            border-radius: 0 0 8px 8px;
        }

        .input-area input {
            flex: 1;
            padding: 0.8rem;
            border: 1px solid #ddd;
            border-radius: 4px;
            font-size: 1rem;
        }

        .input-area input:focus {
            outline: none;
            border-color: #3498db;
        }

        .input-area button {
            padding: 0.8rem 1.5rem;
            background: #3498db;
            color: white;
            border: none;
            border-radius: 4px;
            cursor: pointer;
        }

        .input-area button:hover {
            background: #2980b9;
        }

        .input-area button:disabled {
            background: #bdc3c7;
            cursor: not-allowed;
        }

        .users-list {
            padding: 1rem;
            background: #f8f9fa;
            border-bottom: 1px solid #ddd;
            font-size: 0.9rem;
            color: #666;
        }

        .users-list strong {
            color: #333;
        }
    </style>
</head>
<body>
    <div class="header">
        <h1>Campus Chat</h1>
        <div class="status">
            <div class="status-dot" id="statusDot"></div>
            <span id="statusText">Terputus</span>
        </div>
    </div>

    <div class="chat-container">
        <div class="users-list" id="usersList">
            <strong>Online users:</strong> Loading...
        </div>

        <div class="messages" id="messages">
            <div class="message system" style="text-align: center; color: #999; font-size: 0.85rem;">
                Welcome to Campus Chat!
            </div>
        </div>

        <div class="input-area">
            <input type="text" id="messageInput" placeholder="Type a message..." disabled>
            <button id="sendBtn" onclick="sendMessage()" disabled>Send</button>
        </div>
    </div>

    <script>
        let socket = null;
        let username = '';

        // Prompt username
        username = prompt('Enter your name:') || 'Anonymous';

        // WebSocket connection (simulation)
        function connect() {
            updateStatus('connecting');

            // Connection simulation (for a demo without a server)
            setTimeout(() => {
                updateStatus('connected');
                document.getElementById('messageInput').disabled = false;
                document.getElementById('sendBtn').disabled = false;

                // Simulate online users
                updateUsersList(['Anda', 'John', 'Sarah', 'Rahmat']);

                // Simulate incoming messages
                setTimeout(() => {
                    addMessage('John', 'Hello everyone!', 'received');
                }, 2000);

                setTimeout(() => {
                    addMessage('Sarah', 'Anything you'd like to ask?', 'received');
                }, 5000);
            }, 1000);

            // Real code (uncomment if you have a WebSocket server):
            /*
            socket = new WebSocket('ws://localhost:8080');

            socket.onopen = function() {
                updateStatus('connected');
                document.getElementById('messageInput').disabled = false;
                document.getElementById('sendBtn').disabled = false;

                // Send join message
                socket.send(JSON.stringify({
                    type: 'join',
                    username: username
                }));
            };

            socket.onmessage = function(event) {
                const data = JSON.parse(event.data);

                switch(data.type) {
                    case 'chat':
                        addMessage(data.sender, data.content, 
                            data.sender === username ? 'sent' : 'received');
                        break;
                    case 'users':
                        updateUsersList(data.users);
                        break;
                    case 'system':
                        addSystemMessage(data.content);
                        break;
                }
            };

            socket.onclose = function() {
                updateStatus('disconnected');
            };

            socket.onerror = function() {
                updateStatus('error');
            };
            */
        }

        function sendMessage() {
            const input = document.getElementById('messageInput');
            const message = input.value.trim();

            if (message) {
                addMessage(username, message, 'sent');

                // Send to server (simulation)
                console.log('Mengirim:', message);

                // Real code:
                /*
                if (socket && socket.readyState === WebSocket.OPEN) {
                    socket.send(JSON.stringify({
                        type: 'chat',
                        content: message
                    }));
                }
                */

                input.value = '';
            }
        }

        function addMessage(sender, content, type) {
            const messages = document.getElementById('messages');
            const time = new Date().toLocaleTimeString('id-ID', { 
                hour: '2-digit', 
                minute: '2-digit' 
            });

            const messageDiv = document.createElement('div');
            messageDiv.className = `message ${type}`;
            messageDiv.innerHTML = `
                ${type === 'received' ? `<span class="sender">${sender}</span>` : ''}
                <div class="bubble">${content}</div>
                <span class="time">${time}</span>
            `;

            messages.appendChild(messageDiv);
            messages.scrollTop = messages.scrollHeight;
        }

        function addSystemMessage(content) {
            const messages = document.getElementById('messages');
            const messageDiv = document.createElement('div');
            messageDiv.className = 'message system';
            messageDiv.style.textAlign = 'center';
            messageDiv.style.color = '#999';
            messageDiv.style.fontSize = '0.85rem';
            messageDiv.textContent = content;

            messages.appendChild(messageDiv);
            messages.scrollTop = messages.scrollHeight;
        }

        function updateStatus(status) {
            const dot = document.getElementById('statusDot');
            const text = document.getElementById('statusText');

            dot.className = 'status-dot';

            switch(status) {
                case 'connected':
                    dot.classList.add('connected');
                    text.textContent = 'Terhubung';
                    break;
                case 'connecting':
                    text.textContent = 'Menghubungkan...';
                    break;
                case 'disconnected':
                    text.textContent = 'Terputus';
                    break;
                case 'error':
                    text.textContent = 'Error';
                    break;
            }
        }

        function updateUsersList(users) {
            const list = document.getElementById('usersList');
            list.innerHTML = `<strong>Online users (${users.length}):</strong> ${users.join(', ')}`;
        }

        // Handle enter key
        document.getElementById('messageInput').addEventListener('keypress', function(e) {
            if (e.key === 'Enter') {
                sendMessage();
            }
        });

        // Start connection
        connect();
    </script>
</body>
</html>

Everyday Case Study

Case: A developer needs real-time notifications for a campus app.

<div id="notifications"></div>

<script>
    function connectNotification() {
        // WebSocket simulation for notifications
        setInterval(() => {
            const notifications = [
                'Tomorrow's class schedule has been updated',
                'New semester registration is now open',
                'Graduation announcement'
            ];

            const randomNotif = notifications[Math.floor(Math.random() * notifications.length)];
            showNotification(randomNotif);
        }, 10000);
    }

    function showNotification(message) {
        const div = document.createElement('div');
        div.style.cssText = 'background: #3498db; color: white; padding: 1rem; margin: 0.5rem 0; border-radius: 4px;';
        div.textContent = message;

        document.getElementById('notifications').prepend(div);

        setTimeout(() => div.remove(), 5000);
    }

    connectNotification();
</script>

Best Practices

  1. Reconnection: Handle dropped connections
  2. Heartbeat: Send pings periodically
  3. Message queue: Store messages when offline
  4. Security: Use wss:// (WebSocket Secure)
  5. Fallback: Use polling as a fallback

Summary

WebSocket enables two-way real-time communication. It's perfect for chat, live notifications, and apps that need real-time updates.