nodejs实战案例(Express框架+mongoDB)——(14)——socket通信,聊天功能

现在我们来实现用户聊天在线聊天功能,这里我们需要使用到socket

我们这里新建一个socket.js文件在modules文件夹下

socket.js的内容为:

module.exports = function(io){  
};

然后我们需要修改下app.js里面的内容在

 , flash = require('connect-flash')

后面添加

, sock= require('./models/socket');//这里依赖一个我们自己写的socket.js

然后将

http.createServer(app).listen(app.get('port'), function(){

  console.log('Express server listening on port ' + app.get('port'));
});

替换为

var server = http.createServer(app);
var io = require('socket.io').listen(server);
//socket 通信
sock(io);//执行socket.js里面的内容

server.listen(app.get('port'), function(){
  console.log('Express server listening on port ' + app.get('port'));
});

这里通信需要很多内容和前端配合: 

首先分析下需求,用户登录,就会在用户个人中心显示用户在线,

所以我们只要用户登录页面,就会向后端发送信息,所以登录给后端发送信息需要将通知放在header.js里面

在header.js的head里面添加

<script src="/socket.io/socket.io.js"></script>

然后在下面的js里面加上

var socket = io.connect();

末尾加上

<% if(user){%>

<script>

//如果登陆,发送信息,信息的内容是一个对象

socket.emit('online',{user:"<%= user.name%>"});

</script>

<% }%>


在后端socket.js修改为:

module.exports = function(io){

//存储在线用户列表

var users = [];

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

 socket.on('online',function(data){

   //将上线的用户名存储为 socket 对象的属性,以区分每个 socket 对象,方便后面使用

   socket.name = data.user;

   //数组中不存在该用户名则插入该用户名

   if(users.indexOf(data.user) == -1){

     users.unshift(data.user);

   }

   //向所有用户广播该用户上线信息

   io.sockets.emit('online',{users:users,user:data.user});

});

});

};


上面在收到用户上线信息后,会向所有用户发送一个上线信息,结束的地址在people.ejs页面

在末尾的js中加上:

//接受上线消息

socket.on('online',function(data){

  //如果刚刚上线用户是这个用户中心页面的用户,就显示在线按钮

  if(data.user=="<%= name%>"){

    $("#talk").show();

    $("#offline").hide();

  }

  //如果上线组的用户中有这个页面用户,也显示在线按钮

  if(data.users.indexOf("<%= name%>") != -1){

    $("#talk").show();

    $("#offline").hide();

  }

});


可以在两个浏览器上面分别用两个用户登录试试,成功时聊天按钮会被点亮


下一个是接受用户下线通知,

在socket.js的socket.on('online',function(data){})后面添加

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

 //若 users 数组中保存了该用户名

 if(users.indexOf(socket.name) != -1){

   //从 users 数组中删除该用户名

   users.splice(users.indexOf(socket.name),1);

   //向其他所有用户广播该用户下线信息

   socket.broadcast.emit('offline',{users:users,user:socket.name});

 }

});


在people.ejs的<script>末尾加上

//接受下线消息

socket.on('offline',function(data){

  if(data.user=="<%= name%>"){

    $("#talk").hide();

    $("#offline").show();

  }

}); 


这时打开两个浏览器,如果下线一个a用户,B用户看到的a用户的用户中心页面,聊天按钮会变暗。


最后我们添加最核心的聊天功能

我们把聊天功能也放在header.ejs里面,这样用户可以在任何页面接受聊天信息和聊天

在header.ejs

中提问的div模块后添加

<!-- 聊天 -->

<div id="talkBox" class="modal hide fade" tabindex="-1" role="dialog" aria-labelledby="myModalLabel" aria-hidden="true">

  <div class="modal-body">

    <p>聊天</p>

    <div class="modal-body" id="talkCom">

    </div>

    <textarea name="askText" rows="5" placeholder="正文" style="width:400px" id="selfTalk"></textarea>

  </div>

  <div class="modal-footer">

    <button class="btn" data-dismiss="modal" aria-hidden="true" id="closeAsk">关闭</button>

    <button class="btn" id="talkPost">提问</button>

  </div>

