Node.js İle Socket.io Kullanarak Paylaşımlı Canlı Dosya Düzenleyici Nasıl Yapılır?

Node.js İle Socket.io Kullanarak Paylaşımlı Canlı Dosya Düzenleyici Nasıl Yapılır?
Node.js İle Socket.io Kullanarak Paylaşımlı Canlı Dosya Düzenleyici Nasıl Yapılır?

Gereksinimler ve Ön Hazırlık

Projeye başlamadan önce bilgisayarınızda Node.js'in 20.x veya daha güncel bir sürümünün kurulu olduğundan emin olun. Geliştirme ortamınızda paket yönetimi için npm veya pnpm kullanabilirsiniz. Projemiz için gerekli kütüphaneler şunlardır:

  • Express: Sunucu tarafında HTTP isteklerini yönetmek için.
  • Socket.io: İstemci ve sunucu arasında çift yönlü, gerçek zamanlı iletişim kurmak için.
  • Cors: Farklı portlar üzerinden gelen istekleri güvenli bir şekilde yönetmek için.

Terminalinizde yeni bir klasör oluşturun ve şu komutlarla gerekli bağımlılıkları yükleyin:

mkdir canli-duzenleyici
cd canli-duzenleyici
npm init -y
npm install express socket.io cors

Sunucu Tarafı Kurulumu ve Socket.io Yapılandırması

Sunucu tarafında, Socket.io'yu Express ile entegre ederek WebSocket bağlantılarını dinlemeye başlayacağız. Bu adımda, her gelen bağlantıyı takip edecek ve metin içeriğindeki değişimleri diğer istemcilere yayınlayacak (broadcast) ana yapıyı kuruyoruz.

const express = require('express');
const http = require('http');
const { Server } = require('socket.io');
const cors = require('cors');

const app = express();
app.use(cors());

const server = http.createServer(app);
const io = new Server(server, {
    cors: { origin: "*" }
});

let dosyaIcerigi = ""; // Bellekte tutulan paylaşımlı metin

io.on('connection', (socket) => {
    console.log('Yeni bir kullanıcı bağlandı:', socket.id);
    
    // Bağlanan kullanıcıya mevcut içeriği gönder
    socket.emit('icerik-guncelle', dosyaIcerigi);

    // Değişiklikleri dinle ve diğerlerine yay
    socket.on('metin-degisti', (yeniMetin) => {
        dosyaIcerigi = yeniMetin;
        socket.broadcast.emit('icerik-guncelle', dosyaIcerigi);
    });
});

server.listen(3000, () => console.log('Sunucu 3000 portunda çalışıyor.'));

Burada socket.broadcast.emit metodu, mesajı gönderen kişi hariç tüm bağlı istemcilere veriyi iletmek için kullanılır. Bu, sonsuz döngüleri engellemek adına kritik bir yaklaşımdır.

İstemci Tarafı Arayüzü ve Socket Bağlantısı

İstemci tarafında, kullanıcıların metin girebileceği basit bir textarea alanı oluşturacağız. Socket.io'nun istemci kütüphanesini kullanarak sunucu ile iletişim kuracağız. İstemci tarafında socket.io-client paketine ihtiyaç duyulur.




    
    
    
        const socket = io('http://localhost:3000');
        const editor = document.getElementById('editor');

        editor.addEventListener('input', (e) => {
            socket.emit('metin-degisti', e.target.value);
        });

        socket.on('icerik-guncelle', (metin) => {
            editor.value = metin;
        });
    

Bu kod bloğu, kullanıcı her tuşa bastığında (input olayı) sunucuya yeni metni gönderir. Aynı zamanda sunucudan gelen güncellemeleri dinleyerek metin kutusunu senkronize tutar.

Veri Senkronizasyonu ve Çakışma Yönetimi

