近期,有客户提出希望尝试用HTML5相关技术实现在线交互和文件传输,于是乎找到了socket.io,通过搜索引擎和官方帮助实现了简单的聊天和指定用户的消息广播,在此总结跟大家分享。
A.环境安装与配置
首先,安装nodejs,直接官网下载对应平台对应版本即可。地址为http://nodejs.org/安装,我安装的是Mac版的dmg。
接下来就可以使用终端来安装socket.io和express模块,命令如下:
sudo sh npm install -g socket.io npm install -g expressB.socket.io简介
node.js提供了高效的服务端运行环境,为了解决浏览器的兼容性问题,同时提供客户端与服务端一致的编程体验,于是socket.io诞生了。
socket.io 是一个为实时应用提供跨平台实时通信的库,包括服务器端和客户端。socket.io 旨在使实时应用在每个浏览器和移动设备上成为可能,模糊不同的传输机制之间的差异。只要在服务器端启动服务端js脚本(node app.js),就在某个端口开启了一个实时监听的通道,只要客户端连接就可以进行相互通讯。下面是官网socket.io使用的最简单示例:
SERVER var io = require(‘socket.io‘).listen(80); io.sockets.on(‘connection‘, function (socket) { socket.emit(‘news‘, { hello: ‘world‘ }); socket.on(‘my other event‘, function (data) { console.log(data); }); }); CLIENT <script src="/socket.io/socket.io.js"></script> <script> var socket = io.connect(‘http://localhost‘); socket.on(‘news‘, function (data) { console.log(data); socket.emit(‘my other event‘, { my: ‘data‘ }); }); </script>C.使用socket.io和express构建网页聊天室
环境搭建好之后,构思应用场景,客户端页面加载完成后,自动创建一个用户名(可以是登陆名),然后跟服务器端通讯,发送用户名,服务器端广播欢迎用户的消息,同时纪录用户名称和ip等信息。正常聊天时可以通过广播机制发送消息,每一个人员都可收听的到,而特殊情况可以@用户名只将消息发送给指定的用户群(room机制)。同时,在用新用户登陆或者离开是,系统需要更新每个用户的用户列表并广播用户退出的信息。
服务端主要代码
//记录在线用户 var userList={}; //记录IP var ipList={}; //WebSocket连接监听 io.on(‘connection‘, function (socket) { socket.emit(‘open‘);
// 打印连接信息
var address = socket.handshake.address;
console.log("New connection from " + address.address + ":" + address.port);
// 构造客户端对象
var client = {
socket:socket,
name:false
}
// 对message事件的监听
socket.on(‘message‘, function(msg){
var obj = {time:getTime()};
// 判断是不是第一次连接,消息为用户名称
if(!client.name){
client.name = msg;
obj[‘text‘]=msg;
obj[‘author‘]=‘System‘;
obj[‘type‘]=‘welcome‘;
console.log(client.name + ‘ login‘);
var usertxt=client.name;
//纪录用户和ip信息,用于房间用户筛选,局部通讯
if (!userList[usertxt]) {
userList[usertxt] = 1;
} else {
userList[usertxt] ++;
}
ipList[socket.handshake.address.address]=usertxt;
//返回欢迎语
socket.emit(‘system‘,obj);
//广播新用户已登陆
socket.broadcast.emit(‘system‘,obj);
var objusr = {time:getTime()};
objusr[‘text‘]=userList;
objusr[‘author‘]=‘System‘;
objusr[‘type‘]=‘userConnected‘;
//更新用户列表 io.sockets.emit(‘system‘,objusr); }else{ //如果不是第一次的连接,正常的聊天消息 obj[‘text‘]=msg; obj[‘author‘]=client.name; obj[‘type‘]=‘message‘; console.log(client.name + ‘ say: ‘ + msg); var arr=msg.split("@"); //将消息用@符号进行分割,筛选出关联的在线用户 if(arr.length<3 || arr[0]!="") { // 返回消息(可以省略) socket.emit(‘message‘,obj); // 广播向其他用户发消息 socket.broadcast.emit(‘message‘,obj); } else { var flag=0;
//根据用户名称找到用户对应ip地址,再通过ip地址找到对应的socket连接,将@关联的用户socket加入到一个room中
io.sockets.clients().forEach(function (socket) {
for(ip in ipList)
{
for(i=0;i<arr.length;i++)
{
if(ipList[ip]==arr[i] && ip==socket.handshake.address.address)
{
//加入到指定房间
socket.join(‘someUsers‘); flag++; } } } }); if(flag==0) { socket.join(‘me‘); obj[‘text‘]="请核实您关联的用户名!"; obj[‘author‘]=‘系统‘; io.sockets.in(‘me‘).emit(‘message‘, obj); } else { obj[‘text‘]=arr[arr.length-1];
//将消息发送给加入该房间的用户
io.sockets.in(‘someUsers‘).emit(‘message‘, obj); } } } }); //监听出退事件 socket.on(‘disconnect‘, function () { var obj = { time:getTime(), color:client.color, author:‘System‘, text:client.name, type:‘disconnect‘ }; var userName=client.name; if (userList[userName]) { userList[userName] --; if (userList[userName] == 0) { delete userList[userName]; } } for(ip in ipList) { if (ipList[ip]==client.name) { delete ipList[ip]; } console.log(ip); } // 广播用户已退出 socket.broadcast.emit(‘system‘,obj); console.log(client.name + ‘Disconnect‘); var objusr = {time:getTime()}; objusr[‘text‘]=userList; objusr[‘author‘]=‘System‘; objusr[‘type‘]=‘userConnected‘; socket.broadcast.emit(‘system‘,objusr); });其次,express的简单配置
//express基本配置 app.configure(function(){ app.set(‘port‘, process.env.PORT || 3000); app.set(‘web‘, __dirname + ‘/web‘); app.use(express.favicon()); app.use(express.logger(‘dev‘)); app.use(express.bodyParser()); app.use(express.methodOverride()); app.use(app.router); app.use(express.static(path.join(__dirname, ‘public‘))); }); app.configure(‘development‘, function(){ app.use(express.errorHandler()); }); // 指定webscoket的客户端的html文件 app.get(‘/‘, function(req, res){ res.sendfile(‘web/index.html‘); }); server.listen(app.get(‘port‘), function(){ console.log("Express server listening on port " + app.get(‘port‘)); });
因为nojo可以直接运行js,所以开发应用可phonegap打包到不同平台,同时也可以支持在线的访问。
客户端
与服务器端对应的响应代码
var $chars = ‘ABCDEFGHJKMNPQRSTWXYZabcdefhijkmnprstwxyz2345678‘;
var maxPos = $chars.length;
var usr = ‘‘;
for (i = 0; i < 6; i++) {
usr += $chars.charAt(Math.floor(Math.random() * maxPos));
}
var userName=usr;
//建立websocket连接
socket = io.connect(‘http://192.168.0.23:3000‘);
socket.send(userName);
//收到server的连接确认
socket.on(‘open‘,function(json){
status.text(json);
});
//监听system事件,判断welcome或者disconnect,打印系统消息信息
socket.on(‘system‘,function(json){
var p = ‘‘;
if (json.type === ‘welcome‘){
if(userName==json.text) status.text(userName + ‘:‘);
p = ‘系统@‘+ json.time+ ‘:欢迎‘ + json.text +‘!\n‘;
var txt=content.val();
content.text(txt+p);
}else if(json.type==‘userConnected‘)
{
p=json.text;
var usrlist=‘‘;
for(usr in p)
usrlist=usrlist+usr+‘\n‘;
userlist.html(usrlist);
}
else if(json.type == ‘disconnect‘){
p = ‘系统@‘+ json.time+ ‘:再见‘ + json.text +‘!\n‘;
var txt=content.val();
if(json.text==‘false‘)
return;
else
content.text(txt+p);
}
});
//监听message事件,打印消息信息
socket.on(‘message‘,function(json){
var p = json.author+‘@‘+ json.time+ ‘ : ‘+json.text + ‘\n‘;
var txt=content.val();
content.html(txt+p);
});
//通过“回车”提交聊天信息
input.keydown(function(e) {
if (e.keyCode === 13) {
var msg = $(this).val();
if (!msg) return;
socket.send(msg);
$(this).val(‘‘);
if (userName === false) {
userName = msg;
}
}
});
参考资料