Jump to content
Sign in to follow this  
gaby

Cum Sa Fac Un Chat Realtime ? Cu Node.js !

Recommended Posts

Nota: Articolul a fost scris de Roland Szabo pe blogul personal.

nodejslogo.png

Sa incepem cu primirea de date de la browser. Vom face asta cu un simplu XMLHttpRequest. Sa modificam pagina SSE.html sa contina urmatoarele elemente:

<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8"/>
</head>
<body><input type='text' id='message'/>
<button value='Send' id='send'>Send</button>
<script>
document.getElementById('send').addEventListener('click', sendmessage);
document.getElementById('message').addEventListener('keydown', function(event) {
if (event.keyCode == 13) {
sendmessage();
}
});
function sendmessage() {
var req = new XMLHttpRequest();
req.open('POST', '/xhr');
req.setRequestHeader("Content-Type", "application/x-www-form-urlencoded");
req.send(document.getElementById('message').value);
req.onreadystatechange = function (e) {
if (req.readyState == 4) {
if (req.status == 200)
;//console.log(req.responseText);
else
console.log("Error loading page\n");
}
};
}; </script>
</body>
</html>[/code] [b]Ce avem aici?[/b] Un tag input si un button. Codul Javascript contine o functie si doua event listeners care cheama acea functie: una cand facem click pe butonul Submit, iar cealalta cand apasam Enter in casuta de "[i]chat[/i]". Functia nu face altceva decat sa trimita prin POST continutul inputului la [b]/xhr[/b]. Cum arata acum fisierul [b][color="#FF0000"]chat.js[/color][/b]?
[code]var http = require('http');
var fs = require('fs');
http.createServer(function(req, res) {
if (req.method == "POST" && req.url == '/xhr') {
var data = '';
req.addListener('data', function(chunk) {
data += chunk;
});
req.addListener('end', function() {
res.writeHead(200, {'Content-Type': 'text/html'});
res.write(data);
res.end();
console.log(data);
});
} else {
res.writeHead(200, {'Content-Type': 'text/html'});
res.write(fs.readFileSync(__dirname + '/sse.html'));
res.end();
}
}).listen(8000);
Daca cererea este prin POST si la adresa /xhr, atunci ne pregatim sa primim continutul prin POST. Mai intai initializam variabila data cu un sir de caractere gol. Apoi atasam functii la doua evenimente specifice requesturilor HTTP: data si end. Eventul data se trimite atunci cand se primeste o bucata noua de date, iar cel de end se primeste la … sfarsitul trimiterii. La fiecare "data" adaugam ce s-a primit la acel event la ce am primit mai inainte, iar cand s-a terminat transmisia, atunci prelucram. in acest caz trimitem inapoi ce am primit si scriem la consola continutul mesajului primit. Daca este orice alt tip de cerere, atunci afisam pagina sse.html. Ok, acum e timpul sa combinam ce am invatat data trecuta (trimiterea de mesaje de la server la browser) cu ce am invatat azi (prelucrarea datelor primite de server).
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
</head>
<body>
<ul>

</ul>
<input type='text' id='message' />
<button value='Send' id='send'>Send</button>
<script>
var source = new EventSource('/events');
source.addEventListener('message', function(e) {
var ul = document.getElementsByTagName('ul')[0];
var li = document.createElement('li');
var data = e.data.split('\n')
li.innerHTML = '<strong>'+data[0]+'</strong>:'+data[1];
ul.appendChild(li);
console.log(e);
}, false);

source.addEventListener('open', function(e) {
console.log('open');
}, false);

source.addEventListener('error', function(e) {
if (e.eventPhase == EventSource.CLOSED) {
console.log('close');
}
}, false);
document.getElementById('send').addEventListener('click',sendmessage);
document.body.addEventListener('keydown',function(event) {
if (event.keyCode == 13) {
sendmessage();
}
});
function sendmessage() {
var req = new XMLHttpRequest();
req.open('POST', '/xhr');
req.setRequestHeader("Content-Type", "application/x-www-form-urlencoded");
req.send(document.getElementById('message').value);
req.onreadystatechange = function (e) {
if (req.readyState == 4) {
if (req.status == 200)
;//console.log(req.responseText);
else
console.log("Error loading page\n");
}
};
};
</script>
</body>
</html>[/code] Asta e aproape perfect combinatia celor doua fisiere de dinainte, cu exceptia faptului ca avem o lista in care o sa inseram mesajele de chat, iar cand primim un mesaj de la server, il despartim la ‘\n’. Datele de la server vor veni astfel incat pe un rand va fi numele celui care a trimis mesajul, iar pe al doilea rand va fi mesajul propriu-zis.
[code]var http = require('http');
var fs = require('fs');

//Array to contain active resources
var resources = new Array();

http.createServer(function(req, res) {
if (req.headers.accept && req.headers.accept == 'text/event-stream') {
if (req.url == '/events') {
res.writeHead(200, {
'Content-Type': 'text/event-stream',
'Cache-Control': 'no-cache',
'Connection': 'keep-alive'
});
//Create a random ID for each guest in the chatroom
res.chatID = 'guest'+Math.floor(Math.random()*1000);
resources.push(res);
//Remove from active chat resources if connection is closed
req.addListener('close',function (event) {
console.log('A connection was closed');
var index = resources.indexOf(res);
if (index != -1) {
resources.splice(index,1);
}
});
} else{
res.writeHead(404);
res.end();
}
} else {
if(req.method == "POST" && req.url =='/xhr') {
var data = '';
req.addListener('data',function(chunk) {
data+=chunk;
});
req.addListener('end',function() {
res.writeHead(200, {'Content-Type': 'text/html'});
res.write(data);
res.end();
//send the data to all the active chat resources
for (var i=0; i<resources.length; i++) {
constructSSE(resources[i],2,[resources[i].chatID,data]);
}
});

}
else {
res.writeHead(200, {'Content-Type': 'text/html'});
res.write(fs.readFileSync(__dirname + '/sse-node.html'));
res.end();
}
}
}).listen(8000);

function constructSSE(res, id, data) {
res.write('id: ' + id + '\n');
//Adds the capability to send multiple lines of data if they are in an array
if (data.constructor != Array)
data = [data];
for (var i = 0; i<data.length; i++)
res.write("data: " + data[i] + '\n');
res.write('\n');
}

Ce facem aici? Pai in primul rand, facem ceva ce nu putem face in PHP: stocam intr-un array fiecare response ca sa stim la cine trebuie sa trimitem informatii inapoi. Cand cineva se conecteza la event source, il adaugam la acest array, iar daca se intrerupe conexiunea, il eliminam. Deasemenea, fiecarui response ii asociem un ID, ca sa putem identifica fiecare utilizator care chatuieste. Cand primim un mesaj prin XHR, il trimitem mai departe la fiecare abonat din arrayul nostru.

!!! Fisierele pot fi descarcate de aici: :link:

Numai bine :pct2: !

Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now
Sign in to follow this  

  • Recently Browsing   0 members

    No registered users viewing this page.

×