使用Socket.IO仿微信聊天应用
2016-10-20 本文已影响1604人
Zoemings
页面布局的构建
根据网页版的微信,利用flex框架进行重建。手机版根据实际情况将聊天页面隐藏,点击相应用户的聊天室显示相应的聊天页面。
socket.io
基本原理
- 是一个开源的WebSocket库,它通过Node.js实现WebSocket服务端,同时也提供客户端JS库。
- 原理是基于事件的推送实现双向通讯。对于数据的处理就是一个个的事件触发。
基本语法
// 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, include sender
io.sockets.emit('hi', 'everyone');
// sending to individual socketid
io.sockets.socket(socketId).emit('message', 'for your eyes only');
服务端代码实现
var server = http.createServer(app);
//// 在线用户名单
var onlineList = [];
// 创建socket服务
var io = require('socket.io').listen(server);
io.on('connection', function(socket) {
var usr;
//加入聊天室
socket.on('online', function(uid) {
//用户加入
usr = uid;
socket.join(usr);
//把当前用户加入在线用户名单中
if ( onlineList.indexOf(uid) === -1) {
onlineList.push(uid);
}
console.log(usr + ' online...');
});
// 处理用户消息
socket.on('message', function (uid,fid,msg) {
var type;
// 判断接收方是否在线
if (onlineList.indexOf(fid) === -1) {
//不在线将消息保存到数据库
type = config.site.OFFLINE;//OFFLINE是1
io.to(uid).emit('msg', uid, fid, msg);//user
}else{
//接收方在线,将消息发送给对方
type = config.site.ONLINE;
io.to(fid).emit('msg', uid, fid, msg);//(_id,fid,msg);
io.to(uid).emit('msg', uid, fid, msg);
}
var data = {
"uid":uid,
"from":uid,
"to":fid,
"type":type,
"msg":msg
};
console.log(data);
//数据库存入
dbHelper.addMsg(data, function (success, data) {
console.log(uid + ' -> ' + fid + 'msg saved...');
})
});
//非正常下线
socket.on('disconnect', function() {
// 从在线名单中移除
var index = onlineList.indexOf(usr);
if (index !== -1) {
onlineList.splice(index,1);
socket.leave(usr);
}
console.log(usr + ' offline...');
});
//正常下线
socket.on('leave', function() {
socket.emit('disconnect');
});
});
客户端代码
<section class="chat">
<nav>
//显示当前用户的基本消息
<div class="header">
<img class="avatar" src="{{user.userThumb}}">
<h3 class="nickname">
{{user.username}}
</h3>
</div>
//搜索用户
<div class="search_bar">
<i class="wechat_search"></i>
<input class="frm_search" type="text" placeholder="搜索">
</div>
//功能选择
<ul id="myTab" class="nav nav-tabs tab">
<li class="active">
<a class="" title="好友列表" href="#chat" data-toggle="tab">
<i class="web_wechat_chat"></i>
</a>
</li>
<li>
<a class="" title="传输文件" href="#read" data-toggle="tab">
<i class="web_wechat_public" ></i>
</a>
</li>
<li>
<a class="" title="用户列表" href="#user" data-toggle="tab">
<i class="web_wechat_friends" ></i>
</a>
</li>
</ul>
//好友列表
<div id="" class="chats tab-content">
<div class="tab-pane fade in active" id="chat">
<div class="wrapper-content">
<section class="list">
</section>
</div>
</div>
<div class="tab-pane fade" id="read">
</div>
//获取数据库用户列表
<div class="tab-pane fade" id="user" data-button="addFrd">
{{#each entries}}
<p data-toggle="select" data-id="{{_id}}" class="active chat_item">
<img class="avatar" src="{{userThumb}}">
<div class="info">
<span class="nickname_text">{{username}}</span>
</div>
<div class="ext">
<a href='#' data-id="{{_id}}" class='addFriendBtn'>添加</a>
</div>
{{/each}}
</div>
</div>
</nav>
//聊天区域
<div id="chatArea" data-id="">
<div class="title_wrap">
<a class="friend" data-id=""></a>
<div class="message-content">
<div class="box-content">
hello
</div>
</div>
<form action="">
<div class="box_ft box-send">
<div class="toolbar">
<a class="web_wechat_face" title="表情"></a>
<a class="web_wechat_screencut" href="" title="截屏"></a>
<a class="web_wechat_pic" title="图片和文件"></a>
</div>
<div class="content">
<input class="chat-input input-ctn" id="editArea">
</div>
<div class="action">
<span class="desc ng-scope">按下enter发送</span>
</div>
</div>
</form>
</div>
</div>
</section>
<script src="/chat/chat.js"></script>
<script src="/chat/common.js"></script>
<script src="/lib/socket.io/socket.io.js"></script>
上面的html内容本身没有什么好说的,我们主要看看里面的4个文件请求
1./lib/socket.io/socket.io.js
2.common.js
3.chat.js
第1个JS是Socket.IO提供的客户端JS文件,当npm安装完socket.io并搭建起WebServer后,这个JS文件就可以正常访问了
第2个common.js
主要提供一些格式处理,路径获取,消息提示这里就不再赘述
第3个chat.js
是完整的客户端的业务逻辑实现代码,它的内容如下:
$(init);
function init() {
initSocket();
initFriendList();
$('body').on('click', '.addFriendBtn' , doAddFreind);
$('body').on('click', '.wrapper-content .list li' , doChatSession);
$('body').on('keydown', '#editArea' , doSend);
}
//初始化socketio
function initSocket() {
socket = io();
socket.emit('online', _id);
// 监听消息
socket.on('msg', function(uid, fid, msg) {
var fromID = (_id == fid)?uid:fid;
var message;
//当前用户如果是好友的话显示在发送方
if (_id == fid) {
console.log(uid+"发送信息给"+fid );
friendThumb = $('#'+uid).children('img').attr('src');
//前端显示接收信息
var html = '<li class="message you"><img class="avatar" src="friendThumb"><p class="you">' +msg +'</p></li>';
$('#messages').append(html);
} else {
//前端显示发送消息
var html = '<li class="message me"><img class="avatar" src="$.cookie('userThumb')><p class="you">' +msg +'</p></li>';
$('#messages').append(html);
console.log("from");
}
});
}
//计算sessionid将较大id放置前面小的加后面,这样两个人聊天的sessionid就是唯一的
function calcuSessionId(uid,fid) {
return (uid>fid)?(uid+fid):(fid+uid);
}
//新建会话过程
function doChatSession() {
fid = $(this).attr("id");
//得到sessionid
var sessionId = calcuSessionId(_id,fid);
console.log(sessionId);
//第一次点击的话,发送初始化session消息
if (chatSession.indexOf(sessionId) === -1) {
chatSession.push(sessionId);
socket.emit('join', sessionId);
}
//切换界面
toggleChatView(fid);
}
//切换聊天窗口
function toggleChatView(fid) {
if ($("#message"+fid).length == 0) {
$(".title_wrap .message-content").prepend('<div class="box-content" id="message'+fid+'"></div>');
}
$(".box-content").hide();
$("#message"+fid).show();
}
//发送消息给朋友
function doSend(e) {
if (e.which === 13) {//获取enter键
e.preventDefault();
var msg = $(this).val();
console.log(msg);
$(this).val('');
// 发送消息
socket.send(_id,fid,msg);
}
}
最近入的坑
- format的constructor说undefined,通常是自己传进的参数与定义的的不一致,该函数本身是没错的
- 加入
session
支持,认证机制必须依赖cookie
,所以还应该同时安装一个cookie-parser
,然后再app.js
中导入这两个中间件,
定义cookie解析器,注意,该定义必须写在路由分配之前. - 用户头像的显示
在route文件夹下添加user: req.session.user
,这样相应页面就可以调用user内的数据,通过{{user.userThumb}}
和`{{user.username}}显示用户名及头像