现在我们来实现用户聊天在线聊天功能,这里我们需要使用到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);
}
});
});
});
};
效果如下:
这里看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) { // 。。。。 });