Bu makalemizde, Node.JS'in en popüler modüllerinden biri olan Socket.IO ile WebSocket programlamayı inceleyeceğiz. Bir süredir Design Patterns kategorisindeki makaleler ile ilgilendiğimden, Node.JS kategorisine biraz üvey evlat muamelesi yapmış oldum. İnşallah doyurucu bir makale ile kendimi affettirmeyi planlıyorum.

Her ne kadar, "Her şey bir gaz bulutuydu" şeklinde başlayan cümlelerden hoşlansam da, zaten yeterince uzun bir makale olacağı için şimdilik sadece 1990'ların sonlarına dönmek ve "devrimin sessiz ayak sesleri" konusundan başlamak istiyorum. DHTML'den bahsediyorum.

1990'ların sonlarında (98-99), JavaScript ve CSS ile tarayıcıda o an açık olan HTML sayfa üzerinde değişiklikler yapabilmeye başladık. Bu fikir, her ne kadar çoğumuzu heyecanlandırsa da, çok küçük bir azınlık haricinde, bunu sadece animasyon ve görsel efektler için kullanmayı düşündük ve yaklaşan büyük devrimin ayak sesleri olduğunu anlayamadık.


DHTML o zamanki imkanlara göre muhteşemdi ancak iki büyük sıkıntısı vardı.
  • Yazılan kodlar, her tarayıcıda farklı davranıyordu. Bu sebeple, neredeyse tarayıcı sayısı kadar ayrı kod yazmamız gerekiyordu.
  • İşlenmesi ve gösterilmesi gereken asıl veriler ve işletilmesi gereken algoritmalar sunucu tarafında yer alıyordu ve zamanında ASP/PHP gibi dillerle yazılan bu uygulamalara ulaşmak için sayfanın Refresh olması gerekiyordu.

Yine de türlü taklalar ile bu verilere ulaşmak mümkündü. Örneğin ASP'de çalışan bir Chat uygulaması yazmak için, sayfanın bir köşesinde gözükmeyen bir Frame'de sayfayı 5 saniyede bir refresh ettiriyor, sunucu tarafından datayı alıyor ve DHTML ile sayfanın gözüken tarafına yazdırıyordum. Her programcı gibi, o zamanlar bunu dünyada yapan tek kişinin kendim olduğumu düşündüğümü hatırlıyorum :)

İşin gerçek yüzü, AJAX denilen ve internet uygulamalarında devrim niteliği taşıyan teknoloji ile anlaşıldı. Bu teknoloji sayesinde, sayfaları refresh etmeye gerek kalmadan sunucu tarafına çağrıda bulunabiliyor ve sunucudan aldığımız verileri DHTML ile o anda açık olan sayfamızda görüntüleyebiliyorduk.

Not  : AJAX'ın ilk çıkış tarihi ile ilgili çeşitli söylentiler var. 2000'in başlarında pek çok firma tarafından değişik şekillerde uyarlanmaya çalışılmış ancak W3C tarafından XMLHttpRequest'in resmi standartlara eklenişi 5 Nisan 2006 tarihinde kayıtlara geçti.

Bu yöntem, hem tüm sayfayı komple yeniden yükleme gereksinimini ortadan kaldırdı, hem de bugün SPA (Single Page Application) olarak geçen ve masaüstü uygulamalarının kullanıcı deneyimini aratmayan web uygulamalarının da önünü açtı. Bugün halen çok yaygın olarak kullanılmasa da, özellikle uygulamaların Cloud'a taşınmasına imkan sağlayan bu teknolojinin yakın gelecekte çok daha yaygınlaşacağına inanıyorum (Aynı WebGL sayesinde, yakın gelecekte oyunların da internet üzerinden yükleme yapmadan oynanacak hale geleceğine inandığım gibi).

Not  : SPA (Single Page Application) ile ilgilenenler, blogdaki Angular.JS veya Can.JS kategorisine, WebGL ile oyun yazmak ile ilgilenenler, Three.JS kategorisine göz atabilirler.

