http轮询渲染聊天信息
express-ws库的基本使用
Nginx配置wss
express-ws配合轮询实现聊天信息渲染
使用socket-io实现websocket,真正实现实时会话系统
http轮询
客户端实现两个事件:用户信息发送时间和定时器轮询获取用户聊天记录。
服务端相对应的应该有两个API:用户聊天信息保存API以及返回两个用户之间的所有聊天记录。
客户端通过定时器定时调用查询聊天信息API,然后每次取到数据重新渲染聊天界面。
轮询方法实际上很简单,但是为什么我们一般不会推荐使用http轮询实现实时会话系统呢?因为轮询的缺点是显而易见的,举个很简单的例子:我们在客户端开了个1秒/次的定时器,每秒钟向服务端请求聊天数据,但是这大部分的请求可能都是没有新消息发送与接收的,所以说轮询最大的缺点就是定时器定时请求大部分都是无用的,所以会极大的浪费和消耗带宽和服务器资源。接下来我们可以来实战看下效果,首先我们看下客户端会话界面的效果:输入框可以输入聊天信息点击发送可以发送文本信息,点击+可以选择相册图片发送。我们可以简单看下客户端界面渲染代码,实际上就是通过for循环渲染聊天数据,将好友的信息渲染在左边,自己发送的信息渲染在右边:
<view class="news" bindtap='outbtn'>
<view class="historycon">
<scroll-view scroll-y="true" class="history" scroll-top="{{scrollTop}}">
<block wx:for="{{newslist}}" wx:key>
<view wx:if="{{item.flagtime}}" class="created_date">{{item.created_date}}</view>
<!--自己的消息 -->
<view class="chat-news" wx:if="{{item.username == chatInfo.userInfo.username}}">
<view style="text-align: right;padding-right: 20rpx;">
<text class="name">{{ item.username }}</text>
<image class='new_img' src="{{item.useravatar}}"></image>
</view>
<view class='my_right'>
<block wx:if="{{item.chat_type==0}}">
<view class='new_txt'>{{item.content}}</view>
</block>
<block wx:if="{{item.chat_type==1}}">
<image class="selectImg" src="{{item.content}}" data-src="{{item.content}}" lazy-load="true" bindtap="previewImg"></image>
</block>
</view>
</view>
<!-- 别人的消息 -->
<view class="chat-news" wx:else>
<view style="text-align: left;padding-left: 20rpx;">
<image class='new_img' src="{{item.useravatar? item.useravatar:'/images/defule.png'}}"></image>
<text class="name">{{ item.username }}</text>
</view>
<view class='you_left'>
<block wx:if="{{item.chat_type==0}}">
<view class='new_txt'>{{item.content}}</view>
</block>
<block wx:if="{{item.chat_type==1}}">
<image class="selectImg" src="{{item.content}}" data-src="{{item.content}}" lazy-load="true" bindtap="previewImg"></image>
</block>
</view>
</view>
</block>
</scroll-view>
</view>
</view>
<view id="flag"></view>
<!-- 聊天输入 -->
<view class="message">
<view wx:if="{{loading}}">
<i-load-more tip="{{tip}}" loading="{{loading}}" />
</view>
<i-toast id="toast" />
<form bindreset="cleanInput" class="sendMessage">
<input type="text" placeholder="请输入聊天内容.." value="{{massage}}" bindinput='bindChange' disabled="{{loading}}"></input>
<view class="add" bindtap='increase'>+</view>
<button type="primary" bindtap='send' form-type="reset" size="small" button-hover="blue" disabled="{{loading}}">发送</button>
</form>
<view class='increased {{aniStyle?"slideup":"slidedown"}}' wx:if="{{increase}}">
<view class="image" bindtap='chooseImage'>相册 </view>
</view>
</view>
刚才已经说过了这个界面同时有一个我轮询事件,我这里5秒进行一次轮询,获取用户最新聊天记录然后重新渲染页面,我们看下代码:
getIntervalChat: function (that) {
interval = setInterval(function () {
wx.request({
url: utils.basePath + '/article/v1/getOnlineInfo',
method: "post",
data: {
chatInfo: that.data.chatInfo
},
header: {
'content-type': 'application/json'
},
success(res) {
if (res.data.status == 200) {
var data = res.data.payload.data;
data[0].flagtime = true;
for (var i = 1; i < data.length; i++) {
var currenttime = new Date(data[i].created_date).getTime();
var begintime = new Date(data[i - 1].created_date).getTime();
currenttime - begintime > 1000 * 60 ? data[i].flagtime = true : data[i].flagtime = false;
}
that.setData({
onlineInfo: data
});
}
}
});
}, 5000);
},
async.waterfall([
function (callback) {
connection.beginTransaction(function (err) {
return callback(err);
});
},
function (callback) {
var sql = 'insert into online_chat set ?';
var value = {
friendphone: data.friendInfo.account,
friendname: data.friendInfo.username,
friendavatar: data.friendInfo.avatar,
app_sid: data.friendInfo.app_sid,
userphone: data.userInfo.account,
username: data.userInfo.username,
useravatar: data.userInfo.avatar,
created_date: new Date(),
status: 1,
content: data.chat_content,
chat_type: data.chat_type
};
connection.query(sql, value, function (err, result) {
if (err) {
return callback(err);
}
if (result.affectedRows == 0) {
return callback('聊天出现故障!');
}
return callback(null, '保存聊天记录成功!');
});
},
function (release_info, callback) {
var sql = 'select id, friendphone, friendname, friendavatar, app_sid, DATE_FORMAT(created_date, "%Y-%m-%d %H:%i:%s") as created_date, userphone, username, useravatar, content, chat_type from online_chat ' +
'where (friendphone = ? and userphone = ?) or (friendphone = ? and userphone = ?)';
var value = [data.friendInfo.account, data.userInfo.account, data.userInfo.account, data.friendInfo.account];
connection.query(sql, value, function (err, result) {
if (err) {
return callback(err);
}
var del_info = result && result.length > 0 ? result : null;
if (!del_info) {
return callback(null, true, []);
}
return callback(null, true, del_info);
});
}
], function (DbErr, isSuccess, uidOrInfo) {
if (DbErr || !isSuccess) {
connection.rollback(function () {
connection.release();
});
return cb(DbErr);
}
connection.commit(function (e) {
if (e) {
connection.rollback(function () {
connection.release();
});
return cb(e);
}
connection.release();
cb(null, uidOrInfo);
});
});
接下来看下聊天数据获取API,这个API实际上就是查询两个好友间的聊天记录,然后通过两个账号分别查询用户的基本信息如头像暱称等,一样贴下关键代码:
async.waterfall([
function (callback) {
connection.beginTransaction(function (err) {
return callback(err);
});
},
//通过friendphone查询好友信息
function (callback) {
var sql = 'select username, avatar from users where account = ? and app_sid = ?';
var value = [data.friendphone, data.app_sid];
connection.query(sql, value, function (err, result) {
if (err) {
return callback(err);
}
if (!result[0]) {
return callback('用户不存在!');
}
data.friendname = result[0].username;
data.friendavatar = result[0].avatar;
return callback(null, 200);
});
},
//通过userphone查询好友信息
function (info, callback) {
var sql = 'select username, avatar from users where account = ? and app_sid = ?';
var value = [data.userphone, data.app_sid];
connection.query(sql, value, function (err, result) {
if (err) {
return callback(err);
}
if (!result[0]) {
return callback('用户不存在!');
}
data.username = result[0].username;
data.useravatar = result[0].avatar;
return callback(null, 200);
});
},
function (release_info, callback) {
var sql = 'select id, friendphone, friendname, friendavatar, app_sid, DATE_FORMAT(created_date, "%Y-%m-%d %H:%i:%s") as created_date, userphone, username, useravatar, content, chat_type from online_chat ' +
'where (friendphone = ? and userphone = ?) or (friendphone = ? and userphone = ?)';
var value = [data.friendphone, data.userphone, data.userphone, data.friendphone];
connection.query(sql, value, function (err, result) {
if (err) {
return callback(err);
}
var del_info = result && result.length > 0 ? result : null;
if (!del_info) {
return callback(null, true, []);
}
return callback(null, true, del_info);
});
}
], function (DbErr, isSuccess, uidOrInfo) {
if (DbErr || !isSuccess) {
connection.rollback(function () {
connection.release();
});
return cb(DbErr);
}
connection.commit(function (e) {
if (e) {
connection.rollback(function () {
connection.release();
});
return cb(e);
}
connection.release();
cb(null, uidOrInfo);
});
});
async.waterfall([
function (callback) {
connection.beginTransaction(function (err) {
return callback(err);
});
},
//获取access_token
function (callback) {
postHelper.baseRequest(CONFIG.USERDEFINEPATH, {
grant_type: CONFIG.USERDEFINEGRANTTYPE,
client_id: CONFIG.USERDEFINEKEY,
client_secret: CONFIG.USERDEFINESECRET
}, function(err, result) {
if(err) {
return callback(err);
}
return callback(null, JSON.parse(result).access_token);
});
},
//图像审核
function (access_token, callback) {
postHelper.baseRequestbdai(CONFIG.CENSORINGPATH + access_token, {
imgUrl: data,
imgType: CONFIG.CENSORINGIMGTYPE
}, function(err, result) {
if(err) {
return callback(err);
}
if(result.error_code) {
return callback(result.error_msg);
}
if(result.conclusionType != 1) {
return callback('图片内容不合法,请重新上传吧!');
}
return callback(null, true, result);
});
}
], function (DbErr, isSuccess, uidOrInfo) {
if (DbErr || !isSuccess) {
connection.rollback(function () {
connection.release();
});
return cb(DbErr);
}
connection.commit(function (e) {
if (e) {
connection.rollback(function () {
connection.release();
});
return cb(e);
}
connection.release();
return cb(null, uidOrInfo);
});
});
到这里我们前后端都成功实现,我们就可以来测试下会话系统。我在模拟器发送测试实时聊天系统,然后在手机真机测试看看能不能通过轮询获取:
https://gitee.com/mqzuimeng_admin/wx_blog.git
本文分享自微信公众号 - 程序猿周先森(zhanyue_org)。
如有侵权,请联系 [email protected] 删除。
本文参与“OSC源创计划”,欢迎正在阅读的你也加入,一起分享。