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源創計劃”,歡迎正在閱讀的你也加入,一起分享。