AJAX'ın bize sunduğu imkanlar gerçekten de çok heyecan verici. Ancak bu teknolojide halen bir sıkıntımız var. Aslında bu sıkıntı, aynı zamanda Web'in şimdiye kadar tasarlanış şekliyle de alakalı. Her ne kadar SPA veya WebGL'den de bahsetsek, sonunda tarayıcı içerisinde açılan bir HTML sayfasından bahsediyoruz. Bu sayfada tüm istekler, istemciden sunucuya doğru gidiyor ve sunucu bu isteğe cevap dönüyor.

Böyle bir teknolojide Chat uygulaması yazdığımızı düşünelim. Yazdığımız metni, butona basıldığında AJAX ile sunucuya göndermek kolay. Peki ya karşı taraftaki kullanıcı, bizim gönderdiğimiz mesajdan nasıl haberdar olacak? Çünkü sunucu tarafından istemci tarafına bir istek gönderilemiyor. İstemci tarafı mecburen, belirli periyotlarda (Örneğin 1 saniye) "Yeni mesaj var mı?, Yeni mesaj var mı?" şeklinde sunucuya sormak zorunda. Sunucu ancak bu durumda, bizim gönderdiğimiz mesajı karşı tarafa bildirebilir.

Benzer şekilde WebGL ile FPS (First Person Shooter) tarzında bir oyun yazdığımızı düşünelim. Gittik, karşıdaki adamı tam da kafasından vurduk ve öldürdük. Bunu sunucuya söylemek kolay. Peki ya karşı taraf öldüğünü nereden bilecek. Yine aynı mantıkta, mecburen sunucuya sık sık, "Öldüm mü? Öldüm mü?" diye sormak zorunda ve sunucudan "Öldün!" cevabını aldığında gerekli animasyon ile oyunu sona ermeli.

Yukarıdaki iki paragrafta anlattıklarıma PULL yöntemi deniyor ve istemcinin sunucudan kendine ait mesajları çekmesi prensibine dayanıyor. Kullanıcıların hiçbiri farkında değil ama PULL yöntemi, şimdiye kadar pek çok uygulamada kullanılmış ve kullanılmaya da devam ediyor. Peki bu yöntemin ne gibi sakıncaları var?
  • Olayın gerçekleşmesi ile bildirimin alınması arasında bir miktar gecikme (Latency) yaşanıyor.
  • Tek bir işlem için, belki de bin kez boşu boşuna sunucuya gidiliyor.
  • Kullanıcı sayısının arttığı durumlarda, sunucuyu stres teste sokmuş muamelesi yapıyoruz.
  • Tek bir sunucu ile çözebileceğimiz bir iş için load balanced çalışan on sunucu gücüne ihtiyacımız oluyor.

İşin doğrusu, SPA uygulamalarda PULL gereksinimi çok fazla olmadığı gibi, eş zamanlı kullanıcı sayısı da kısıtlıydı. Chat gibi uygulamalar için de geçmişte Java Applet tarzında uygulamalar tercih ediliyordu. Ancak WebGL ile oyunların internet üzerine çıkması ile birlikte bu konunun artık kaçarı kalmadı ve kesinlikle çözülmesi gereken bir sorun haline geldi.

Not  : Belki burada Comet'ten de bahsetmem gerekiyor ancak uygulama sırasındaki komplekslik ve sıkıntıları sebebiyle, özellikle de WebSocket çıktıktan sonra kimsenin Comet'e ilgi duyacağını sanmıyorum. Bu yüzden makale kapsamı dışında bırakıyorum. Kaldı ki, makalenin sonunda da göreceğimiz gibi, Socket.IO bu konuyla bizim için ilgileniyor.

İşte bu noktada, internet uygulamalarındaki yeni bir devrim ile karşı karşıyayız. WebSocket!

WebSocket, aktif bir TCP bağlantı üzerinden çift yönlü mesaj alış verişine izin veren yeni bir protokol. Bu protokol sayesinde, tarayıcı ile sunucu arasında, sürekli açık kalan bir TCP Connection oluşuyor ve bu aktif Connection üzerinden, isteyen taraf diğer tarafa, istediği mesajı gönderebiliyor.