Gerçek zamanlı düzenleyicilerde en büyük sorun, iki kullanıcının aynı anda metni değiştirmesidir. Basit uygulamalarda metnin tamamını göndermek yeterli olsa da, büyük dosyalarda bu yöntem performans kaybına yol açar. Profesyonel uygulamalarda "Operational Transformation" (OT) veya "CRDT" (Conflict-free Replicated Data Types) algoritmaları kullanılır.

Kritik Uyarı: Basit uygulamalarda metnin tamamını göndermek kolaydır ancak yüksek trafikli uygulamalarda "Diffing" (fark bulma) algoritmaları kullanarak sadece değişen karakterleri göndermek sunucu yükünü ciddi oranda azaltır.

Performans ve Ölçeklenebilirlik Karşılaştırması

Yöntem Avantajı Dezavantajı
Tüm Metni Gönderme Basit uygulama, kolay hata ayıklama Düşük performans, veri çakışması riski
Operational Transformation Yüksek doğruluk, çakışma yönetimi Karmaşık mimari, zor geliştirme
CRDT (Yjs/Automerge) Dağıtık sistemler için mükemmel Öğrenme eğrisi yüksek

Güvenlik Önlemleri ve Hata Yönetimi

Canlı bir uygulamada güvenlik en öncelikli konudur. Özellikle kullanıcıdan gelen verilerin doğrudan işlenmesi XSS (Cross-Site Scripting) saldırılarına kapı aralayabilir. Kullanıcı girdilerini mutlaka temizleyin (sanitize) ve Socket.io üzerinde kimlik doğrulama katmanı kullanın.

// Sunucu tarafında basit bir doğrulama örneği
io.use((socket, next) => {
    const token = socket.handshake.auth.token;
    if (token === "gizli-anahtar") {
        next();
    } else {
        next(new Error("Yetkisiz erişim"));
    }
});

Bu yapı, sadece geçerli bir token'a sahip olan kullanıcıların düzenleme yapmasına izin verir. Güvenlik için asla istemciden gelen veriye güvenmeyin.

Güvenlik Sorumluluk Reddi: Bu makaledeki örnekler eğitim amaçlıdır. Üretim ortamında (production) kullanmadan önce mutlaka veri doğrulama, XSS engelleme (DOMPurify gibi) ve SSL/TLS şifrelemesi gibi güvenlik katmanlarını uygulayın.

Sıkça Sorulan Sorular

Socket.io yerine neden WebSocket kullanmıyoruz?

Socket.io, otomatik yeniden bağlanma, oda (room) desteği ve geri dönüş (fallback) mekanizmaları ile WebSocket'in üzerine eklenmiş güçlü bir soyutlama katmanıdır. Geliştirme sürecini ciddi oranda hızlandırır.

Aynı anda 100 kişi düzenleme yaparsa ne olur?

Sunucunun RAM kullanımı artar. Bu durumda Redis kullanarak Socket.io adaptörü kurmanız ve yükü dağıtmanız gerekir.

Veriler neden sayfayı yenileyince siliniyor?

Çünkü veriler RAM'de tutuluyor. Kalıcı olması için MongoDB veya PostgreSQL gibi bir veritabanına periyodik olarak kaydetmelisiniz.

Mobil cihazlarda gecikme yaşanır mı?

Socket.io, düşük gecikmeli bağlantı sağlar. Ancak 4G/5G bağlantı kalitesine bağlı olarak milisaniyelik gecikmeler kaçınılmazdır.

Bu projeyi nasıl canlıya alabilirim?

PM2 gibi bir süreç yöneticisi kullanarak Node.js uygulamasını arka planda çalıştırabilir ve Nginx ile ters vekil sunucu (reverse proxy) yapılandırması yapabilirsiniz.

İleri Seviye Optimizasyon: Operasyonel Dönüşüm (Operational Transformation) Mantığı

