Node.js İle Socket.io Kullanarak Gerçek Zamanlı Oyun Lobisi Nasıl Yapılır?

Node.js İle Socket.io Kullanarak Gerçek Zamanlı Oyun Lobisi Nasıl Yapılır?
Node.js İle Socket.io Kullanarak Gerçek Zamanlı Oyun Lobisi Nasıl Yapılır?

Gereksinimler ve Ön Hazırlık

Projeye başlamadan önce sisteminizde Node.js'in güncel bir LTS (Long Term Support) sürümünün yüklü olduğundan emin olmalısınız. Socket.io, WebSocket protokolü üzerine inşa edilmiş güçlü bir kütüphanedir ve tarayıcı ile sunucu arasındaki bağlantıyı otomatik olarak yönetir.

Gerekli paketleri kurmak için proje dizininizde terminali açıp şu komutları çalıştırın:

mkdir oyun-lobisi
cd oyun-lobisi
npm init -y
npm install express socket.io

Bu komutlar, projenizi başlatacak ve gerekli bağımlılıkları node_modules klasörüne ekleyecektir. express, statik dosyaları sunmak ve HTTP yönlendirmelerini yönetmek için, socket.io ise gerçek zamanlı iletişim için kullanılacaktır.

Sunucu Tarafında Socket.io Kurulumu

Sunucu tarafı, tüm oyuncuların bağlandığı merkezdir. Burada bir HTTP sunucusu oluşturup Socket.io'yu bu sunucuya bağlamamız gerekir. Aşağıdaki kod, basit bir WebSocket sunucusu başlatır.

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);

io.on('connection', (socket) => {
    console.log('Yeni bir oyuncu bağlandı:', socket.id);

    socket.on('disconnect', () => {
        console.log('Oyuncu ayrıldı:', socket.id);
    });
});

httpServer.listen(3000, () => {
    console.log('Sunucu http://localhost:3000 üzerinde çalışıyor.');
});

Bu kod bloğunda io.on('connection') fonksiyonu, her yeni bağlantıda tetiklenir. socket.id, her kullanıcıya atanan benzersiz bir kimliktir ve oyun içi işlemlerde oyuncuları ayırt etmek için kullanılır.

Oyun Lobisi Odaları ve İletişim

Socket.io'nun "Rooms" (Odalar) özelliği, oyuncuları gruplandırmak için mükemmeldir. Bir oyun lobisi, aslında belirli bir odaya katılmış oyuncular kümesidir. Aşağıdaki örnekte, bir oyuncunun nasıl lobiye katılabileceğini ve odaya mesaj gönderebileceğini görebilirsiniz.

io.on('connection', (socket) => {
    socket.on('join-lobby', (lobbyId) => {
        socket.join(lobbyId);
        console.log(`Oyuncu ${socket.id}, ${lobbyId} nolu lobiye katıldı.`);
        
        // Lobiye katılan herkese haber ver
        io.to(lobbyId).emit('player-joined', { playerId: socket.id });
    });
});

Burada socket.join(lobbyId) metodu, oyuncuyu ilgili odaya dahil eder. io.to(lobbyId).emit ise sadece o odadaki oyunculara veri gönderir; bu, lobideki oyuncu listesini güncellemek için kritik bir yöntemdir.

İstemci Tarafı Entegrasyonu

Sunucu tarafını hazırladıktan sonra, tarayıcı tarafında Socket.io istemcisini kullanarak sunucuya bağlanmamız gerekir. İstemci kodunuzda socket.io-client kütüphanesini kullanmalısınız.

// client.js
const socket = io('http://localhost:3000');

function lobiyeKatil(lobbyId) {
    socket.emit('join-lobby', lobbyId);
}

socket.on('player-joined', (data) => {
    console.log('Yeni oyuncu katıldı:', data.playerId);
});

İstemci tarafında emit ile sunucuya bir olay göndeririz. Sunucu bu olayı dinler ve gerekli işlemleri yapar. Bu etkileşim, oyunun akıcılığı için temeldir.

Veri Güvenliği ve Doğrulama