Not : Birazdan adım adım uygulamalı olarak göreceğimiz bu yeni teknolojiyi HTTP ile karşılaştırmamak lazım. HTTP'de istemci tarafından gönderilen her mesaja, sunucu tarafından dönülen bir cevap mevcuttur. WebSocket'te ise, gönderim tek taraflı oluyor. İsteyen, istediği tarafa mesaj gönderiyor ancak bir cevap beklemiyor. Eğer bir cevap beklentimiz varsa, sistemi buna göre tasarlamalı ve mesajı alan tarafın, göndericiye geri mesaj yollamasını sağlamamız gerekir.

Bunlar şimdilik sadece teferruat, birazdan uygulamalı olarak zaten göreceğiz. Ama WebSocket'i, HTTP'nin gelişmiş bir versiyonu olarak görmemek gerektiğini ifade etmek için bunları söylüyorum.

Eleştri  : Eğer ki WebSocket uygulamamızı .Net ile yazmak isteseydik, en azından Windows 8 veya Windows Server 2012 işletim sistemli bir sunucuya ihtiyacımız olacaktı (Ya da internetteki Open Source projelerden birini kullanmamız gerekecekti). Bu aynı, .Net Framework 4.5'in Windows 2003 sunuculara kurulamaması gibi, Microsoft'un yeni işletim sistemlerini satma çabalarından kaynaklanıyor. Neyse ki Node üzerinde uygulama geliştirirken böyle bir kısıtımız yok.

Epeyi sohbet ettik. Artık eğlenmenin vaktidir. Node.JS üzerinde WebSocket destekli uygulama geliştirmek için kullanacağımız modülün adı Socket.IO. Bu, aynı zamanda Node'un en çok rağbet gören modüllerinden biri olma özelliğine sahip. Örneğin ben bu makaleyi yazarken, sadece geçen ayki indirilme sayısı 271.103'tü.



Bu modülü kullanmak için, ilk olarak masaüstüne "SocketIOTest" isimli bir klasör açıp içerisine "package.json" dosyasını aşağıdaki gibi hazırlayalım.
{
    "name": "SocketIOTest",
    "version": "1.0.0",
    "dependencies": {
        "socket.io": "0.9.16"
    }
}

Ardından, asıl kodumuzu içeren "program.js" dosyamızı aşağıdaki gibi hazırlayalım.
"use strict";

var http = require("http");

var server = http.createServer(processRequest).listen(1234, "127.0.0.1");

function processRequest(request, response) {

    response.writeHead(200, {
        "Content-Type": "text/html"
    });

    response.write("Simple HTML Page");

    response.end();

}


// Buradan sonrası yeni
var io = require("socket.io").listen(server);

io.sockets.on('connection', function (socket) {

    console.log("User Connected");

    socket.on("disconnect", function () {

        console.log("User Disconnected");
    });
});

Programı çalıştırmadan önce biraz durup kodu inceleyelim. Aslında kodun büyük kısmı, ilk makalelerimizde gördüğümüz HTTP Server örneğine benziyor. Uygulama başladığı zaman 1234 portundan dinleme yapıyor ve bu porta gelen standart HTTP taleplerine "Simple HTML" şeklinde dönüş yapıyor. Buraya kadar bilmediğimiz bir şey yok ve birazdan tarayıcı ile bu adresi direk ziyaret ettiğimizde, bu mesaj ile karşılaşacağız.


var io = require("socket.io").listen(server);
io.sockets.on('connection', function (socket) {
    console.log("User Connected");
    socket.on("disconnect", function () {
        console.log("User Disconnected");
    });
});


Amma velakin, bir de yukarıdaki kod parçası var. İşte bu yeni. Buradaki kod ile, belirtilen porttan WebSocket dinlemesi yapmak istediğimizi söylüyoruz. Eğer bu protokol ile herhangi bir bağlantı olursa da (connection olayı), sunucu tarafındaki konsola "User connected" yazdırıyoruz. Eğer ki istemci ile bağlantı kesilirse de (disconnect olayı), "User Disconnected" yazdırıyoruz.

