Socket.IO, Angular ve Node.js ile Gerçek Zamanlı Uygulama Geliştirme Rehberi
Gerçek zamanlı uygulamalar, modern web deneyiminin ayrılmaz bir parçası haline gelmiştir. Anlık bildirimler, canlı sohbet odaları, çok oyunculu oyunlar, finansal grafikler ve işbirliğine dayalı araçlar gibi birçok alanda kullanıcı deneyimini zenginleştiren bu uygulamalar, sunucu ile istemci arasında sürekli ve hızlı bir iletişim kurma yeteneğine dayanır. Bu makalede, güçlü bir backend için Node.js, gerçek zamanlı iletişim için Socket.IO ve dinamik bir kullanıcı arayüzü için Angular’ı kullanarak sıfırdan bir gerçek zamanlı uygulama geliştirmeyi adım adım inceleyeceğiz.
Gerçek zamanlı uygulamalar, kullanıcıların anında geri bildirim almasını ve etkileşimde bulunmasını sağlayarak geleneksel “istek-yanıt” döngüsünün sınırlamalarını aşar. Bu tür uygulamalar, kullanıcıların bir eylemi gerçekleştirdiği anda veya sunucuda bir veri değişikliği meydana geldiğinde, ilgili tüm istemcilere anında bilgi akışını mümkün kılar. Bu yetenek, kullanıcı katılımını artırır ve daha akıcı, etkileşimli bir deneyim sunar.
Node.js, asenkron yapısı ve olay tabanlı mimarisi sayesinde gerçek zamanlı uygulamalar için ideal bir backend platformudur. Yüksek eşzamanlı bağlantıları düşük gecikmeyle yönetebilir. Socket.IO, Node.js ekosisteminin en popüler gerçek zamanlı kütüphanelerinden biridir. WebSocket protokolünü soyutlayarak, tarayıcı uyumluluğu, otomatik yeniden bağlantı, odalar ve yayınlama gibi gelişmiş özellikler sunar. Angular ise, bileşen tabanlı mimarisi ve güçlü veri bağlama yetenekleriyle karmaşık kullanıcı arayüzlerini oluşturmak için mükemmel bir seçimdir. Bu üç teknolojinin birleşimi, hem ölçeklenebilir hem de bakımı kolay gerçek zamanlı uygulamalar geliştirmek için sağlam bir temel oluşturur.
Bu rehberde, bir sohbet uygulaması örneği üzerinden, Node.js ile bir Socket.IO sunucusu kurmayı, Angular ile bir Socket.IO istemcisi geliştirmeyi ve bu iki tarafı birleştirerek tam teşekküllü bir gerçek zamanlı uygulama oluşturmayı öğreneceğiz. Ayrıca, uygulamanızı daha güçlü ve esnek hale getirecek ileri seviye konulara da değineceğiz.
Gerçek Zamanlı Uygulamaların Temelleri
Gerçek zamanlı uygulamaların arkasındaki temel prensipleri anlamak, bu tür sistemleri etkili bir şekilde geliştirmek için kritik öneme sahiptir. Geleneksel web uygulamalarının çalışma şekli ile gerçek zamanlı uygulamaların ihtiyaçları arasındaki farkı kavramak, doğru teknolojileri seçmemize yardımcı olacaktır.
Geleneksel HTTP ve Gerçek Zamanlı İhtiyaçlar
Geleneksel web uygulamaları, HTTP protokolü üzerine kuruludur ve “istek-yanıt” döngüsüyle çalışır. Bir istemci (tarayıcı), bir sunucuya bir istek gönderir ve sunucu bu isteği işleyip bir yanıt döner. Bu model, sayfa yüklemeleri veya form gönderimleri gibi tek seferlik veri alışverişleri için oldukça etkilidir. Ancak, sunucunun istemciye anında bilgi göndermesi gerektiğinde veya sürekli, çift yönlü bir iletişim hattına ihtiyaç duyulduğunda yetersiz kalır.
Örneğin, bir sohbet uygulamasında her yeni mesaj geldiğinde istemcinin sunucuya sürekli istek göndermesi (polling) veya sunucunun uzun süre bekletip bir yanıt döndürmesi (long-polling) gibi yöntemler, hem sunucu kaynaklarını verimsiz kullanır hem de gecikmeye neden olur. Bu yöntemler, gerçek zamanlı deneyim için gerekli olan anlık iletişimi sağlamakta zorlanır ve performans sorunlarına yol açabilir.
WebSocket Protokolü
WebSocket protokolü, HTTP’nin sınırlamalarını aşmak için tasarlanmıştır. Tek bir TCP bağlantısı üzerinden tam çift yönlü (full-duplex) iletişim kanalı kurar. Yani, hem istemci sunucuya hem de sunucu istemciye aynı anda ve bağımsız olarak veri gönderebilir. Bir WebSocket bağlantısı, ilk el sıkışma (handshake) tamamlandıktan sonra açık kalır ve bu sayede sürekli bir iletişim hattı sağlanır. Bu, anlık veri akışı gerektiren uygulamalar için idealdir.
WebSocket’in temel avantajları şunlardır:
* Düşük Gecikme: Bağlantı bir kez kurulduğunda, ek HTTP başlıkları olmadan doğrudan veri alışverişi yapılabilir, bu da gecikmeyi önemli ölçüde azaltır.
* Tam Çift Yönlü İletişim: İstemci ve sunucu aynı anda veri gönderip alabilir.
* Verimlilik: Bağlantı açık kaldığı için her istek için yeni bir bağlantı kurma maliyeti ortadan kalkar.
* Sunucu Tarafından İstemciye İletişim: Sunucu, istemcinin bir istek göndermesini beklemeden doğrudan veri gönderebilir.
Socket.IO Neden Kullanılmalı?
WebSocket protokolü güçlü olsa da, farklı tarayıcılar ve ağ koşulları arasında tutarlı bir deneyim sağlamak bazen zor olabilir. İşte burada Socket.IO devreye girer. Socket.IO, WebSocket’in üzerine inşa edilmiş bir kütüphanedir ve gerçek zamanlı uygulamalar geliştirmeyi çok daha kolay hale getirir.
Socket.IO’nun başlıca avantajları:
* Tarayıcı Uyumluluğu ve Fallback Mekanizmaları: Socket.IO, istemci ve sunucu arasında en iyi iletişim yöntemini otomatik olarak seçer. Eğer WebSocket bağlantısı mümkün değilse (eski tarayıcılar veya bazı ağ proxy’leri nedeniyle), otomatik olarak long-polling gibi diğer yöntemlere geri döner. Bu, uygulamanızın geniş bir kullanıcı kitlesi tarafından erişilebilir olmasını sağlar.
* Otomatik Yeniden Bağlantı: Ağ kesintileri veya sunucu yeniden başlatmaları durumunda, Socket.IO istemcisi otomatik olarak sunucuya yeniden bağlanmaya çalışır ve bu süreçte kaybolan verileri kurtarmaya yardımcı olabilir.
* Mesaj Yayınlama (Broadcasting) ve Odalar (Rooms): Belirli bir olayı tüm bağlı istemcilere, belirli bir gruba (oda) veya belirli bir istemciye yayınlamak için kolay API’ler sunar. Bu, sohbet uygulamaları, bildirim sistemleri gibi birçok senaryoda büyük kolaylık sağlar.
* Olay Tabanlı Mimari: Socket.IO, olay tabanlı bir yapıya sahiptir. İstemciler belirli olayları dinleyebilir ve sunucular belirli olayları yayabilir. Bu, kodunuzu daha modüler ve yönetilebilir hale getirir.
* Hata Yönetimi: Bağlantı hataları ve diğer sorunlar için yerleşik hata yönetimi mekanizmaları sunar.
Özetle, Socket.IO, WebSocket’in gücünü ve esnekliğini alırken, gerçek dünya dağıtım senaryolarında ortaya çıkabilecek zorlukları soyutlayarak geliştiricilere daha sorunsuz bir deneyim sunar.
Proje Ortamının Hazırlanması
Gerçek zamanlı uygulamamızı geliştirmeye başlamadan önce, gerekli araçları kurmalı ve projemizin temel klasör yapısını oluşturmalıyız. Bu adımlar, geliştirme sürecimizin sorunsuz ilerlemesini sağlayacaktır.
Ön Koşullar
Bu rehberi takip etmek için aşağıdaki yazılımların sisteminizde kurulu olması gerekmektedir:
1. Node.js ve npm (Node Package Manager): Node.js, backend’imizi çalıştırmak için gereklidir. npm ise Node.js paketlerini yönetmek için kullanılır. Node.js’in resmi web sitesinden (nodejs.org) en son LTS (Uzun Süreli Destek) sürümünü indirebilirsiniz. Kurulumdan sonra terminalinizde node -v ve npm -v komutlarını çalıştırarak doğru şekilde kurulup kurulmadığını kontrol edebilirsiniz.
2. Angular CLI (Command Line Interface): Angular projeleri oluşturmak, geliştirmek ve derlemek için kullanılan bir komut satırı aracıdır. npm install -g @angular/cli komutu ile global olarak kurabilirsiniz. Kurulumdan sonra ng v komutu ile sürümünü kontrol edebilirsiniz.
3. Bir Kod Düzenleyici/IDE: Visual Studio Code (VS Code) gibi modern bir kod düzenleyici, intellisense, hata ayıklama ve Git entegrasyonu gibi özelliklerle geliştirme sürecinizi büyük ölçüde kolaylaştıracaktır.
Klasör Yapısı ve Temel Kurulum
Projemizi düzenli tutmak için backend ve frontend kodlarını ayrı klasörlerde barındıracağız. Ana projemiz için realtime-app adında bir klasör oluşturalım.
mkdir realtime-app
cd realtime-app
Şimdi bu ana klasörün içine backend ve frontend adında iki alt klasör oluşturalım:
mkdir backend
mkdir frontend
Bu yapı, iki ayrı uygulamanın bağımsız olarak geliştirilmesini ve yönetilmesini sağlar.
Node.js Backend Geliştirme (Socket.IO Sunucusu)
Şimdi projemizin Node.js tarafını, yani Socket.IO sunucusunu geliştirmeye başlayabiliriz. Bu kısım, istemcilerden gelen bağlantıları yönetecek, olayları dinleyecek ve istemcilere veri yayınlayacaktır.
Node.js Projesinin Oluşturulması
İlk olarak, backend klasörüne geçelim ve yeni bir Node.js projesi başlatalım:
cd backend
npm init -y
Bu komut, varsayılan ayarlarla bir package.json dosyası oluşturacaktır. Şimdi gerekli bağımlılıkları kuralım:
npm install express socket.io cors
* express: HTTP sunucumuzu oluşturmak için kullanacağımız popüler bir Node.js web çerçevesidir.
* socket.io: Gerçek zamanlı iletişim için Socket.IO sunucu kütüphanesidir.
* cors: Farklı kökenlerden (domain) gelen istekleri kabul etmek için Cross-Origin Resource Sharing (CORS) middleware’idir. Angular uygulamamız farklı bir portta çalışacağı için bu gereklidir.
Temel Sunucu Kurulumu (index.js veya app.js)
backend klasöründe index.js adında yeni bir dosya oluşturalım ve temel sunucu kurulumunu yapalım:
// backend/index.js
const express = require('express');
const http = require('http');
const socketIo = require('socket.io');
const cors = require('cors');
const app = express();
const server = http.createServer(app); // Express uygulamasını bir HTTP sunucusuna bağla
// CORS ayarları: Angular frontend'imizin localhost:4200'den bağlanmasına izin ver
const io = socketIo(server, {
cors: {
origin: "http://localhost:4200", // Angular uygulamasının adresi
methods: ["GET", "POST"]
}
});
const PORT = process.env.PORT || 3000;
app.use(cors()); // Express için CORS middleware'ini etkinleştir
// Basit bir HTTP GET isteği için rota (isteğe bağlı, Socket.IO için gerekli değil)
app.get('/', (req, res) => {
res.send('Socket.IO sunucusu çalışıyor!');
});
// Socket.IO bağlantı olaylarını dinle
io.on('connection', (socket) => {
console.log('Yeni bir kullanıcı bağlandı:', socket.id);
// İstemciden 'sendMessage' olayını dinle
socket.on('sendMessage', (message) => {
console.log('Mesaj alındı:', message);
// Gelen mesajı tüm bağlı istemcilere geri gönder
io.emit('message', {
text: message.text,
user: message.user,
id: socket.id,
timestamp: new Date().toLocaleTimeString()
});
});
// Kullanıcı ayrıldığında
socket.on('disconnect', () => {
console.log('Bir kullanıcı ayrıldı:', socket.id);
});
});
server.listen(PORT, () => {
console.log(Sunucu http://localhost:${PORT} adresinde çalışıyor);
});
Bu kod parçası şunları yapar:
1. Gerekli Modülleri Yükler: express, http, socket.io ve cors.
2. Express Uygulaması Oluşturur: app = express().
3. HTTP Sunucusu Oluşturur: http.createServer(app) ile Express uygulamasını sarmalar. Socket.IO’nun bir HTTP sunucusu üzerinde çalışması gerekir.
4. Socket.IO Sunucusunu Başlatır: socketIo(server, {...}) ile HTTP sunucusunu Socket.IO’ya bağlar. CORS ayarları burada çok önemlidir. Angular uygulamamız farklı bir portta (genellikle 4200) çalışacağı için, Socket.IO sunucumuzun bu adresten gelen bağlantılara izin vermesi gerekir.
5. CORS Middleware’i: app.use(cors()) ile Express HTTP rotaları için de CORS’u etkinleştirir.
6. Socket.IO Bağlantı Olayı: io.on('connection', (socket) => { ... }) yeni bir istemci sunucuya bağlandığında tetiklenir. Her bağlı istemci için benzersiz bir socket nesnesi oluşturulur.
7. sendMessage Olayı: Bir istemci sendMessage adında bir olay gönderdiğinde, sunucu bu olayı yakalar.
8. io.emit('message', ...): Sunucu, aldığı mesajı message olayıyla tüm bağlı istemcilere geri yayınlar. Bu, bir chat uygulamasının temelini oluşturur.
9. disconnect Olayı: Bir istemci bağlantısı kesildiğinde tetiklenir.
10. Sunucuyu Dinlemeye Başlar: server.listen(PORT, ...) ile sunucuyu belirli bir portta (varsayılan olarak 3000) dinlemeye başlar.
Socket.IO Olaylarını Yönetme
Yukarıdaki örnekte temel olay yönetimini gördük (connection, disconnect, sendMessage). Socket.IO, farklı yayınlama yöntemleri sunarak iletişimi daha esnek hale getirir:
* socket.emit('event', data): Sadece olayı tetikleyen istemciye veri gönderir.
* io.emit('event', data): Tüm bağlı istemcilere veri gönderir. (Yukarıdaki chat örneğinde kullanıldı)
* socket.broadcast.emit('event', data): Olayı tetikleyen istemci hariç, tüm diğer bağlı istemcilere veri gönderir.
* io.to('roomName').emit('event', data): Belirli bir odaya katılmış tüm istemcilere veri gönderir. (İleri seviye konularda detaylandırılacak)
* socket.join('roomName') / socket.leave('roomName'): Bir istemciyi bir odaya dahil etmek veya odadan çıkarmak için kullanılır.
Şimdilik chat uygulamamız için io.emit() kullanımı yeterlidir. Backend’imiz hazır!
Angular Frontend Geliştirme (Socket.IO İstemcisi)
Şimdi sıra Angular tarafında, yani Socket.IO istemcisini geliştirmeye geldi. Bu kısım, kullanıcı arayüzünü oluşturacak, Socket.IO sunucusuna bağlanacak ve olayları dinleyip gönderecektir.
Angular Projesinin Oluşturulması
frontend klasörüne geçelim ve yeni bir Angular projesi oluşturalım:
cd ../frontend # Eğer backend klasöründeyseniz, bir üst dizine çıkıp frontend'e girin
ng new frontend-app --skip-git --style=scss --routing=false
cd frontend-app
* --skip-git: Git deposu oluşturmayı atlar (isteğe bağlı).
* --style=scss: CSS ön işlemcisi olarak SCSS kullanır (isteğe bağlı).
* --routing=false: Başlangıçta routing modülü oluşturmaz (basit bir uygulama için gerek yok).
Socket.IO İstemci Kütüphanesinin Kurulumu
Angular projemizin içine Socket.IO istemci kütüphanesini kuralım:
npm install socket.io-client
Socket.IO Servisi Oluşturma
Gerçek zamanlı iletişimi yönetmek için bir Angular servisi oluşturalım. Bu servis, Socket.IO bağlantısını kuracak, olayları dinleyecek ve gönderecek.
ng generate service services/socket
Şimdi src/app/services/socket.service.ts dosyasını açalım ve içeriğini güncelleyelim:
// frontend/frontend-app/src/app/services/socket.service.ts
import { Injectable } from '@angular/core';
import { io, Socket } from 'socket.io-client';
import { Observable } from 'rxjs';
@Injectable({
providedIn: 'root'
})
export class SocketService {
private socket: Socket;
private readonly serverUrl = 'http://localhost:3000'; // Node.js sunucumuzun adresi
constructor() {
this.socket = io(this.serverUrl); // Socket.IO sunucusuna bağlan
}
// Sunucuya mesaj gönderme
sendMessage(message: { text: string, user: string }): void {
this.socket.emit('sendMessage', message);
}
// Sunucudan gelen 'message' olayını dinleme
onMessage(): Observable {
return new Observable(observer => {
this.socket.on('message', (data: any) => {
observer.next(data);
});
});
}
// Bağlantı durumunu izleme (isteğe bağlı)
onConnect(): Observable {
return new Observable(observer => {
this.socket.on('connect', () => {
observer.next('Bağlandı');
});
});
}
onDisconnect(): Observable {
return new Observable(observer => {
this.socket.on('disconnect', () => {
observer.next('Bağlantı kesildi');
});
});
}
// Daha fazla olay dinleyici veya yayıcı eklenebilir
}
Bu servis şunları yapar:
1. socket.io-client kütüphanesinden io ve Socket nesnelerini import eder.
2. serverUrl değişkeninde Node.js sunucumuzun adresini tutar.
3. Constructor içinde io(this.serverUrl) ile Socket.IO sunucusuna bağlantı kurar.
4. sendMessage metodu ile sunucuya sendMessage olayını ve mesaj verisini gönderir.
5. onMessage metodu, sunucudan gelen message olaylarını bir Observable olarak döndürür. Bu sayede bileşenler bu Observable‘a abone olarak gerçek zamanlı mesajları alabilirler.
6. onConnect ve onDisconnect gibi metotlar, bağlantı durumunu izlemek için eklenebilir.
Bileşenlerin Geliştirilmesi (Örnek: Chat Bileşeni)
Şimdi bir chat bileşeni oluşturalım ve bu bileşende SocketService‘i kullanarak mesaj gönderip alalım.
ng generate component components/chat
Şimdi src/app/components/chat/chat.component.ts dosyasını açalım:
// frontend/frontend-app/src/app/components/chat/chat.component.ts
import { Component, OnInit, OnDestroy } from '@angular/core';
import { SocketService } from '../../services/socket.service';
import { Subscription } from 'rxjs';
interface Message {
text: string;
user: string;
id: string; // Socket ID
timestamp: string;
}
@Component({
selector: 'app-chat',
templateUrl: './chat.component.html',
styleUrls: ['./chat.component.scss']
})
export class ChatComponent implements OnInit, OnDestroy {
messageText: string = '';
userName: string = 'Kullanıcı' + Math.floor(Math.random() * 100); // Rastgele kullanıcı adı
messages: Message[] = [];
private messageSubscription: Subscription | undefined;
constructor(private socketService: SocketService) { }
ngOnInit(): void {
// Mesajları dinlemeye başla
this.messageSubscription = this.socketService.onMessage().subscribe((message: Message) => {
this.messages.push(message);
});
// Bağlantı durumunu da dinleyebiliriz (isteğe bağlı)
this.socketService.onConnect().subscribe(status => console.log('Socket durumu:', status));
this.socketService.onDisconnect().subscribe(status => console.log('Socket durumu:', status));
}
sendMessage(): void {
if (this.messageText.trim()) {
const message = {
text: this.messageText,
user: this.userName
};
this.socketService.sendMessage(message);
this.messageText = ''; // Mesaj gönderildikten sonra inputu temizle
}
}
ngOnDestroy(): void {
// Bileşen yok edildiğinde aboneliği iptal et
if (this.messageSubscription) {
this.messageSubscription.unsubscribe();
}
}
}
Ve src/app/components/chat/chat.component.html dosyasını güncelleyelim:
Socket.IO Chat Uygulaması
Son olarak, src/app/components/chat/chat.component.scss dosyasına basit stil ekleyelim:
/ frontend/frontend-app/src/app/components/chat/chat.component.scss /
.chat-container {
max-width: 600px;
margin: 50px auto;
border: 1px solid #ccc;
border-radius: 8px;
padding: 20px;
box-shadow: 0 2px 10px rgba(0,0,0,0.1);
font-family: Arial, sans-serif;
h2 {
text-align: center;
color: #333;
margin-bottom: 20px;
}
.messages {
height: 300px;
overflow-y: auto;
border: 1px solid #eee;
padding: 10px;
margin-bottom: 15px;
background-color: #f9f9f9;
border-radius: 4px;
}
.message-item {
margin-bottom: 8px;
.timestamp {
color: #888;
font-size: 0.8em;
margin-right: 5px;
}
.user {
font-weight: bold;
color: #007bff;
}
.user.me {
color: #28a745;
}
.text {
color: #333;
}
}
.input-area {
display: flex;
gap: 10px;
.username-input,
.message-input {
flex: 1;
padding: 10px;
border: 1px solid #ddd;
border-radius: 4px;
}
.username-input {
flex: 0.5;
}
button {
padding: 10px 15px;
background-color: #007bff;
color: white;
border: none;
border-radius: 4px;
cursor: pointer;
&:hover:not(:disabled) {
background-color: #0056b3;
}
&:disabled {
background-color: #cccccc;
cursor: not-allowed;
}
}
}
}
Son olarak, src/app/app.module.ts dosyasına FormsModule‘u ve ChatComponent‘i eklememiz gerekiyor:
// frontend/frontend-app/src/app/app.module.ts
import { NgModule } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
import { FormsModule } from '@angular/forms'; // FormsModule'u import et
import { AppComponent } from './app.component';
import { ChatComponent } from './components/chat/chat.component'; // ChatComponent'i import et
@NgModule({
declarations: [
AppComponent,
ChatComponent // ChatComponent'i declarations dizisine ekle
],
imports: [
BrowserModule,
FormsModule // FormsModule'u imports dizisine ekle
],
providers: [],
bootstrap: [AppComponent]
})
export class AppModule { }
Ve src/app/app.component.html dosyasını da güncelleyelim, böylece ChatComponent ana uygulamamızda görüntülenir:
CORS Politikaları ve Proxy Yapılandırması (Geliştirme Ortamı İçin)
Geliştirme ortamında Angular uygulaması genellikle localhost:4200 adresinde çalışırken, Node.js sunucusu localhost:3000 adresinde çalışır. Bu, farklı kökenler (origin) anlamına gelir ve tarayıcılar güvenlik nedeniyle bu tür çapraz köken isteklerini varsayılan olarak engeller (CORS politikası).
Backend tarafında Socket.IO sunucusunu başlatırken CORS ayarlarını yaptık: origin: "http://localhost:4200". Bu, Socket.IO bağlantıları için yeterlidir. Ancak, eğer Angular uygulamanızdan Express backend’ine doğrudan HTTP istekleri (örneğin bir REST API) göndermek isterseniz, Express tarafında da app.use(cors()) kullanmanız gerekir.
Angular geliştirme sunucusunun (ng serve) proxy özelliğini kullanarak da bu sorunu aşabilirsiniz. Bu, özellikle REST API’leri için kullanışlıdır:
1. frontend-app klasöründe proxy.conf.json adında bir dosya oluşturun:
// frontend/frontend-app/proxy.conf.json
{
"/api": {
"target": "http://localhost:3000",
"secure": false,
"changeOrigin": true
}
}
2. angular.json dosyanızda serve konfigürasyonunu güncelleyin:
// angular.json (ilgili kısım)
"architect": {
"serve": {
"builder": "@angular-devkit/build-angular:dev-server",
"options": {
"browserTarget": "frontend-app:build",
"proxyConfig": "proxy.conf.json" // Bu satırı ekle
},
// ...
}
}
3. Angular uygulamasını başlatırken ng serve yerine ng serve --proxy-config proxy.conf.json veya angular.json‘a eklediyseniz sadece ng serve kullanabilirsiniz.
Bu yapılandırma ile Angular uygulamanız /api ile başlayan tüm istekleri http://localhost:3000 adresine yönlendirecektir. Socket.IO için bu doğrudan gerekli olmasa da, karmaşık uygulamalarda sıkça kullanılır.
Uygulamayı Çalıştırma ve Test Etme
Şimdi hem backend hem de frontend uygulamamızı çalıştırarak gerçek zamanlı sohbet uygulamasını test edebiliriz.
Backend’i Başlatma
Terminalinizde backend klasörüne gidin ve Node.js sunucusunu başlatın:
cd realtime-app/backend
node index.js
Başarılı olursa, “Sunucu http://localhost:3000 adresinde çalışıyor” mesajını görmelisiniz. Bu terminal penceresini açık bırakın.
Frontend’i Başlatma
Yeni bir terminal penceresi açın, frontend/frontend-app klasörüne gidin ve Angular uygulamasını başlatın:
cd realtime-app/frontend/frontend-app
ng serve --open
--open bayrağı, uygulamanızın tarayıcıda otomatik olarak http://localhost:4200 adresinde açılmasını sağlar.
Tarayıcıda Test Etme
1. Tarayıcınızda http://localhost:4200 adresine gidin. Sohbet uygulamasının arayüzünü görmelisiniz.
2. Yeni bir tarayıcı sekmesi veya penceresi açın ve yine http://localhost:4200 adresine gidin.
3. Her iki sekmede de farklı kullanıcı adları girin (veya varsayılan rastgele adları kullanın).
4. Bir sekmede bir mesaj yazın ve gönderin. Mesajın anında diğer sekmede de belirdiğini görmelisiniz.
Bu, Socket.IO’nun gerçek zamanlı iletişim yeteneğini ve Node.js backend ile Angular frontend arasındaki sorunsuz entegrasyonu başarıyla gösterir. Backend terminalinizde yeni bağlantıları ve alınan mesajları da görebilirsiniz.
İleri Seviye Konular ve Optimizasyonlar
Basit bir sohbet uygulaması oluşturmuş olsak da, gerçek dünya senaryolarında daha karmaşık ihtiyaçlar ve performans optimizasyonları gerekebilir. İşte bazı ileri seviye konular:
Odalar (Rooms) Kullanımı
Socket.IO’nun en güçlü özelliklerinden biri “odalar”dır. Odalar, belirli bir grup istemciye mesaj göndermek için kullanılır. Örneğin, farklı sohbet kanalları, oyun lobileri veya belirli bir belge üzerinde çalışan kullanıcı grupları oluşturabilirsiniz.
Backend’de:
Bir istemciyi bir odaya dahil etmek için:
socket.on('joinRoom', (roomName) => {
socket.join(roomName);
console.log(${socket.id} kullanıcısı ${roomName} odasına katıldı.);
// Odaya katılan kullanıcıya veya odaya özel mesaj gönderebilirsiniz
socket.emit('roomJoined', Odaya başarıyla katıldınız: ${roomName});
io.to(roomName).emit('notification', ${socket.id} odaya katıldı.);
});
socket.on('sendRoomMessage', (data) => {
io.to(data.roomName).emit('roomMessage', {
text: data.message,
user: data.user,
room: data.roomName
});
});
Frontend’de:
// SocketService içinde
joinRoom(roomName: string): void {
this.socket.emit('joinRoom', roomName);
}
onRoomMessage(): Observable {
return new Observable(observer => {
this.socket.on('roomMessage', (data: any) => {
observer.next(data);
});
});
}
// Chat bileşeninde
joinSpecificRoom(room: string) {
this.socketService.joinRoom(room);
// Daha sonra bu odanın mesajlarını dinlemeye başlayın
}
Kimlik Doğrulama ve Yetkilendirme
Gerçek zamanlı uygulamalarda genellikle kullanıcıların kimliğini doğrulamak ve belirli eylemleri gerçekleştirmek için yetkilerini kontrol etmek istersiniz. JWT (JSON Web Token) en yaygın yöntemlerden biridir.
Süreç:
1. Kullanıcı normal bir HTTP isteği ile (örneğin /login rotasına) kimlik doğrulaması yapar.
2. Sunucu, başarılı kimlik doğrulamasından sonra bir JWT oluşturur ve istemciye gönderir.
3. İstemci, Socket.IO bağlantısını kurarken bu JWT’yi Socket.IO bağlantısının extraHeaders veya auth seçenekleri aracılığıyla gönderir.
// frontend/socket.service.ts
const token = localStorage.getItem('jwt_token'); // Veya başka bir yerden alın
this.socket = io(this.serverUrl, {
auth: {
token: token
}
});
4. Sunucu tarafında, Socket.IO bağlantısı kurulmadan önce bir middleware kullanarak JWT’yi doğrulayabilirsiniz:
// backend/index.js
io.use((socket, next) => {
const token = socket.handshake.auth.token;
if (token) {
// JWT'yi doğrula
jwt.verify(token, 'YOUR_SECRET_KEY', (err, decoded) => {
if (err) return next(new Error('Authentication error'));
socket.user = decoded; // Kullanıcı bilgilerini socket nesnesine ata
next();
});
} else {
next(new Error('Authentication error: Token not provided'));
}
});
Bu sayede, sadece geçerli bir JWT’ye sahip kullanıcıların Socket.IO bağlantısı kurmasına izin verilir.
Hata Yönetimi ve Yeniden Bağlantı
Socket.IO istemcisi, bağlantı kesildiğinde otomatik olarak yeniden bağlanmaya çalışır. Ancak, bu davranışın nasıl yönetileceğini özelleştirebilirsiniz:
// frontend/socket.service.ts
this.socket = io(this.serverUrl, {
reconnection: true, // Varsayılan olarak true
reconnectionAttempts: 5, // Yeniden bağlantı deneme sayısı
reconnectionDelay: 1000, // İlk yeniden bağlantı denemesi gecikmesi (ms)
reconnectionDelayMax: 5000 // Maksimum yeniden bağlantı gecikmesi (ms)
});
this.socket.on('reconnect', (attemptNumber) => {
console.log('Yeniden bağlandı:', attemptNumber);
});
this.socket.on('reconnect_error', (error) => {
console.error('Yeniden bağlantı hatası:', error);
});
Sunucu tarafında da error olaylarını dinlemek önemlidir:
// backend/index.js
socket.on('error', (err) => {
console.error('Socket hatası:', err);
});
Ölçeklenebilirlik
Tek bir Node.js sunucusu binlerce eşzamanlı bağlantıyı yönetebilirken, daha büyük uygulamalar için birden fazla sunucuya ihtiyacınız olabilir. Bu durumda, Socket.IO’nun istemcileri farklı sunuculara dağıtması ve tüm sunucuların aynı mesajları yayınlayabilmesi için bir adaptöre ihtiyacınız vardır.
* Redis Adaptörü: Socket.IO, birden fazla sunucu arasında olayları yayınlamak için Redis adaptörü sağlar. Bu, farklı sunuculara bağlı istemcilerin bile aynı mesajları almasını sağlar.
npm install @socket.io/redis-adapter redis
// backend/index.js (Redis adaptörünü kullanmak için)
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));
// Sunucu dinlemeye başla
server.listen(PORT, () => {
console.log(Sunucu http://localhost:${PORT} adresinde çalışıyor);
});
}).catch(err => console.error("Redis bağlantı hatası:", err));
* Sticky Sessions: Birden fazla Node.js sunucusu kullanırken, bir istemcinin her zaman aynı sunucuya bağlanmasını sağlamak için bir load balancer üzerinde “sticky sessions” (yapışkan oturumlar) yapılandırmanız gerekebilir. Bu, her WebSocket bağlantısının belirli bir sunucuyla ilişkilendirilmesini sağlar.
Performans İpuçları
* Veri Boyutunu Azaltın: Yalnızca gerekli verileri gönderin. Gereksiz veya büyük nesneleri göndermek ağ trafiğini artırır.
* Olay Sıklığını Yönetin: Fare hareketleri veya tuş vuruşları gibi yüksek frekanslı olayları gönderirken debounce veya throttle tekniklerini kullanın. Bu, sunucuya gönderilen olay sayısını azaltır.
* Binary Veri Kullanımı: Görüntüler veya ses gibi büyük ikili veriler için Socket.IO’nun ikili veri desteğini kullanın.
* Güvenlik: DDoS saldırılarına karşı koruma, rate limiting ve veri şifrelemesi gibi güvenlik önlemlerini uygulayın.
Sonuç
Bu kapsamlı rehberde, Node.js, Socket.IO ve Angular kullanarak sıfırdan bir gerçek zamanlı sohbet uygulaması geliştirmeyi öğrendik. Gerçek zamanlı uygulamaların temel prensiplerini, WebSocket protokolünün önemini ve Socket.IO’nun bu süreci nasıl kolaylaştırdığını anladık. Node.js ile güçlü bir Socket.IO sunucusu kurarken, Angular ile dinamik ve etkileşimli bir kullanıcı arayüzü geliştirdik.
Geliştirdiğimiz basit sohbet uygulaması, gerçek zamanlı iletişimin gücünü ve bu teknolojilerin birlikte nasıl çalıştığını net bir şekilde gösterdi. Ayrıca, odalar, kimlik doğrulama, hata yönetimi ve ölçeklenebilirlik gibi ileri seviye konulara değinerek, uygulamanızı daha sağlam, güvenli ve performanslı hale getirmek için atabileceğiniz adımları inceledik.
Gerçek zamanlı uygulamalar, modern web’in vazgeçilmez bir parçasıdır ve bu rehberde edindiğiniz bilgilerle, kendi yenilikçi ve etkileşimli uygulamalarınızı geliştirmek için sağlam bir temele sahipsiniz. Bu teknolojileri kullanarak canlı panolar, işbirliğine dayalı düzenleyiciler, oyunlar ve çok daha fazlasını hayata geçirebilirsiniz. Unutmayın, pratik yapmak ve farklı senaryoları denemek, bu alandaki yetkinliğinizi artırmanın en iyi yoludur. İyi geliştirmeler!