Node.js WebSocket: Gerçek Zamanlı Uygulamalar

Node.js WebSocket: Gerçek Zamanlı Uygulamalar
WebSocket, istemci ve sunucu arasında tam çift yönlü (full-duplex) iletişim sağlayan bir protokoldür. HTTP'nin istek-yanıt modelinin aksine, WebSocket bağlantısı bir kez kurulduktan sonra her iki taraf da istedikleri zaman veri gönderebilir. Bu rehberde Node.js ile WebSocket kullanarak gerçek zamanlı uygulamalar geliştirmeyi adım adım ele alacağız.
1. WebSocket vs HTTP
| Özellik | HTTP | WebSocket |
|---|---|---|
| İletişim | Tek yönlü (istek-yanıt) | Çift yönlü |
| Bağlantı | Her istekte yeni | Sürekli (persistent) |
| Overhead | Her istekte header | Minimal frame header |
| Gecikme | Yüksek | Düşük |
| Kullanım | CRUD işlemleri | Gerçek zamanlı veri akışı |
2. Socket.IO ile Başlangıç
Socket.IO, WebSocket üzerine inşa edilmiş, otomatik yeniden bağlanma, room desteği ve fallback mekanizmaları sunan popüler bir kütüphanedir.
# Kurulum
npm install socket.io
# İstemci tarafı
npm install socket.io-client
Sunucu Tarafı
// server.js
const express = require('express');
const { createServer } = require('http');
const { Server } = require('socket.io');
const app = express();
const httpServer = createServer(app);
const io = new Server(httpServer, {
cors: {
origin: process.env.CLIENT_URL || 'http://localhost:3000',
methods: ['GET', 'POST'],
},
pingTimeout: 60000,
pingInterval: 25000,
});
// Bağlantı dinleme
io.on('connection', (socket) => {
console.log(`Kullanıcı bağlandı: ${socket.id}`);
// Mesaj alma
socket.on('message', (data) => {
console.log('Mesaj alındı:', data);
// Tüm bağlı istemcilere gönder
io.emit('message', {
...data,
timestamp: new Date().toISOString(),
});
});
// Bağlantı kopma
socket.on('disconnect', (reason) => {
console.log(`Kullanıcı ayrıldı: ${socket.id}, Sebep: ${reason}`);
});
});
httpServer.listen(3001, () => {
console.log('WebSocket sunucusu: port 3001');
});
İstemci Tarafı
// client.js
import { io } from 'socket.io-client';
const socket = io('http://localhost:3001', {
autoConnect: true,
reconnection: true,
reconnectionAttempts: 5,
reconnectionDelay: 1000,
});
socket.on('connect', () => {
console.log('Sunucuya bağlandı:', socket.id);
});
// Mesaj gönder
socket.emit('message', { text: 'Merhaba!', user: 'Tarık' });
// Mesaj al
socket.on('message', (data) => {
console.log('Yeni mesaj:', data);
});
socket.on('disconnect', () => {
console.log('Bağlantı kesildi');
});
3. Room ve Namespace
Room'lar, bağlı istemcileri gruplara ayırmanızı sağlar. Namespace'ler ise farklı iletişim kanalları oluşturur.
// Room yönetimi
io.on('connection', (socket) => {
// Odaya katıl
socket.on('joinRoom', (roomId) => {
socket.join(roomId);
socket.to(roomId).emit('userJoined', {
userId: socket.id,
message: 'Yeni kullanıcı odaya katıldı',
});
console.log(`${socket.id} odaya katıldı: ${roomId}`);
});
// Odadan ayrıl
socket.on('leaveRoom', (roomId) => {
socket.leave(roomId);
socket.to(roomId).emit('userLeft', { userId: socket.id });
});
// Belirli odaya mesaj gönder
socket.on('roomMessage', ({ roomId, message }) => {
io.to(roomId).emit('message', {
sender: socket.id,
message,
roomId,
timestamp: Date.now(),
});
});
});
// Namespace kullanımı
const chatNamespace = io.of('/chat');
const notificationNamespace = io.of('/notifications');
chatNamespace.on('connection', (socket) => {
console.log('Chat bağlantısı:', socket.id);
});
notificationNamespace.on('connection', (socket) => {
console.log('Notification bağlantısı:', socket.id);
});
4. Gerçek Zamanlı Sohbet Uygulaması
// chat/server.js
const activeUsers = new Map();
io.on('connection', (socket) => {
// Kullanıcı kaydı
socket.on('register', (userData) => {
activeUsers.set(socket.id, {
...userData,
socketId: socket.id,
joinedAt: Date.now(),
});
io.emit('activeUsers', Array.from(activeUsers.values()));
});
// Sohbet mesajı
socket.on('chatMessage', async ({ roomId, text }) => {
const user = activeUsers.get(socket.id);
if (!user) return;
const message = {
id: crypto.randomUUID(),
text,
sender: { id: user.id, name: user.name },
roomId,
timestamp: new Date().toISOString(),
};
// Mesajı veritabanına kaydet
await db.saveMessage(message);
// Odadaki herkese gönder
io.to(roomId).emit('chatMessage', message);
});
// Yazıyor göstergesi
socket.on('typing', ({ roomId }) => {
const user = activeUsers.get(socket.id);
if (user) {
socket.to(roomId).emit('typing', { userId: user.id, name: user.name });
}
});
socket.on('stopTyping', ({ roomId }) => {
socket.to(roomId).emit('stopTyping', { userId: activeUsers.get(socket.id)?.id });
});
// Bağlantı kopma
socket.on('disconnect', () => {
activeUsers.delete(socket.id);
io.emit('activeUsers', Array.from(activeUsers.values()));
});
});
5. Authentication
const jwt = require('jsonwebtoken');
// Middleware ile authentication
io.use((socket, next) => {
const token = socket.handshake.auth.token;
if (!token) {
return next(new Error('Yetkilendirme gerekli'));
}
try {
const decoded = jwt.verify(token, process.env.JWT_SECRET);
socket.user = decoded;
next();
} catch (err) {
next(new Error('Geçersiz token'));
}
});
// İstemci tarafında
const socket = io('http://localhost:3001', {
auth: {
token: localStorage.getItem('authToken'),
},
});
socket.on('connect_error', (err) => {
if (err.message === 'Geçersiz token') {
// Token yenile veya login sayfasına yönlendir
}
});
6. Scaling — Redis Adapter
Birden fazla Node.js process veya sunucu çalıştırıyorsanız, Redis adapter ile WebSocket mesajlarını senkronize etmeniz gerekir.
# Kurulum
npm install @socket.io/redis-adapter ioredis
const { createAdapter } = require('@socket.io/redis-adapter');
const { createClient } = require('redis');
async function setupRedisAdapter() {
const pubClient = createClient({ url: process.env.REDIS_URL });
const subClient = pubClient.duplicate();
await Promise.all([pubClient.connect(), subClient.connect()]);
io.adapter(createAdapter(pubClient, subClient));
console.log('Redis adapter aktif — çoklu sunucu desteği hazır');
}
setupRedisAdapter();
7. Native ws Kütüphanesi
Socket.IO'nun ek özellikleri gerekmiyorsa, daha hafif olan ws kütüphanesini kullanabilirsiniz.
npm install ws
const WebSocket = require('ws');
const http = require('http');
const server = http.createServer();
const wss = new WebSocket.Server({ server });
wss.on('connection', (ws, req) => {
console.log('Yeni bağlantı');
ws.on('message', (data) => {
const message = JSON.parse(data.toString());
// Tüm bağlı istemcilere broadcast
wss.clients.forEach((client) => {
if (client.readyState === WebSocket.OPEN) {
client.send(JSON.stringify({
...message,
timestamp: Date.now(),
}));
}
});
});
ws.on('close', () => {
console.log('Bağlantı kapandı');
});
// Heartbeat
ws.isAlive = true;
ws.on('pong', () => { ws.isAlive = true; });
});
// Heartbeat interval — ölü bağlantıları temizle
setInterval(() => {
wss.clients.forEach((ws) => {
if (!ws.isAlive) return ws.terminate();
ws.isAlive = false;
ws.ping();
});
}, 30000);
server.listen(3001);
ws, zengin özellikler (room, namespace, otomatik yeniden bağlanma) için Socket.IO tercih edin. Socket.IO'nun binary verisi (ArrayBuffer, Blob) desteği de avantajdır.
8. Error Handling ve Reconnection
// Sunucu tarafı error handling
io.on('connection', (socket) => {
socket.on('error', (err) => {
console.error(`Socket hatası [${socket.id}]:`, err.message);
});
// Her event handler'da try-catch
socket.on('action', async (data, callback) => {
try {
const result = await processAction(data);
callback({ success: true, data: result });
} catch (error) {
callback({ success: false, error: error.message });
}
});
});
// İstemci tarafı reconnection yönetimi
socket.on('reconnect_attempt', (attemptNumber) => {
console.log(`Yeniden bağlanma denemesi: ${attemptNumber}`);
});
socket.on('reconnect', () => {
console.log('Yeniden bağlandı');
// Room'lara tekrar katıl
socket.emit('joinRoom', currentRoomId);
});
Sonuç
WebSocket, gerçek zamanlı uygulamalar için vazgeçilmez bir teknolojidir. Socket.IO ile hızlı geliştirme yapabilir, room ve namespace yapılarıyla organize iletişim kanalları oluşturabilirsiniz. Authentication middleware ile bağlantıları güvenli hale getirin, Redis adapter ile çoklu sunucu desteği ekleyin. Heartbeat mekanizması ile ölü bağlantıları temizleyin ve error handling ile uygulamanızın dayanıklılığını artırın. Sohbet, bildirim, canlı dashboard ve oyun gibi senaryolarda WebSocket mükemmel bir çözümdür.