Pekala, haydi biraz aksiyon görelim. İlk olarak gerekli modülü indirmek için terminal (Linux veya Mac OS) veya command prompt (Windows) penceresinden "SocketIOTest" isimli klasöre girip aşağıdaki komutu çalıştıralım.

npm install

Böylece Socket.IO için gerekli olan dosyalar inmiş olacak. Ardından aşağıdaki komutla uygulamamızı ayağa kaldıralım.

node program.js

Her şey hazır. İlk olarak bir tarayıcı açarak http://localhost:1234 adresini ziyaret edelim. Karşına aşağıdaki gibi bir sayfanın gelmesi gerekiyor.



Sunucu tarafında da aşağıdaki gibi bir görüntü oluşmalı.



Gayet normal, çünkü standart bir HTTP Request'te bulunduk ve bir şey olmadı. WebSocket Request'te bulunmak için istemci tarafında da bir şeyler yapmak gerekiyor.

Öncelikle HTML sayfamıza Socket.IO kütüphanesini eklemeliyiz. Bu dosyayı, sunucu tarafında modül için inen dosyaların içinden "node_modules\socket.io\node_modules\socket.io-client\dist" klasöründe "socket.io.min.js" adıyla bulabiliriz. Bu dosyayı, masaüstüne kopyalayalım. Ardından yine masaüstünde, aşağıdaki kodlarla "sayfa.html" adında bir dosya oluşturalım.
<!DOCTYPE html>
<html>
<head>
<title></title>
<meta http-equiv="Content-Type" content="text/html; charset=windows-1254" />
<script src="http://ajax.googleapis.com/ajax/libs/jquery/2.0.3/jquery.min.js"></script>
<script src="socket.io.min.js"></script>
<script>
$(document).ready(function(){

    var socket = io.connect("http://localhost:1234");

});
</script>
</head>
<body>
</body>
</html>


Bu sayfa görüntüde bir şey yapmayacak ancak localhost:1234 adresine WebSocket için TCP Connection açacak. Böylece sunucu tarafında yazdırdığımız "User Connected" yazısını, tarayıcıyı kapattığımızda da "User Disconnected" yazısını görebileceğiz.

Sayfayı açtığında, aşağıdaki gibi bir görüntü ile karşılaşmış olman lazım.


Gayet temiz ve boş bir sayfa. Gel gelelim, sunucu tarafına baktığında, aşağıdaki gibi bir ekranla karşılaşmış olman lazım.


Socket.IO modülü, debug için bir miktar Trace yazmış, ardından da bizim "User Connected" metnini yazdırmış. Demek ki, WebSocket ile bağlantı kurmayı başarmışız.

Şimdi de tarayıcımızı kapatalım. Bu durumda sunucu tarafında aşağıdaki gibi bir ekranla karşılaşmış olman lazım.


Demek ki, sayfa açık olduğu sürece arada aktif bir bağlantı varmış ki, sayfa kapandığı anda sunucu anında fark etti.

Ehh, bir şeyler gördük ama ben olsam bu kadarıyla etkilenmezdim. İlk olarak istemciden sunucuya nasıl mesaj gönderildiğini bir inceleyelim. Bunun için "socket" nesnesinin "emit" fonksiyonundan faydalanıyoruz. Bu fonksiyon iki parametre alıyor. İlki, karşı tarafta karşılanması gereken "Event" ismi. Sonradan sunucu tarafında bu Event'i yakalamak için bir Handler yazmamız gerekecek. Burada istediğimiz gibi bir isim seçebiliriz. İkinci parametre ise, göndermek istediğimiz veri ki, bu veri basit bir tip olabileceği gibi kompleks bir JSON nesnesi de olabilir.

Sunucuya "test" isimli bir Event fırlatmak ve veri olarak da "{ value1:"Ali", value2:49 }" göndermek için "sayfa.html" dosyamızın içeriğini aşağıdaki gibi değiştirelim.
<!DOCTYPE html>
<html>
<head>
<title></title>
<meta http-equiv="Content-Type" content="text/html; charset=windows-1254" />
<script src="http://ajax.googleapis.com/ajax/libs/jquery/2.0.3/jquery.min.js"></script>
<script src="socket.io.min.js"></script>
<script>
$(document).ready(function(){

    var socket = io.connect("http://localhost:1234");

    socket.emit("test", { value1:"Ali", value2:49 });

});
</script>
</head>
<body>
</body>
</html>