</div>


然后在var socket = io.connect();后面

添加

var talkNews={};

//接收聊天消息

socket.on('selfTalk',function(data){

  talkNews.from=data.from;

  $("#news").show();

  $("#talkCom").append("<p>"+data.from+"对你说:"+data.talk+"</p>");

});

//发送聊天消息

$("#talkPost").on("click",function(){

  if($("#toName").length>0){

    talkNews.from=$("#toName").text();

  }

  socket.emit('selfTalk',

    {from:$("#myName").text(),

    to:talkNews.from,

    talk:$("#selfTalk").val()},

    function(info){

      if(info=="ok"){

        $("#talkCom").append("<p style='color:pink;'>你对"+$("#toName").text()+"说:"+$("#selfTalk").val()+"</p>");

        $("#selfTalk").val("");

      }

  });

});

//点击消息显示

$("#news").on("click",function(){

  $("#news").hide();

});


这时需要在后端的socket.js里面修改为:

module.exports = function(io){

//存储在线用户列表

var users = [];

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

 socket.on('online',function(data){

   //将上线的用户名存储为 socket 对象的属性,以区分每个 socket 对象,方便后面使用

   socket.name = data.user;

   //数组中不存在该用户名则插入该用户名

   if(users.indexOf(data.user) == -1){

     users.unshift(data.user);

   }

   //向所有用户广播该用户上线信息

   io.sockets.emit('online',{users:users,user:data.user});

});

//接受用户下线通知

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

 //若 users 数组中保存了该用户名

 if(users.indexOf(socket.name) != -1){

   //从 users 数组中删除该用户名

   users.splice(users.indexOf(socket.name),1);

   //向其他所有用户广播该用户下线信息

   socket.broadcast.emit('offline',{users:users,user:socket.name});

 }

});

//私信聊天

socket.on('selfTalk',function(data,fn){

fn("ok");

var clients = io.sockets.clients();

clients.forEach(function(client){

     if(client.name == data.to){

       //触发该用户客户端的 say 事件

       client.emit('selfTalk',data);

     }

   });

});

});

};

效果如下:

nodejs实战案例(Express框架+mongoDB)——(14)——socket通信,聊天功能


这里看socket可能很蒙,建议大家在按照代码实现一次功能,然后参考下面api

引用来自:http://www.csser.com/board/4f5ed68d417a7f6a6e000059

--------------------------------------------------------------------------------------------------------

 socket.io与express结合使用

socket.io 与 express 结合使用

我们可以使用 expressjs 处理页面请求以及 Ajax 请求,由于 express 是 nodejs 平台下最流行的 web 服务端开发框架,因此 socket.io 提供了方便的方式来绑定 express 服务。

服务器端代码


var app = require('express').createServer()
  , io = require('socket.io').listen(app);

app.listen(80);

app.get('/', function (req, res) {
  res.sendfile(__dirname + '/index.html');});

io.sockets.on('connection', function (socket) {
  socket.emit('news', { hello: 'world' });
  socket.on('my other event', function (data) {
    console.log(data);
  });});

浏览器端代码



<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>


8 条评论一回•2012-03-13 13:19

 发送以及接收自定义事件

发送以及接收自定义事件

除了 socket.io 提供的默认事件(如:connect, message, disconnect)外,我们还可以发送以及接收自定义事件的数据。

示例代码

// note, io.listen(<port>) will create a http server for youvar io = require('socket.io').listen(80);

io.sockets.on('connection', function (socket) {
  io.sockets.emit('this', { will: 'be received by everyone'});

  socket.on('private message', function (from, msg) {
    console.log('I received a private message by ', from, ' saying ', msg);
  });

  socket.on('disconnect', function () {
    io.sockets.emit('user disconnected');
  });});

添加评论一回•2012-03-13 13:24

 将数据关联并存储到当前连接的socket

将数据关联并存储到当前连接的 socket

