nodeJs socketJs 搭建长连接(1) 基础讲解

聊天服务器开发

环境搭建

首先下载node.js,前往官网下载并安装,在此中需要下载安装express和socket,前者为node开发web的环境,后者为我们开发聊天服务器的js

搭建项目

1.新建文件夹,qqDemo
2.在项目下安装express和socket
npm install --save express
npm install --save socket.io
3.新建一个index.js作为启动js
4.新建index.html作为前端展示
5.启动 进入qqDemo文件夹中,使用命令 node index.js启动站点服务

文件阐述

index.js

var app = require('express')();
var http = require('http').Server(app);
var io = require('socket.io')(http);
 
app.get('/', function(req, res){
    res.send('<h1>Welcome Realtime Server</h1>');
});
 
http.listen(3000, function(){
    console.log('listening on *:3000');
});

var onlineUsers = new Array();
var users = {};
io.sockets.on('connection', function (socket) {
  io.sockets.emit('connect',{'status':'正确'});
  console.log("new conns join ..");
  socket.on('private message', function (from,to,msg) {
    console.log('I received a private message by ', from, ' say to ',to, msg);
    if(to in users){
        users[to].emit('to'+to,{mess:msg});
    }
  });
  socket.on('new user',function(data){
	 //console.log("socket标示为:"+socket.id);
     if(data in users){
         
     }else{
        var nickname = data;
        users[nickname]= socket;
		onlineUsers.push(data);
		console.log('用户'+data+'加入聊天室');
        console.log('当前的在线用户有 '+onlineUsers);		
     }
	 io.sockets.emit('online users',onlineUsers); 
  });
  socket.on('disconnect', function () {
	  var logoutUserName ;
	  for(var obj in users){
		  console.log("obj的值是:"+obj);
			if(users[obj] == socket){
				console.log(obj+"用户退出聊天室");
				logoutUserName = obj;
				delete users[obj];
			}
		}
		for(var i = 0; i<onlineUsers.length; i++){
			if(onlineUsers[i] == logoutUserName){
				onlineUsers.splice(i,i);
			}
		}
		console.log("当前在线人员:"+onlineUsers);
		//更新在线用户
		io.sockets.emit('online users',onlineUsers); 
        io.sockets.emit('user disconnected');
  });
});

index.html

<!DOCTYPE html>
<html>
    <head>
        
		<meta http-equiv="Content-Type" content="text/html; charset=gb2312">

        <meta name="format-detection" content="telephone=no"/>
        <meta name="format-detection" content="email=no"/>
		<meta content="width=device-width, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0, user-scalable=0" name="viewport">
        <title>多人聊天室</title>
        <!--[if lt IE 8]><script src="./json3.min.js"></script><![endif]-->
		<script src="http://ajax.googleapis.com/ajax/libs/jquery/1.11.0/jquery.min.js"></script>
        <script src="http://127.0.0.1:3000/socket.io/socket.io.js"></script>
    </head>
    <body>
    <script>
        $(function(){
		    var onlineUsers;
		    var socket;
			var userName;
		    $("#login").on("click",function(){
			    socket = io.connect('ws://localhost:3000');
				socket.on('connect', function (data) {              
				userName = $('#user_name').val();
				socket.emit('new user',userName);
				$('#div').show();
				//接受聊天信息
				socket.on('to'+userName, function (data) {
				    alert("来消息了"+data.mess);
					console.log("来消息了"+data.mess);
                    $message_list.append('<li>'+data.from+'说'+data.message+'</li><li>');
                });
				//获得当前在线人员
				socket.on("online users",function(data){
				   onlineUsers = data;
				   console.log("刷新在线人数");
				   $("#select").empty();
				   for(var j=0; j< onlineUsers.length; j++){
				       var option = $("<option value='"+onlineUsers[j]+"'>"+onlineUsers[j]+"</option>");
					   $("#select").append(option);
				   }
				   
				});
              });
			});	
				
             $("#send").click(function(e){     
                  var msg  = $('#message').val(),
                      to = $('#select').val(),
                 $message_list = $('#message_list');
                 socket.emit('private message',userName,to,msg);
              
             });
        });
    </script>
	<div>
	 姓名:<input id="user_name" type="text"><br>
	 <input type="button" value="登陆" id="login">
	</div>
	
	<div id="div"  style="display:none">
	在线用户:<select id="select"></select><br>
    消息内容:<input type="text" id="message"><button type="button" id="send">发送</button>
	</div>
   
    <ul id="message_list">
         
    </ul>
    </body>
</html>

1) JS 对象处理

//js 对象
var a = {};
//特殊的方法对对象进行复制
a['a'] = "aaa";
a['b'] = "bbb";
a['c'] = "ccc";

console.log(a);
//结果
//{a:'aaa',b:'bbb',c:'ccc'}

for(var obj in a){
	//遍历对象中所有的属性
	if(a[obj] == "bbb"){
		console.log(a[obj]);
		//删除该属性
		delete a[obj];
	}
	if(obj == 'function'){
		console.log('这是一个方法');
	}
	console.log(obj);
}