Ardından "test" isimli Event'i sunucu tarafında yakalamak ve ekrana yazdırmak için "program.js" dosyamızın içeriğini aşağıdaki gibi değiştirelim.
"use strict";

var http = require("http");

var server = http.createServer(processRequest).listen(1234, "127.0.0.1");

function processRequest(request, response) {

    response.writeHead(200, {
        "Content-Type": "text/html"
    });

    response.write("Simple HTML Page");

    response.end();

}

var io = require("socket.io").listen(server);

io.sockets.on('connection', function (socket) {

    socket.on("test", function (data) {

        console.log("Name : " + data.value1 + ", Age : " + data.value2);
    });

});

Bu durumda sunucuyu ayağa kaldırıp sayfayı açtığında, sunucu tarafında aşağıdaki gibi bir ekranla karşılaşmış olan gerekiyor.


Aslında burada yaptığımız değişiklik çok basit. Bir önceki örneğimizde "disconnect" Event'ini dinlemiştik. Burada ise "test" Event'ini dinliyoruz ve fonksiyona gelen "data" isimli nesnenin alt değişkenlerini ekrana yazdırıyoruz. Sunucuya "test" isimli Event'i ise html sayfamızdan, "socket" nesnesinin "emit" fonksiyonu vasıtasıyla fırlatıyoruz, hepsi bu kadar.

Dikkat : Tekrar hatırlatıyorum. Bu mesajların tamamı tek yönlüdür ve HTTP'de alışık olduğumuz gibi bir geri dönüş olmaz. Yukarıdaki örneğimizde, sayfamızdan sunucumuza "test" isimli bir Event vasıtasıyla veri gönderdik ve bunu sunucuda yakaladık. Hepsi bu kadar. İşlem burada tamamlanmış oldu.

İstemciden sunucuya mesaj gönderdik. Ama bunu HTTP ile de yapabiliyorduk zaten. Haydi artık, farklı bir şeyler görmenin zamanı gelmedi mi? Şimdi de sunucudan istemciye doğru bir mesaj gönderelim. Örneğin istemci sunucuya bir bağlantı açsın. Bu aktif bağlantı üzerinden sunucu, az önce kendine gelen mesajın aynısını istemciye göndersin. Bu sefer "test" Event'i için gerekli Handler'ı da istemci tarafına yazalım ve gelen veriyi ekrana yazdıralım.

Bunun için ilk olarak sunucu tarafındaki "sayfa.html" dosyamızın içeriğini aşağıdaki gibi değiştirelim.
<!DOCTYPE html>
<html>
<head>
<title></title>
<meta http-equiv="Content-Type" content="text/html; charset=windows-1254" />
<script src="http://ajax.googleapis.com/ajax/libs/jquery/2.0.3/jquery.min.js"></script>
<script src="socket.io.min.js"></script>
<script>
$(document).ready(function(){

    var socket = io.connect("http://localhost:1234");

    socket.on("test", function (data) {

        $("body").append("Name : " + data.value1 + ", Age : " + data.value2);
        $("body").append("<br>");
    });

});
</script>
</head>
<body>
</body>
</html>


Gördüğün gibi, istemcide mesaj göndermek için kullandığımız kodun aynısını sunucu tarafına taşıdık. Şimdi de "program.js" dosyamızın içeriğini aşağıdaki gibi değiştirelim.
"use strict";

var http = require("http");

var server = http.createServer(processRequest).listen(1234, "127.0.0.1");

function processRequest(request, response) {

    response.writeHead(200, {
        "Content-Type": "text/html"
    });

    response.write("Simple HTML Page");

    response.end();

}

var io = require("socket.io").listen(server);

io.sockets.on('connection', function (socket) {

    socket.emit("test", {
        value1: "Ali",
        value2: 49
    });

});

