WebSocket ile Gerçek Zamanlı Uygulama Geliştirme: Socket.IO ve Node.js

WebSocket ile Gerçek Zamanlı Uygulama Geliştirme: Socket.IO ve Node.js

HTTP'nin Sınırları ve WebSocket'in Doğuşu

Klasik HTTP modelinde istemci bir istek gönderir, sunucu yanıt verir ve bağlantı kapanır. Canlı bildirimler, gerçek zamanlı sohbet veya anlık değişen veri için bu model yetersiz kalır. WebSocket, istemci ile sunucu arasında tek bir TCP bağlantısı üzerinden sürekli, çift yönlü iletişim kurarak bu sorunu kökten çözer.

HTTP Polling vs WebSocket: Fark Ne Kadar?

ÖzellikHTTP PollingWebSocket
BağlantıHer istek için yeniTek, kalıcı
GecikmeYüksek (polling aralığı)Çok düşük (ms)
Sunucu YüküYüksek (gereksiz istek)Düşük
Veri AkışıTek yön (istemci → sunucu)Çift yön
İdeal KullanımSeyrek güncellenen veriCanlı chat, oyun, finans

Socket.IO ile Sunucu Kurulumu

Ham WebSocket API yerine, otomatik reconnect ve oda (room) yönetimi gibi üst düzey özellikler sunan Socket.IO kullanacağız:

npm install socket.io express
// 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: "http://localhost:3000",  // Next.js dev sunucusu
    methods: ["GET", "POST"]
  }
});

// Bağlanan kullanıcıları tut
const bagliKullanicilar = new Map();

io.on('connection', (socket) => {
  console.log(`✅ Yeni bağlantı: ${socket.id}`);

  // Kullanıcı giriş eventi
  socket.on('kullanici_giris', (kullanici) => {
    bagliKullanicilar.set(socket.id, kullanici);

    // Sadece bu kullanıcıya: "Hoşgeldin"
    socket.emit('mesaj', {
      tur: 'sistem',
      icerik: `Hoşgeldin, ${kullanici.ad}!`,
      zaman: new Date()
    });

    // Herkese bildir (giren kişi hariç)
    socket.broadcast.emit('kullanici_katildi', {
      id: socket.id,
      ad: kullanici.ad
    });
  });

  // Mesaj eventi
  socket.on('mesaj_gonder', (veri) => {
    const gonderen = bagliKullanicilar.get(socket.id);
    if (!gonderen) return;

    // Mesajı odadaki HERKES'e gönder (gönderen dahil)
    io.emit('yeni_mesaj', {
      id: Date.now(),
      gonderen: gonderen.ad,
      icerik: veri.icerik,
      zaman: new Date()
    });
  });

  // Özel oda sistemi
  socket.on('odaya_katil', (odaId) => {
    socket.join(odaId);
    socket.to(odaId).emit('mesaj', {
      tur: 'sistem',
      icerik: `${bagliKullanicilar.get(socket.id)?.ad} odaya katıldı`
    });
  });

  // Bağlantı kesilince temizle
  socket.on('disconnect', () => {
    const kullanici = bagliKullanicilar.get(socket.id);
    bagliKullanicilar.delete(socket.id);

    if (kullanici) {
      io.emit('kullanici_ayrildi', { ad: kullanici.ad });
    }
    console.log(`❌ Bağlantı kesildi: ${socket.id}`);
  });
});

httpServer.listen(4000, () => {
  console.log('Socket.IO sunucusu :4000 portunda çalışıyor');
});

React İstemcisi (Next.js ile)

npm install socket.io-client
// hooks/useSocket.ts
import { useEffect, useRef, useState } from 'react';
import { io, Socket } from 'socket.io-client';

export function useSocket(kullanici: { ad: string }) {
  const socketRef = useRef(null);
  const [mesajlar, setMesajlar] = useState([]);
  const [bagliMi, setBagliMi] = useState(false);

  useEffect(() => {
    // Bağlantı kur
    socketRef.current = io('http://localhost:4000', {
      reconnectionDelay: 1000,
      reconnectionAttempts: 5
    });

    const socket = socketRef.current;

    socket.on('connect', () => {
      setBagliMi(true);
      socket.emit('kullanici_giris', kullanici);
    });

    socket.on('disconnect', () => setBagliMi(false));

    socket.on('yeni_mesaj', (mesaj) => {
      setMesajlar(prev => [...prev, mesaj]);
    });

    // Temizleme: component unmount'ta bağlantıyı kapat
    return () => {
      socket.disconnect();
    };
  }, []); // eslint-disable-line react-hooks/exhaustive-deps

  const mesajGonder = (icerik: string) => {
    socketRef.current?.emit('mesaj_gonder', { icerik });
  };

  return { mesajlar, bagliMi, mesajGonder };
}

// ChatBileşeni.tsx
export default function Chat() {
  const { mesajlar, bagliMi, mesajGonder } = useSocket({ ad: 'Kullanıcı' });
  const [input, setInput] = useState('');

  return (
    <div className="flex flex-col h-screen bg-gray-950">
      {/* Bağlantı durumu */}
      <div className={`px-4 py-2 text-sm ${bagliMi ? 'text-green-400' : 'text-red-400'}`}>
        {bagliMi ? '🟢 Bağlı' : '🔴 Bağlantı kesildi'}
      </div>

      {/* Mesaj listesi */}
      <div className="flex-1 overflow-y-auto p-4 space-y-3">
        {mesajlar.map(m => (
          <div key={m.id} className="bg-gray-800 rounded-lg p-3">
            <span className="text-blue-400 font-bold">{m.gonderen}: </span>
            <span className="text-gray-200">{m.icerik}</span>
          </div>
        ))}
      </div>

      {/* Input alanı */}
      <form className="p-4 flex gap-2"
        onSubmit={e => { e.preventDefault(); mesajGonder(input); setInput(''); }}>
        <input value={input} onChange={e => setInput(e.target.value)}
          className="flex-1 bg-gray-800 rounded-lg px-4 py-2 text-white" />
        <button type="submit" className="bg-indigo-600 px-6 py-2 rounded-lg text-white">
          Gönder
        </button>
      </form>
    </div>
  );
}

Ölçeklendirme: Birden Fazla Sunucu

Tek bir sunucu tüm bağlantıları taşıyamadığında, Socket.IO'yu birden fazla Node.js instance'ında çalıştırmak için Redis Adapter kullanılır. Bu sayede Sunucu A'daki istemci, Sunucu B'deki istemciye mesaj gönderebilir.

const { createAdapter } = require('@socket.io/redis-adapter');
const { createClient } = require('redis');

const pubClient = createClient({ url: process.env.REDIS_URL });
const subClient = pubClient.duplicate();

await Promise.all([pubClient.connect(), subClient.connect()]);

// Redis adapter'ı bağla — artık tüm sunucular senkron
io.adapter(createAdapter(pubClient, subClient));
🔌 WebSocket Kullanım Alanları
  • Canlı sohbet: Discord, Slack benzeri mesajlaşma uygulamaları
  • Ortak düzenleme: Figma, Google Docs tarzı gerçek zamanlı işbirliği
  • Oyun: Çok oyunculu tarayıcı tabanlı oyunlar
  • Finans: Anlık kripto/hisse fiyat akışları
  • Bildirimler: Sipariş takibi, canlı dashboard güncellemeleri

0 Yorum

YORUM YAPMAK İÇİN SİSTEME SIZMANIZ GEREKİYOR

Lütfen yukarıdaki butonu kullanarak giriş yapın veya kimlik oluşturun.

Yorumlar yükleniyor...