Gerçek zamanlı uygulamalarda veri güvenliği hayati önem taşır. Kullanıcıların gönderdiği verileri asla doğrudan veritabanına veya diğer oyunculara yansıtmayın. Gelen verileri mutlaka doğrulayın.

Kritik Uyarı: İstemciden gelen veriler manipüle edilebilir. Sunucu tarafında her zaman "Input Validation" (Girdi Doğrulama) yapın. Oyuncunun bir lobiye katılma yetkisi olup olmadığını kontrol edin ve asla istemci tarafından gelen "oyuncu puanı" gibi verileri doğrudan kabul etmeyin; bu değerleri sunucuda hesaplayın.

socket.on('send-message', (data) => {
    // XSS koruması için basit bir temizleme
    const cleanMessage = data.message.replace(//g, ">");
    io.to(data.lobbyId).emit('new-message', {
        sender: socket.id,
        message: cleanMessage
    });
});

Performans Karşılaştırması: Socket.io vs Standart HTTP

Özellik Standart HTTP (REST) Socket.io (WebSocket)
İletişim Yönü Tek yönlü (İstemci başlatır) Çift yönlü (Tam duplex)
Gecikme Yüksek (Her istekte başlıklar gider) Düşük (Sürekli açık bağlantı)
Kullanım Alanı Veri çekme/yükleme Oyun, Sohbet, Canlı Skor

Sıkça Sorulan Sorular

Socket.io bağlantısı koptuğunda ne olur?

Socket.io, bağlantı koptuğunda otomatik olarak yeniden bağlanmaya (reconnect) çalışır. İstemci tarafında socket.on('disconnect') olayını dinleyerek kullanıcıya bir uyarı mesajı gösterebilirsiniz.

Bir lobiye aynı anda kaç kişi bağlanabilir?

Bu, sunucunuzun RAM ve CPU kapasitesine bağlıdır. Node.js tek iş parçacıklı olduğu için çok büyük ölçekli lobilerde "Redis Adapter" kullanarak yükü dağıtmanız önerilir.

Oyun lobisinde hile yapılması nasıl engellenir?

Tüm kritik oyun mantığını (oyuncu konumu, skor, oyun süresi) sunucu tarafında çalıştırın. İstemci sadece görselleştirme ve girdi gönderimi yapmalıdır.

HTTPS kullanmak zorunda mıyım?

Evet, üretim ortamında (production) mutlaka SSL/TLS sertifikası (HTTPS) kullanmalısınız. Aksi takdirde WebSocket bağlantıları güvenlik duvarları tarafından engellenebilir.

Socket.io yerine neden başka bir şey kullanmayayım?

Socket.io, bağlantı kopmalarını yönetme, otomatik yeniden bağlanma ve eski tarayıcılar için fallback (geriye dönük uyumluluk) mekanizmaları sunduğu için oyun geliştirme süreçlerinde en güvenilir seçenektir.

Gerçek Zamanlı Oyun Lobilerinde Hata Ayıklama ve İzleme

Gerçek zamanlı uygulamalarda, özellikle oyun lobileri gibi yüksek etkileşimli sistemlerde, hataların kaynağını bulmak standart HTTP uygulamalarına göre daha karmaşıktır. Bağlantı kopmaları, paket kayıpları veya senkronizasyon sorunları için gelişmiş izleme yöntemleri kullanmalısınız.

Socket.io Debug Modu ile İnceleme

Socket.io, geliştirme aşamasında tüm olayları konsola dökmenize olanak tanıyan bir hata ayıklama arayüzüne sahiptir. Sunucunuzu başlatırken DEBUG ortam değişkenini kullanarak trafiği izleyebilirsiniz:

# Linux/macOS üzerinde
DEBUG=socket.io:* node server.js

# Windows PowerShell üzerinde
$env:DEBUG="socket.io:*"; node server.js

Bu komut, istemci ile sunucu arasında gidip gelen tüm paketleri, el sıkışma (handshake) süreçlerini ve oda geçişlerini detaylı bir şekilde terminalinize yansıtacaktır. Özellikle packet ve engine loglarını inceleyerek, mesajların neden iletilmediğini veya hangi aşamada reddedildiğini görebilirsiniz.

İstemci Tarafı Hata Yakalama

İstemci tarafında bağlantı hatalarını yönetmek, kullanıcı deneyimini doğrudan etkiler. Bağlantı koptuğunda otomatik yeniden bağlanma (reconnection) mekanizmasını optimize etmeniz gerekir:

const socket = io('https://oyun-sunucum.com', {
  reconnectionAttempts: 5,
  reconnectionDelay: 1000,
  timeout: 20000
});

socket.on("connect_error", (err) => {
  console.error("Bağlantı hatası:", err.message);
  // Kullanıcıya bildirim göster
  showNotification("Sunucuya bağlanılamıyor, tekrar deneniyor...");
});

socket.on("reconnect_failed", () => {
  alert("Sunucu ile bağlantı kurulamadı. Lütfen sayfayı yenileyin.");
});

Üretim Ortamında Ölçeklendirme ve Redis Adaptörü

Tek bir Node.js sunucusu, belirli bir bağlantı sayısından sonra CPU ve RAM darboğazına girer. Uygulamanızı birden fazla sunucuya (horizontal scaling) yaydığınızda, bir sunucuya bağlı olan oyuncunun diğer sunucudaki oyuncuyla iletişim kurabilmesi için bir "mesaj aracısı"na ihtiyacınız vardır.

Redis Pub/Sub ile Çoklu Sunucu Yönetimi

Socket.io, @socket.io/redis-adapter paketi sayesinde birden fazla sunucu örneğinin birbirleriyle haberleşmesini sağlar. Bu yapı, bir sunucuya gönderilen mesajın Redis üzerinden diğer sunuculara dağıtılmasını sağlar.

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

const pubClient = createClient({ url: "redis://localhost:6379" });
const subClient = pubClient.duplicate();

Promise.all([pubClient.connect(), subClient.connect()]).then(() => {
  io.adapter(createAdapter(pubClient, subClient));
});

Bu mimari ile oyun lobiniz artık tek bir sunucuya bağımlı kalmaz. Yük dengeleyici (Load Balancer) kullanarak trafiği farklı sunuculara dağıtabilir ve oyuncu sayınız arttıkça yeni sunucular ekleyerek sisteminizi kolayca ölçeklendirebilirsiniz.

Performans İçin İpuçları

  • Binary Veri Kullanımı: Büyük veri paketleri yerine Buffer veya TypedArray kullanarak ağ trafiğini optimize edin.
  • Olay Filtreleme: Gereksiz emit işlemlerinden kaçının. Sadece durumu değişen oyunculara güncelleme gönderin.
  • Heartbeat Ayarları: Çok sık ping/pong gönderimi ağ trafiğini artırır. Gecikme sürenize göre pingInterval ve pingTimeout değerlerini optimize edin.

Sonuç

Node.js ve Socket.io kullanarak bir oyun lobisi oluşturmak, gerçek zamanlı sistemlerin temelini anlamanızı sağlar. Bu rehberde, bağlantı yönetimi, odalar (rooms) ve temel güvenlik pratiklerini ele aldık. Bir sonraki adım olarak, oyuncuların lobiye giriş-çıkışlarını bir veritabanında (örneğin MongoDB veya Redis) tutarak kalıcı hale getirebilir ve oyunun durumunu (state) yönetmek için bir oyun motoru mantığı kurgulayabilirsiniz.

Yasal Uyarı ve Güvenlik Sorumluluğu: Bu makaledeki kod örnekleri eğitim amaçlıdır. Üretim ortamına geçmeden önce tüm girdileri sanitize edin, güvenlik duvarı (firewall) yapılandırmanızı gözden geçirin ve WebSocket bağlantılarınızda mutlaka kimlik doğrulama (JWT veya Session) kullanın. Kod güvenliği tamamen geliştiricinin sorumluluğundadır.

Bu yazıya tepkinizi paylaşın:
Selin Yılmaz

Verimlilik ve zaman yönetimi odaklı pratik bilgiler konusunda içerik üretiyorum. Okuyuculara günlük rutinlerinde hız kazandıracak uygulanabilir stratejiler sunuyorum.

Yorumlar (0)

Yorum Yaz