Yine gördüğün gibi, bu sefer de sunucu tarafındaki Event Handler'ı istemci tarafına taşıdık ve JQuery ile gelen veriyi uygun formatta ekrana yazdırdık. Bu durumda sayfayı açtıktan kısa bir süre sonra aşağıdaki gibi bir ekranla karşılaşmış olman gerekiyor.


Biliyorum. İstemciden bir Connection açıp karşılığında sunucudan bir cevap gelmesi o kadar da ilginç gözükmüyor. Neredeyse HTTP'deki akışa benziyor. Ama burada sunucudan gelen mesaj, istemcinin Connection açması sebebiyle değil, biz sunucudan mesaj göndermek istediğimiz için gidiyor.

Bak, bunu daha inandırıcı hale getirmek için şöyle yapalım; İstemci tarafının kodunu hiç değiştirmeyelim. Ama sunucu tarafındaki kodu aşağıdaki gibi değiştirelim.
"use strict";

var http = require("http");

var server = http.createServer(processRequest).listen(1234, "127.0.0.1");

function processRequest(request, response) {

    response.writeHead(200, {
        "Content-Type": "text/html"
    });

    response.write("Simple HTML Page");

    response.end();

}

var io = require("socket.io").listen(server);

io.sockets.on('connection', function (socket) {

    sendMessage(socket);

});

function sendMessage(socket) {

    setTimeout(function () {
        socket.emit("test", {
            value1: "Ali",
            value2: 49
        });

        sendMessage(socket);
    }, 1000);
}

