Node.js 切近实战(十一) 之实时通讯
2019獨角獸企業重金招聘Python工程師標準>>>
今天我們主要看一下Socket.IO實時通訊,先看一下界面。
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 | .row ?.col-md-9 ??.panel.panel-primary ???.panel-heading ????h3.panel-title(style='font-size:13px;')?Chat?Message ???.panel-body#div_msgbody(style='min-height:590px;max-height:590px;overflow:auto;max-width:750px;') ????#div_msg.panel-content(style='word-wrap:break-word;word-break:?break-word;') ???.panel-footer ????#div_footer(style='height:36px;line-height:36px')???? ?????.row ??????.col-md-8(style='color:#3f51b5;font-weight:bold')?Chat?History: ????????input#chat_history ??????.col-md-4.right-align-text? ???????a#link_clear(href='javascript:void(0)')?Clear ??.row ???.col-md-10 ????input#txt_msg.form-control(type='text'?style='height:40px;resize:none'?maxlength=200?placeholder='Input?message?here.') ???.col-md-2.right-align-text ????button#btn_send.k-button.k-primary(type='button'?style='height:40px;width:100%') ?????span.glyphicon.glyphicon-send ?????span(style='margin-left:5px')?Send ?.col-md-3 ??.panel.panel-primary ???.panel-heading ????h3.panel-title(style='font-size:13px;')?Members ???.panel-body.panel-inner-height(style='overflow:auto;') ????#div_users.panel-content(style='word-break:?break-word;') ???.panel-footer ????.left-margin-10? ?????span.text-color#total?Member?Count:0 ? #chat_historyWindow(style='display:none') ??#chat_historyContent.panel-content(style='word-wrap:break-word;word-break:?break-word;') span#notify ? ? block?scripts ?script(type='text/javascript'?src='/javascripts/local/other/chat.js') |
?
這就是聊天界面,左邊是聊天內容,右邊是參加聊天的用戶。要實現這個聊天,之前我們在博客中提到了SingalR,可以用于ASP.NET,WinForm以及WPF。今天我們要使用Node.js平臺上的Socket.IO.js。首先要在項目中引用這個擴展包。
安裝好之后,在Package.json中就會自動加入這個包,管理起來。
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 | "dependencies":?{ ????"array-splice":?"^0.1.1", ????"body-parser":?"~1.8.4", ????"busboy":?"^0.2.12", ????"cassandra-driver":?"^3.0.0", ????"cookie-parser":?"~1.3.3", ????"debug":?"~2.0.0", ????"express":?"~4.9.8", ????"express-session":?"1.12.1", ????"gridfs-stream":?"^1.1.1", ????"jade":?"^1.11.0", ????"log4js":?"^0.6.29", ????"mongoose":?"~4.2.3", ????"morgan":?"^1.6.1", ????"request":?"^2.67.0", ????"serve-favicon":?"~2.1.3", ????"socket.io":?"^1.3.7", ????"string.prototype.endswith":?"^0.2.0", ????"string.prototype.startswith":?"^0.2.0" ??} |
我在這里還使用的是老版本,哈哈,OK,老版本新版本都能用。我們進入主題,在第一篇環境搭建中,我就說了我們的啟動入口是www文件。
在www文件中,我們初始化了SocketIO的一些東西。
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 | var?chatUserCount?=?0; var?chatUsers?=?{}; var?server?=?app.listen(app.get('port'),?function()?{ ????debug('Express?server?listening?on?port?'?+?server.address().port); }); ? var?io?=?require('socket.io')(server); ? io.on('connection',?function?(socket)?{ ????socket.on('joinchat',?function?(obj)?{ ????????console.log('a?user?connected:'+obj.UserName); ????????socket.name?=?obj.UserName; ????????if?(!chatUsers.hasOwnProperty(obj.UserName))?{ ????????????chatUsers[obj.UserName]?=?obj; ????????????chatUserCount++; ????????} ? ????io.emit('joinchat',?{?chatUsers:?chatUsers?,chatUserCount:?chatUserCount,joinedUser:?obj}); ????}); ????? ????socket.on('leftchat',?function?()?{ ????????????console.log('a?user?left'); ????????????if?(chatUsers.hasOwnProperty(socket.name))?{ ????????????????var?obj?=?chatUsers[socket.name]; ????????????????delete?chatUsers[socket.name]; ????????????????chatUserCount--; ????????? ????????????????io.emit('leftchat',?{?chatUsers:?chatUsers,?chatUserCount:?chatUserCount,?leftUser:?obj?}); ????????????} ????????}); ? ????socket.on('disconnect',?function?()?{ ?????????if?(chatUsers.hasOwnProperty(socket.name))?{ ????????????var?obj?=?chatUsers[socket.name]; ????????????delete?chatUsers[socket.name]; ????????????chatUserCount--; ? ????????????io.emit('leftchat',?{?chatUsers:?chatUsers,?chatUserCount:?chatUserCount,?leftUser:?obj?}); ????????} ????}); ? ????socket.on('message',?function?(obj)?{ ????????io.emit('message',?obj); ????}); ? ????socket.on('error',?function(exception)?{ ????console.log('SOCKET?ERROR'); ????socket.destroy(); ????}); }); |
在這里當客戶端有用戶進入聊天時,就會發射joinchat事件,后臺就會觸發joinchat事件。當客戶端和服務端建立連接時,服務端就會發射廣播joinchat,所有連接的客戶端都會收到這個廣播,悄無聲息刷新界面。當客戶端用戶失去連接(關閉瀏覽器)時,就會自動發射disconnect事件,服務端就會觸發disconnect事件,并將結果廣播到各個客戶端,客戶端自動刷新頁面。當用戶unload該頁面時,會觸發leftchat。當客戶端發信息時,就會觸發message事件,將該用戶的消息發送到其他人。這個聊天界面的過程就是這樣,很簡單。
接下來我們來看一下客戶端代碼。
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 | var?popupNotification?=?$("#notify").kendoNotification({ ????autoHideAfter:?2000, ????height:?60, ????stacking:?"down" }).data("kendoNotification"); ? var?socket?=?io(); var?loginUser?=?sessionStorage.getItem("LoginUser"); if?(loginUser?==?null)?{ ??window.location.href?=?"/"; ??return; } var?userObj?=?eval("("?+?loginUser?+?")"); ????? sessionStorage.removeItem('chatUser'); socket.emit("joinchat",?userObj); socket.on('joinchat',?function?(data)?{ ????if?(!sessionStorage.getItem('chatUser'))?{ ????????sessionStorage.setItem('chatUser',?JSON.stringify({?"user":?[]?})); ????} ????? ????var?usersObj?=?JSON.parse(sessionStorage.getItem('chatUser')); ????if?(usersObj.user.indexOf(data.joinedUser.UserID)?==?-1)?{ ????????usersObj.user.push(data.joinedUser.UserID); ????????sessionStorage.setItem('chatUser',?JSON.stringify(usersObj)); ????????setchartdetail(data); ????????? ????????if?(data.joinedUser.UserID?!=?userObj.UserID)?{ ????????????popupNotification.show('<span?style="color:red">'?+?data.joinedUser.FullName?+?'?joined?in.</span>',?'info'); ????????} ????} }); ? socket.on('leftchat',?function?(data)?{ ????var?usersObj?=?JSON.parse(sessionStorage.getItem('chatUser')); ????var?index?=?usersObj.user.indexOf(data.leftUser.UserID); ????? ????usersObj.user.splice(index,?1); ????sessionStorage.setItem('chatUser',?JSON.stringify(usersObj)); ????setchartdetail(data); ????popupNotification.show('<span?style="color:red">'?+?data.leftUser.FullName?+?'?left.</span>',?'warning'); }); |
當用戶進入這個頁面時,我們發射joinchat,并將當前登錄用戶信息發送到服務端,服務端再將該用戶信息以及計算好的用戶總數廣播到各個客戶端。注意這里我們為了提醒用戶,用到了kendoNotification,效果如下,當有人進入或者離開時,會出現popup提示。
當用戶離開時,如上,當用戶進入時,如下
OK,接下來我們看一下最主要的部分,聊天。
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 | $("#btn_send").click(function?()?{ ????sendmsg(); }) ? $("#txt_msg").keydown(function?(e)?{ ????if?(e.keyCode?==?13)?{ ????????sendmsg(); ????} }); ? function?sendmsg()?{ ????var?msg?=?$.trim($("#txt_msg").val()); ????if?(msg)?{ ????????var?msgObj?=?{?user:?userObj,?msg:?msg?}; ????????socket.emit("message",?msgObj); ????} ????? ????$("#txt_msg").val(""); } |
上面就是點擊SEND按鈕或者文本框回車發送消息的代碼,后臺接到message廣播給客戶端。
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 | socket.on('message',?function?(data)?{ ????var?msgObj?=?data; ????? ????var?userAvatar?=?'/images/userlogin.png'; ????? ????if?(msgObj.user.UserName?==?userObj.UserName)?{ ????????$("#div_msg").append("<div?class='row-margin'>"? ????????+?"<img?src='"?+?userAvatar?+?"'?style='height:40px;width:40px;right:-660px;position:relative'/>"? ????????+?"<span?style='right:-530px;position:relative'>"?+?msgObj.user.FullName?+?"</span>"? ????????+?"<div?class='demo?clearfix?fr'>"? ????????+?"<span?class='triangle'></span>"? ????????+?"<div?class='article'?style='word'>"?+?msgObj.msg? ????????+?"</div></div></div>"); ????????? ????????db.transaction(function?(tx)?{ ????????????tx.executeSql('INSERT?INTO?ChatRecords(userId,sendUserId,fullname,content)?VALUES("'?+?userObj.UserID?+?'","'?+?msgObj.user.UserID?+?'","'?+?msgObj.user.FullName?+?'","'?+?msgObj.msg?+?'")'); ????????}); ????} ????else?{ ????????$("#div_msg").append("<div?class='row-margin'>"? ????????+?"<img?src='"?+?userAvatar?+?"'?style='height:40px;width:40px;position:relative'/>"? ????????+?"<span?style='left:10px;position:relative'>"?+?msgObj.user.FullName?+?"</span>"? ????????+?"<div?class='demo?clearfix'>"? ????????+?"<span?class='triangle'></span>"? ????????+?"<div?class='article'>"?+?msgObj.msg? ????????+?"</div></div></div>"); ????????? ????????db.transaction(function?(tx)?{ ????????????tx.executeSql('INSERT?INTO?ChatRecords(userId,sendUserId,fullname,content)?VALUES("'?+?userObj.UserID?+?'","'?+?msgObj.user.UserID?+?'","'?+?msgObj.user.FullName?+?'","'?+?msgObj.msg?+?'")'); ????????}); ????} ????? ????var?objDiv?=?document.getElementById("div_msgbody"); ????objDiv.scrollTop?=?objDiv.scrollHeight; }); |
其實這里不過是一個拼message的過程,如果發送者是本人,則消息靠右顯示,否則靠左顯示。看看James和lilei的聊天。
看到了吧,聊天聊的很Happy。OK,上面大家是不是看到了一段類似sql的代碼,不錯,就是將聊天信息存到本地WebSQL sqlite數據庫。
| 1 2 3 | db.transaction(function?(tx)?{ ????????????tx.executeSql('INSERT?INTO?ChatRecords(userId,sendUserId,fullname,content)?VALUES("'?+?userObj.UserID?+?'","'?+?msgObj.user.UserID?+?'","'?+?msgObj.user.FullName?+?'","'?+?msgObj.msg?+?'")'); ????????}); |
在使用之前我們需要首先連接數據庫創建表。
| 1 2 3 4 | var?db?=?openDatabase('ChatHistory',?'2.0',?'chat?records',?10?*?1024?*?1024); db.transaction(function?(tx)?{ ????tx.executeSql('CREATE?TABLE?IF?NOT?EXISTS?ChatRecords?(id?INTEGER?PRIMARY?KEY?AUTOINCREMENT,userId?TEXT?NOT?NULL?DEFAULT?"",sendUserId?TEXT?NOT?NULL?DEFAULT?"",fullname?TEXT?NOT?NULL?DEFAULT?"",content?TEXT?NOT?NULL?DEFAULT?"",indate?DATETIME?default?CURRENT_TIMESTAMP)'); }); |
確實和sqlSever的語法有點像,我們看一下存儲到本地webSQL的聊天記錄,google Chrome,按F12
看到了吧,聊天記錄已經被存儲下來,由于我是一臺機器,一個瀏覽器開兩個tab頁,所以這里的聊天記錄就是兩份,一個是發送人的,一個是接收人的。大家注意這里還有張表,sqlite_sequence,我們的主鍵id定義為自增列,所以這張表存儲的是我們的自增列(id)的最大值。
最大是54,和我們表ChatRecords中的最大值相等。
?
結束語
?
免費學習更多精品課程,登錄樂搏學院官網http://h.learnbo.cn/
或關注我們的官方微博微信,還有更多驚喜哦~
?
本文出自 “技術創造價值” 博客,請務必保留此出處http://leelei.blog.51cto.com/856755/1825565
轉載于:https://my.oschina.net/learnbo/blog/776482
總結
以上是生活随笔為你收集整理的Node.js 切近实战(十一) 之实时通讯的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 笔记(用Python做些事情)--变量(
- 下一篇: 测试版本迭代管理机制