使用socket.io+express实现网页聊天的实践

        近期,有客户提出希望尝试用HTML5相关技术实现在线交互和文件传输,于是乎找到了socket.io,通过搜索引擎和官方帮助实现了简单的聊天和指定用户的消息广播,在此总结跟大家分享。

A.环境安装与配置

首先,安装nodejs,直接官网下载对应平台对应版本即可。地址为http://nodejs.org/安装,我安装的是Mac版的dmg。

接下来就可以使用终端来安装socket.io和express模块,命令如下:

sudo sh
npm install -g socket.io
npm install -g express
B.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;
            }
        }
    });

参考资料

cnodechat聊天室设计及实现介绍

socket.io 简介及使用

使用socket.io+express实现网页聊天的实践,古老的榕树,5-wow.com

郑重声明:本站内容如果来自互联网及其他传播媒体,其版权均属原媒体及文章作者所有。转载目的在于传递更多信息及用于网络分享,并不代表本站赞同其观点和对其真实性负责,也不构成任何其他建议。