在一个会话周期中,我们大部分情况下都需要存储当前会话者的一些数据,来识别或者特定情形下获取这些数据。

示例代码


var io = require('socket.io').listen(80);

io.sockets.on('connection', function (socket) {
  socket.on('set nickname', function (name) {
    socket.set('nickname', name, function () {
      socket.emit('ready');
    });
  });

  socket.on('msg', function () {
    socket.get('nickname', function (err, name) {
      console.log('Chat message by ', name);
    });
  });});

非常建议使用这种方式来设置用户会话的数据。


1 条评论一回•2012-03-13 13:29

 发送、接收需要确认的数据

发送、接收需要确认的数据

在服务端与终端发送消息的过程中,如需要对方接收到消息后立刻得到确认,则只需在 .send 或 .emit最后一个参数传入回调函数就可以了。

服务端代码

var io = require('socket.io').listen(80);

io.sockets.on('connection', function (socket) {
  socket.on('ferret', function (name, fn) {
    fn('woot');
  });});

客户端代码

<script>
  var socket = io.connect(); // TIP: .connect with no args does auto-discovery
  socket.on('connect', function () { // TIP: you can avoid listening on `connect` and listen on events directly too!
    socket.emit('ferret', 'tobi', function (data) {
      console.log(data); // data will be 'woot'
    });
  });</script>

添加评论一回•2012-03-13 13:36

 socket.io路由

socket.io 路由

官网称之为命名空间绑定,但我觉得用路由来形容似乎更好理解,上示例代码:


var io = require('socket.io').listen(80);var chat = io
  .of('/chat')
  .on('connection', function (socket) {
    socket.emit('a message', {
        that: 'only'
      , '/chat': 'will get'
    });
    chat.emit('a message', {
        everyone: 'in'
      , '/chat': 'will get'
    });
  });var news = io
  .of('/news')
  .on('connection', function (socket) {
    socket.emit('item', { news: 'item' });
  });

当客户端连接 /chat 时,由 chat 来处理,连接 /news 由 news 来处理。



<script>
  var chat = io.connect('http://localhost/chat')
    , news = io.connect('http://localhost/news');
  
  chat.on('connect', function () {
    chat.emit('hi!');
  });
  
  news.on('news', function () {
    news.emit('woot');
  });</script>


添加评论一回•2012-03-18 15:28

 发送易变(volatile)的数据

发送易变(volatile)的数据

volatile 意思大概是说,当服务器发送数据时,客户端因为各种原因不能正常接收,比如网络问题、或者正处于长连接的建立连接阶段。此时会让我们的应用变得 suffer,那就需要考虑发送 volatile 数据。


var io = require('socket.io').listen(80);

io.sockets.on('connection', function (socket) {
  var tweets = setInterval(function () {
    getBieberTweet(function (tweet) {
      socket.volatile.emit('bieber tweet', tweet);
    });
  }, 100);

  socket.on('disconnect', function () {
    clearInterval(tweets);
  });});

按一回的理解,即使客户端没连线,一样可以这样发送,服务器会自动丢弃发送失败的数据。


2 条评论一回•2012-03-19 13:44

 认证与握手

认证与握手

我们可以设置认证。这样客户端在连接 socket.io 服务器时可以做一些安全或者特殊处理。

socket.io 采用 TJ 风格的设置语法,如:


io.configure(function (){
  io.set('authorization', function (handshakeData, callback) {
    callback(null, true); // error first callback style 
  });});

上面的 authorrization 设置项即设置服务端的认证,后面回调函数的第一个参数 handshakeData 就附带了客户端传递过来的数据,比如头数据以及IP地址、请求的 URL 和 参数等,基于这些数据进行验证,如果验证符合你的业务逻辑,则调用 callback(null, true) ,否则将错误放在callback的第一个参数,如 callback('need login');


注,验证失败之后,socket 所注册的各种事件就不会被执行。


io.sockets.on('connection', function (socket) {
    // 。。。。 });


你可能感兴趣的:(nodejs实战案例(Express框架+mongoDB)——(14)——socket通信,聊天功能)