Burada "sendMessage" isimli bir fonksiyonumuz var ve her 1 saniyede bir istemciye mesaj gönderiyor (Yani "test" isimli Event'i fırlatıyor). Sunucuyu bu şekilde çalıştırdıktan sonra sayfayı tekrar açarsan ve bir süre beklersen, aşağıdaki gibi bir ekranla karşılaşacaksın.


Şimdi biraz daha inandırıcı geldi mi? Bu protokolde, hem istemci sunucuya, hem de sunucu istemciye istediği zaman mesaj gönderebiliyor. WebSocket protokolü ile full dublex/çift yönlü veri akışına izin veren bir teknolojiye kavuşmuş oluyoruz.

Haydi, yeni oyuncağımızla biraz daha vakit geçirelim. Sunucu tarafında "connectionCount" adında bir değişkenimiz olsun ve ilk başlangıçta değerini 0 kabul edelim. Ardından her bir istemci bağlandığında (connection olayı) bu değeri bir arttıralım. İstemci bağlantıyı kestiğinde de (disconnect olayı) değeri bir azaltalım. Yani bu değişken, sitemize WebSocket ile eş zamanlı bağlı istemci sayısını gösteriyor olsun. Her yeni bağlanan istemciye de bu değeri "connectionCountChanged" isimli bir Event fırlatarak bildirelim. İstemci tarafında da bu Event'i yakalayacak bir Handler yazalım ve bu değeri ekrana yazdıralım.

Bunun için "sayfa.html" dosyamızın içeriği aşağıdaki gibi olmalı.
<!DOCTYPE html>
<html>
<head>
<title></title>
<meta http-equiv="Content-Type" content="text/html; charset=windows-1254" />
<script src="http://ajax.googleapis.com/ajax/libs/jquery/2.0.3/jquery.min.js"></script>
<script src="socket.io.min.js"></script>
<script>
$(document).ready(function(){

    var socket = io.connect("http://localhost:1234");

    socket.on("connectionCountChanged", function (data) {
        
        $("body").append("Connection Count : " + data);
        $("body").append("<br>");
    });

});
</script>
</head>
<body>
</body>
</html>


"program.js" dosyamızın içeriğini de aşağıdaki gibi değiştirmemiz gerekiyor.
"use strict";

var http = require("http");

var server = http.createServer(processRequest).listen(1234, "127.0.0.1");

function processRequest(request, response) {

    response.writeHead(200, {
        "Content-Type": "text/html"
    });

    response.write("Simple HTML Page");

    response.end();

}

var io = require("socket.io").listen(server);

var connectionCount = 0;

io.sockets.on('connection', function (socket) {

    connectionCount++;

    socket.emit("connectionCountChanged", connectionCount);

    socket.on("disconnect", function () {

        connectionCount--;
    });

});

Sunucuyu ayağa kaldırdıktan sonra üç adet tarayıcı pencere açtığımızı varsayalım. Bu durumda aşağıdaki üç ekranla karşılaşıyor olmamız lazım.


Güzel, sayı işe yarıyor. Yalnız en güncel değeri, sadece en son bağlanan istemci alıyor. Çünkü sunucu tarafındaki "connection" olayında bize gelen "socket" nesnesi, en son bağlanan istemciye ait ve bu nesne üzerinden "emit" fonksiyonunu çağırdığımızda, mesaj sadece en son bağlanan istemciye gidiyor.

Bir düşünelim bakalım, diğer istemciler için neler yapabiliriz. Öncelikle bu "socket" nesnesini bir diziye atabilir ve dizideki tüm "socket" nesnelerinin "emit" fonksiyonlarını çağırabiliriz ki, P2P (Peer2Peer ya da Client -> Client) bir uygulama yazmak isteseydik, zaten böyle bir şey yapmamız gerekecekti. Ancak Socket.IO'nun, o anda bağlı olan tüm istemcilere tek seferde mesaj göndermek için hazır bir fonksiyonu var, "broadcast.emit".

Bu fonksiyon, o ana kadar bağlantı kurmuş tüm istemcilere mesaj gönderiyor. Yalnız buraya dikkat, o ana kadar bağlantı kurmuş tüm istemcilere, yani yeni bağlanan istemciye göndermiyor. Bu durumda "connection" olayında hem "emit", hem de "broadcast.emit" fonksiyonlarını kullanarak mesaj gönderirsek, mesajımız tüm istemcilere ulaşmış olur.

"disconnect" olayında ise, zaten aktif socket kapandığı için sadece "broadcast.emit" fonksiyonunu çağırmak yeterli olacaktır. Bu örneği denemek için istemci kodumuza dokunmayalım ve "program.js" dosyamızın içeriğini aşağıdaki gibi değiştirelim.
"use strict";

var http = require("http");

var server = http.createServer(processRequest).listen(1234, "127.0.0.1");

function processRequest(request, response) {

    response.writeHead(200, {
        "Content-Type": "text/html"
    });

    response.write("Simple HTML Page");

    response.end();

}

var io = require("socket.io").listen(server);

var connectionCount = 0;

io.sockets.on('connection', function (socket) {

    connectionCount++;

    socket.broadcast.emit("connectionCountChanged", connectionCount);
    socket.emit("connectionCountChanged", connectionCount);

    socket.on("disconnect", function () {

        connectionCount--;

        socket.broadcast.emit("connectionCountChanged", connectionCount);
    });

});

Örnek olarak, arka arkaya dört sayfa açalım ve sonuncuyu kapatalım. Bu durumda aşağıdaki üç ekranla karşılaşıyor olman lazım.



Böylece sunucu tarafından istemcilere tek tek veya toplu olarak mesaj göndermenin örneklerini yapmış olduk. Elbette bu şekilde yazılabilecek sayısız örnek vardır ancak genel konsepti anlamak adına yeterli olur diye ümit ediyorum.

Bundan böyle sunucuya ikide bir "Öldüm mü?" diye sormak yok. Ecel gelince, sunucu "Azrail" adında bir Event fırlatıp bize haber verecektir.

Kısmen uzun bir makale oldu ama çok kafa karıştırmamışımdır diye ümit ediyorum. Aslında tüm örnekler boyunca "emit" fonksiyonu ile bir yerden Event fırlattık, karşı taraftan da "socket.on" ile bu Event'i karşıladık, hepsi bu kadar.

Makaleyi tamamlamadan önce Socket.IO hakkında da bir şeyler söylemek istiyorum. Socket.IO aslında, WebSocket'i kullanan bir kütüphane. Ancak WebSocket'in kullanılamadığı durumlarda (Örneğin eski bir tarayıcıya hizmet verirken), sırasıyla aşağıdaki yöntemlerden birini kullanarak, aynı fonksiyonaliteyi sağlamaya çalışır.
  • Adobe® Flash® Socket
  • AJAX long polling
  • AJAX multipart streaming
  • Forever Iframe
  • JSONP Polling

Bunların ne olduğunu veya nasıl çalıştığını bilmene gerek yok. Tek bilmen gereken, Socket.IO kullandığın takdirde, WebSocket kullanılamayan ortamlarda dahi fark etmeden değişik alternatiflerle aynı fonksiyonaliteye sahip olacağındır. Ancak bunlardan faydalanmak için başlangıçta "socket.io.min.js" dosyasını aldığımız "node_modules\socket.io\node_modules\socket.io-client\dist" klasöründe bulunan diğer dosyaları da HTML sayfamızın yanına kopyalaman gerekir (Burada birkaç Flash dosyası göreceksin). Bu şekilde, yazdığımız kodları değiştirmeden Internet Explorer 5.5'den itibaren PUSH yeteneğine sahip olma imkanına kavuşuyoruz. Allah'tan daha ne isteyelim yani.


Makalenin sonuna geldiğimize göre, artık öğrendiklerimizi özetleyebiliriz.
  • Klasik Ajax yaklaşımı ile sunucudaki veriyi, istemcinin PULL etmesi gerekiyordu.
  • WebSocket, aktif bir TCP bağlantı üzerinden çift yönlü mesaj alış verişine izin veren yeni bir protokoldür.
  • Node.JS ile WebSocket programlama yaparken, Microsoft'un satış politikalarına mahkum kalmayız.
  • Bu protokol sayesinde, hem istemci, hem de sunucu tarafından, istenildiği zaman Event fırlatılabilir ve karşı taraftan karşılanabilir.
  • Socket.IO, WebSocket protokolünü Node.JS üzerinde kullanabilmemiz için tercih edilebilecek, Node'un en popüler modüllerinden biridir.
  • Socket.IO, WebSocket'in desteklenmediği ortamlarda da, değişik alternatifler deneyerek, mevcut API'yi değiştirmeden aynı fonksiyonaliteyi korumaya çalışır.
  • WebSocket, tek taraflı bir mesajlaşma protokolüdür. Gönderilen mesaja geri bir cevap dönmez. Böyle bir duruma ihtiyacımız varsa, mesajı alan tarafın tekrar mesaj göndermesini sağlayacak bir kod yazmamız gerekir.
  • WebSocket ile istemci tarafından açılan Connection, istemci kapanmadığı sürece açık kalır ve her iki yönden de "socket" nesnesinin "emit" fonksiyonu ile mesaj gönderimi mümkün olur.
  • Sunucunun, o anda bağlı olan tüm istemcilere mesaj gönderebilmesi için "socket" nesnesinin "broadcast.emit" isimli bir fonksiyonu mevcuttur.


Yorum Gönder

  1. Ülkemizde nodejs ile ilgili bilgi karışıklığı ve yetersizliği var. Aynı zamanda globalde de sürüm farklılıklarından dolayı bir karman çormanlık mevcut. Yazınız için teşekkürler , broadcast.emit işime yaradı.

    YanıtlaSil
  2. Çok teşekkürler.
    Çok güzel bir makale olmuş.

    Örnek kodları proje olarak paylaşmanız mümkün mü?

    YanıtlaSil
  3. Çok teşekkürler.
    Çok güzel bir makale olmuş.

    Örnek kodları proje olarak paylaşmanız mümkün mü?

    YanıtlaSil
  4. helal olsun çok güzel anlatım

    YanıtlaSil
  5. Eline, diline sağlık üstad. Node'a yeni başladım. Senin paylaşımlarınla temellerini atıyorum.

    YanıtlaSil
  6. Ajax hakkında bile bilgim yoktu, bir çok node.js dökümanı da okudum bir şey anlamadım. websocketler üzerine oldukça açıklayıcı olmuş bir fikir sahibi oldum diyebilirim sayenizde.. Teşekkür ederim.

    YanıtlaSil

 
Top