2)socketJs 事件语法

服务器信息传输

// send to current request socket client
socket.emit('message', "this is a test");

// sending to all clients except sender
socket.broadcast.emit('message', "this is a test");

// sending to all clients in 'game' room(channel) except sender
socket.broadcast.to('game').emit('message', 'nice game');

// sending to all clients, include sender
io.sockets.emit('message', "this is a test");

// sending to all clients in 'game' room(channel), include sender
io.sockets.in('game').emit('message', 'cool game');

// sending to individual socketid
io.sockets.socket(socketid).emit('message', 'for your eyes only');

请注意,由于版本问题如果上述的io.sockets.scoket(socketid).emit(),该方法不能正常使用,请这样写,io.sockets.connected[socketid].emit('message',msg);

上述集中方式为socket.io常用的数据传输方式

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

});

回调函数的socket参数为一个 client 与服务器的连接标示,不同的 client 会有不同的连接标示。

不分组,数据传输

  • socket.emit 
    socket.emit 信息传输对象为当前 socket 对应的 client ,各个 client socket 相互不影响。

  • socket.broadcast.emit 
    socket.broadcast.emit 信息传输对象为所有 client ,排除当前 socket 对应的 client 。

  • io.sockets.emit 
    信息传输对象为所有 client 。

分组数据传输

类似于之前提过的 of 方法生成命名空间来管理用户, socket.io 可以使用分组方法, socket.join() ,以及与之对应的 socket.leave() 。

io.sockets.on('connection', function (socket) {
  socket.on('firefox', function (data) {
    socket.join('firefox');
  });
  socket.on('chrome',function(data){
    socket.join('chrome');
  });
});

假设有两个聊天室,一个名为firefox,另一个为chrome,客户端操作

socket.emit('firefox') ,就可以加入 firefox 聊天室; 
socket.emit('chrome') ,就可以加入 chrome 聊天室;

向一个分组传输消息,有两种方式:

socket.broadcast.to('chrome').emit('event_name', data);
  //emit to 'room' except this socket client
io.sockets.in('chrome').emit('event_name', data)
  //emit to all socket client in the room

broadcast 方法允许当前 socket client 不在该分组内。

可能有一个疑问,一个 socket 是否可以同时存在于几个分组,等效于一个用户会同时在几个聊天室活跃,答案是”可以“, socket.join() 添加进去就可以了。官方提供了订阅模式的示例:

socket.on('subscribe', function(data) { 
    socket.join(data.room);
})

socket.on('unsubscribe', function(data) { 
    socket.leave(data.room);
 })

后台处理订阅/退订事件

socket = io.connect('http://127.0.0.1:1338/');
socket.emit('subscribe',{"room" : "chrome"};
socket.emit('unsubscribe',{"room" : "chrome"};

前端触发订阅/退订事件,就可以加入对应的聊天室。 通过 of 方法也可以通过划分命名空间的方式,实现聊天室功能,但不如分组管理来的方便。

Socket.io难点大放送(暂时没有搞定)

  • 授权验证 
    socket 连接需要添加权限验证,让已登录的用户 socket 连接到服务器,未登录的用户无条件拒绝。全局授权管理如下:
io.sockets.authorization(function (handshakeData, callback) {
     callback(null, true);
}).

callback 函数有两个参数,第一个为 error ,第二个参数为是否授权bool值,通过授权回调函数应为 callback(null,true) ,其它情况下都为拒绝建立连接。

按照web的开发方式,检测是否登录首选 cookie-session 来实现,问题也是出在这里。 websocket 握手阶段属于 HTTP 协议,简单来说是可以读到cookie,就可以实现session。 
+ 精准单用户推送 
理论上来说

// sending to individual socketid
io.sockets.socket(socketid).emit('message', 'for your eyes only');

就可以向一个特定用户推送消息,但是如何获得这个 socketId ,就是生成一个哈希数组,key为username,值为socket.id,这样就可以通过用户名获取对应的id,进而可以向特定client推送消息。

如果是想关机的时候让服务不间断,后台运行,nohup node index.js(文件名) &如果是想简单启动,写个启动脚本,将nohup node index.js(文件名) 写入.sh文件

学习前人经验

NodeJs集成Express开发web站点
http://blog.fens.me/nodejs-express3/

NodeJs和socketJs开发私人聊天
http://www.2cto.com/kf/201403/285640.html

SocketIoJs 创建连接参数
http://www.kazaff.me/2014/03/12/

socketIo三种读写方式,可用于发送图片
http://blog.csdn.net/awangz/article/details/8954798

NodeJs和socketJs开发公众聊天室
http://www.open-open.com/lib/view/open1402479198587.html

前端GruntJs开发web项目
http://www.cnblogs.com/wangfupeng1988/p/4561993.html

socket.io 入门整理,很好的一篇,包括存储数据和接收消息,命名空间
https://cnodejs.org/topic/50a1fcc7637ffa4155b5a264

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值