Canlı düzenleme projelerinde en büyük zorluk, iki kullanıcının aynı anda aynı karakteri değiştirmesi durumunda oluşan "çakışma" (conflict) sorunudur. Basit bir socket.emit yapısı, yüksek trafikli anlarda verinin tutarsızlaşmasına neden olabilir. Bunun önüne geçmek için "Operational Transformation" (OT) veya daha modern bir yaklaşım olan "Conflict-free Replicated Data Types" (CRDT) mantığını projenize dahil etmelisiniz.

Aşağıdaki örnek, istemci tarafında basit bir versiyon kontrolü mantığı ile verinin üzerine yazılmasını engelleyen bir yapı sunar:

// İstemci tarafı: Versiyon takibi ile güncelleme
let localVersion = 0;

socket.on('update-content', (data) => {
    if (data.version > localVersion) {
        editor.value = data.content;
        localVersion = data.version;
    }
});

function sendUpdate(newContent) {
    localVersion++;
    socket.emit('edit', {
        content: newContent,
        version: localVersion
    });
}

Proje İçin Otomatik Test Stratejileri

Gerçek zamanlı uygulamalarda "race condition" (yarış durumu) hatalarını manuel test etmek imkansızdır. Socket.io bağlantılarını simüle etmek için socket.io-client kütüphanesini kullanarak bir test senaryosu oluşturmalısınız. Bu testler, birden fazla istemcinin aynı anda veri gönderdiğinde sunucunun nasıl tepki verdiğini doğrular.

Aşağıdaki test betiği, iki farklı istemcinin eşzamanlı veri gönderimini simüle eder:

const io = require('socket.io-client');
const assert = require('assert');

const client1 = io('http://localhost:3000');
const client2 = io('http://localhost:3000');

client1.emit('join-room', 'test-doc');
client2.emit('join-room', 'test-doc');

client1.on('connect', () => {
    client1.emit('edit', { text: 'Merhaba Dünya' });
});

client2.on('update-content', (data) => {
    assert.strictEqual(data.text, 'Merhaba Dünya');
    console.log('Senkronizasyon başarılı!');
    client1.disconnect();
    client2.disconnect();
});

Performans İzleme ve Metrikler

Uygulamanızın canlı ortamda ne kadar sağlıklı çalıştığını anlamak için şu metrikleri takip etmeniz kritik öneme sahiptir:

  • Socket Bağlantı Süresi: Kullanıcının sunucuya bağlanması ne kadar sürüyor?
  • Mesaj Gecikmesi (Latency): Bir karakterin yazılıp diğer kullanıcıya ulaşması kaç milisaniye alıyor?
  • Bellek Kullanımı: Node.js süreci, oda sayısı arttıkça ne kadar RAM tüketiyor?
  • Hata Oranı: Bağlantı kopmaları (disconnect) ve yeniden bağlanma (reconnect) sıklığı nedir?

Bu verileri izlemek için pm2-prometheus-exporter veya socket.io-admin-ui gibi araçları kullanarak sistemin genel sağlığını görselleştirebilirsiniz. Özellikle socket.io-admin-ui, canlı odalardaki kullanıcı sayısını ve anlık mesaj trafiğini izlemek için oldukça pratiktir.

Sonuç

Node.js ve Socket.io kullanarak paylaşımlı canlı dosya düzenleyici yapmak, gerçek zamanlı sistemlerin temellerini anlamak için harika bir projedir. Bu rehberde, temel bir iletişim kanalı kurmayı, istemci-sunucu senkronizasyonunu ve temel güvenlik önlemlerini inceledik. Bir sonraki adım olarak, projenize veritabanı entegrasyonu ekleyerek metinlerin kalıcı hale getirilmesini sağlayabilir ve "Oda" (Room) yapısını kullanarak farklı kullanıcıların farklı dosyalar üzerinde çalışmasını sağlayacak bir çoklu dosya sistemi geliştirebilirsiniz.

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

Kullanıcı odaklı rehberler hazırlama konusunda uzmanım. Adım adım anlatımlarla karmaşık süreçleri herkes için anlaşılır kılıyorum.

Yorumlar (0)

Yorum Yaz