自拍偷在线精品自拍偷,亚洲欧美中文日韩v在线观看不卡

使用Node.js+Socket.IO搭建WebSocket實時應用

移動開發(fā) Android
Web領(lǐng)域的實時推送技術(shù),也被稱作Realtime技術(shù)。這種技術(shù)要達到的目的是讓用戶不需要刷新瀏覽器就可以獲得實時更新。它有著廣泛的應用場景,比如在線聊天室、在線客服系統(tǒng)、評論系統(tǒng)、WebIM等。本文就讓我們來看看如何搭建一個Web實時應用。

Web領(lǐng)域的實時推送技術(shù),也被稱作Realtime技術(shù)。這種技術(shù)要達到的目的是讓用戶不需要刷新瀏覽器就可以獲得實時更新。它有著廣泛的應用場景,比如在線聊天室、在線客服系統(tǒng)、評論系統(tǒng)、WebIM等。

WebSocket簡介

談到Web實時推送,就不得不說WebSocket。在WebSocket出現(xiàn)之前,很多網(wǎng)站為了實現(xiàn)實時推送技術(shù),通常采用的方案是輪詢 (Polling)和Comet技術(shù),Comet又可細分為兩種實現(xiàn)方式,一種是長輪詢機制,一種稱為流技術(shù),這兩種方式實際上是對輪詢技術(shù)的改進,這些 方案帶來很明顯的缺點,需要由瀏覽器對服務器發(fā)出HTTP request,大量消耗服務器帶寬和資源。面對這種狀況,HTML5定義了WebSocket協(xié)議,能更好的節(jié)省服務器資源和帶寬并實現(xiàn)真正意義上的實 時推送。

WebSocket協(xié)議本質(zhì)上是一個基于TCP的協(xié)議,它由通信協(xié)議和編程API組成,WebSocket能夠在瀏覽器和服務器之間建立雙向連接, 以基于事件的方式,賦予瀏覽器實時通信能力。既然是雙向通信,就意味著服務器端和客戶端可以同時發(fā)送并響應請求,而不再像HTTP的請求和響應。

為了建立一個WebSocket連接,客戶端瀏覽器首先要向服務器發(fā)起一個HTTP請求,這個請求和通常的HTTP請求不同,包含了一些附加頭信 息,其中附加頭信息”Upgrade: WebSocket”表明這是一個申請協(xié)議升級的HTTP請求,服務器端解析這些附加的頭信息然后產(chǎn)生應答信息返回給客戶端,客戶端和服務器端的 WebSocket連接就建立起來了,雙方就可以通過這個連接通道自由的傳遞信息,并且這個連接會持續(xù)存在直到客戶端或者服務器端的某一方主動的關(guān)閉連 接。

一個典型WebSocket客戶端請求頭:

前面講到WebSocket是HTML5中新增的一種通信協(xié)議,這意味著一部分老版本瀏覽器(主要是IE10以下版本)并不具備這個功能, 通過百度統(tǒng)計的公開數(shù)據(jù)顯示,IE8 目前仍以33%的市場份額占據(jù)榜首,好在chrome瀏覽器市場份額逐年上升,現(xiàn)在以超過26%的市場份額位居第二,同時微軟前不久宣布停止對IE6的技 術(shù)支持并提示用戶更新到新版本瀏覽器,這個曾經(jīng)讓無數(shù)前端工程師為之頭疼的瀏覽器有望退出歷史舞臺,再加上幾乎所有的智能手機瀏覽器都支持HTML5,所 以使得WebSocket的實戰(zhàn)意義大增,但是無論如何,我們實際的項目中,仍然要考慮低版本瀏覽器的兼容方案:在支持WebSocket的瀏覽器中采用 新技術(shù),而在不支持WebSocket的瀏覽器里啟用Comet來接收發(fā)送消息。

WebSocket實戰(zhàn)

本文將以多人在線聊天應用作為實例場景,我們先來確定這個聊天應用的基本需求。

需求分析

1、兼容不支持WebSocket的低版本瀏覽器。
2、允許客戶端有相同的用戶名。
3、進入聊天室后可以看到當前在線的用戶和在線人數(shù)。
4、用戶上線或退出,所有在線的客戶端應該實時更新。
5、用戶發(fā)送消息,所有客戶端實時收取。

在實際的開發(fā)過程中,為了使用WebSocket接口構(gòu)建Web應用,我們首先需要構(gòu)建一個實現(xiàn)了 WebSocket規(guī)范的服務端,服務端的實現(xiàn)不受平臺和開發(fā)語言的限制,只需要遵從WebSocket規(guī)范即可,目前已經(jīng)出現(xiàn)了一些比較成熟的 WebSocket服務端實現(xiàn),比如本文使用的Node.js+Socket.IO。為什么選用這個方案呢?先來簡單介紹下他們兩。

Node.js

Node.js采用C++語言編寫而成,它不是Javascript應用,而是一個Javascript的運行環(huán)境,據(jù)Node.js創(chuàng)始人 Ryan Dahl回憶,他最初希望采用Ruby來寫Node.js,但是后來發(fā)現(xiàn)Ruby虛擬機的性能不能滿足他的要求,后來他嘗試采用V8引擎,所以選擇了 C++語言。

Node.js支持的系統(tǒng)包括*nux、Windows,這意味著程序員可以編寫系統(tǒng)級或者服務器端的Javascript代碼,交給 Node.js來解釋執(zhí)行。Node.js的Web開發(fā)框架Express,可以幫助程序員快速建立web站點,從2009年誕生至今,Node.js的 成長的速度有目共睹,其發(fā)展前景獲得了技術(shù)社區(qū)的充分肯定。

Socket.IO

Socket.IO是一個開源的WebSocket庫,它通過Node.js實現(xiàn)WebSocket服務端,同時也提供客戶端JS庫。Socket.IO支持以事件為基礎的實時雙向通訊,它可以工作在任何平臺、瀏覽器或移動設備。

Socket.IO支持4種協(xié)議:WebSocket、htmlfile、xhr-polling、jsonp-polling,它會自動根據(jù)瀏覽 器選擇適合的通訊方式,從而讓開發(fā)者可以聚焦到功能的實現(xiàn)而不是平臺的兼容性,同時Socket.IO具有不錯的穩(wěn)定性和性能。

編碼實現(xiàn)

本文一開始的的插圖就是效果演示圖:可以點擊這里查看在線演示,整個開發(fā)過程非常簡單,下面簡單記錄了開發(fā)步驟:

安裝Node.js

根據(jù)自己的操作系統(tǒng),去Node.js官網(wǎng)下載安裝即可。如果成功安裝。在命令行輸入node -vnpm -v應該能看到相應的版本號。

node -v  
v0.10.26  
npm -v  
1.4.6  

搭建WebSocket服務端

這個環(huán)節(jié)我們盡可能的考慮真實生產(chǎn)環(huán)境,把WebSocket后端服務搭建成一個線上可以用域名訪問的服務,如果你是在本地開發(fā)環(huán)境,可以換成本地ip地址,或者使用一個虛擬域名指向本地ip。

先進入到你的工作目錄,比如 /workspace/wwwroot/plhwin/realtime.plhwin.com,新建一個名為 package.json的文件,內(nèi)容如下:

  1.   "name""realtime-server"
  2.   "version""0.0.1"
  3.   "description""my first realtime server"
  4.   "dependencies": {} 

接下來使用npm命令安裝expresssocket.io

npm install --save express
npm install --save socket.io

安裝成功后,應該可以看到工作目錄下生成了一個名為node_modules的文件夾,里面分別是expresssocket.io,接下來可以開始編寫服務端的代碼了,新建一個文件:index.js

  1. var app = require('express')(); 
  2. var http = require('http').Server(app); 
  3. var io = require('socket.io')(http); 
  4.  
  5. app.get('/'function(req, res){ 
  6.     res.send('<h1>Welcome Realtime Server</h1>'); 
  7. }); 
  8.  
  9. http.listen(3000, function(){ 
  10.     console.log('listening on *:3000'); 
  11. }); 

命令行運行node index.js,如果一切順利,你應該會看到返回的listening on *:3000字樣,這說明服務已經(jīng)成功搭建了。此時瀏覽器中打開http://localhost:3000應該可以看到正常的歡迎頁面。

如果你想要讓服務運行在線上服務器,并且可以通過域名訪問的話,可以使用Nginx做代理,在nginx.conf中添加如下配置,然后將域名(比如:realtime.plhwin.com)解析到服務器IP即可。

  1. server 
  2.   listen       80; 
  3.   server_name  realtime.plhwin.com; 
  4.   location / { 
  5.     proxy_pass http://127.0.0.1:3000; 
  6.   } 

完成以上步驟,http://realtime.plhwin.com:3000的后端服務就正常搭建了。

服務端代碼實現(xiàn)

前面講到的index.js運行在服務端,之前的代碼只是一個簡單的WebServer歡迎內(nèi)容,讓我們把WebSocket服務端完整的實現(xiàn)代碼加入進去,整個服務端就可以處理客戶端的請求了。完整的index.js代碼如下:

  1. var app = require('express')(); 
  2. var http = require('http').Server(app); 
  3. var io = require('socket.io')(http); 
  4.  
  5. app.get('/'function(req, res){ 
  6.     res.send('<h1>Welcome Realtime Server</h1>'); 
  7. }); 
  8.  
  9. //在線用戶 
  10. var onlineUsers = {}; 
  11. //當前在線人數(shù) 
  12. var onlineCount = 0; 
  13.  
  14. io.on('connection'function(socket){ 
  15.     console.log('a user connected'); 
  16.      
  17.     //監(jiān)聽新用戶加入 
  18.     socket.on('login'function(obj){ 
  19.         //將新加入用戶的唯一標識當作socket的名稱,后面退出的時候會用到 
  20.         socket.name = obj.userid; 
  21.          
  22.         //檢查在線列表,如果不在里面就加入 
  23.         if(!onlineUsers.hasOwnProperty(obj.userid)) { 
  24.             onlineUsers[obj.userid] = obj.username; 
  25.             //在線人數(shù)+1 
  26.             onlineCount++; 
  27.         } 
  28.          
  29.         //向所有客戶端廣播用戶加入 
  30.         io.emit('login', {onlineUsers:onlineUsers, onlineCount:onlineCount, user:obj}); 
  31.         console.log(obj.username+'加入了聊天室'); 
  32.     }); 
  33.      
  34.     //監(jiān)聽用戶退出 
  35.     socket.on('disconnect'function(){ 
  36.         //將退出的用戶從在線列表中刪除 
  37.         if(onlineUsers.hasOwnProperty(socket.name)) { 
  38.             //退出用戶的信息 
  39.             var obj = {userid:socket.name, username:onlineUsers[socket.name]}; 
  40.              
  41.             //刪除 
  42.             delete onlineUsers[socket.name]; 
  43.             //在線人數(shù)-1 
  44.             onlineCount--; 
  45.              
  46.             //向所有客戶端廣播用戶退出 
  47.             io.emit('logout', {onlineUsers:onlineUsers, onlineCount:onlineCount, user:obj}); 
  48.             console.log(obj.username+'退出了聊天室'); 
  49.         } 
  50.     }); 
  51.      
  52.     //監(jiān)聽用戶發(fā)布聊天內(nèi)容 
  53.     socket.on('message'function(obj){ 
  54.         //向所有客戶端廣播發(fā)布的消息 
  55.         io.emit('message', obj); 
  56.         console.log(obj.username+'說:'+obj.content); 
  57.     }); 
  58.    
  59. }); 
  60.  
  61. http.listen(3000, function(){ 
  62.     console.log('listening on *:3000'); 
  63. }); 

客戶端代碼實現(xiàn)

進入客戶端工作目錄/workspace/wwwroot/plhwin/demo.plhwin.com/chat,新建一個index.html:

  1. <!DOCTYPE html> 
  2. <html> 
  3.     <head> 
  4.         <meta charset="utf-8"> 
  5.         <meta name="format-detection" content="telephone=no"/> 
  6.         <meta name="format-detection" content="email=no"/> 
  7. <meta content="width=device-width, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0, user-scalable=0" name="viewport"> 
  8.         <title>多人聊天室</title> 
  9.         <link rel="stylesheet" type="text/css" href="./style.css" /> 
  10.         <!--[if lt IE 8]><script src="./json3.min.js"></script><![endif]--> 
  11.         <script src="http://realtime.plhwin.com:3000/socket.io/socket.io.js"></script> 
  12.     </head> 
  13.     <body> 
  14.         <div id="loginbox"> 
  15.             <div style="width:260px;margin:200px auto;"> 
  16.                 請先輸入你在聊天室的昵稱 
  17.                 <br/> 
  18.                 <br/> 
  19.                 <input type="text" style="width:180px;" placeholder="請輸入用戶名" id="username" name="username" /> 
  20.                 <input type="button" style="width:50px;" value="提交" onclick="CHAT.usernameSubmit();"/> 
  21.             </div> 
  22.         </div> 
  23.         <div id="chatbox" style="display:none;"> 
  24.             <div style="background:#3d3d3d;height: 28px; width: 100%;font-size:12px;"> 
  25.                 <div style="line-height: 28px;color:#fff;"> 
  26.                     <span style="text-align:left;margin-left:10px;">Websocket多人聊天室</span> 
  27.                     <span style="float:right; margin-right:10px;"><span id="showusername"></span> |  
  28.                     <a href="javascript:;" onclick="CHAT.logout()" style="color:#fff;">退出</a></span> 
  29.                 </div> 
  30.             </div> 
  31.             <div id="doc"> 
  32.                 <div id="chat"> 
  33.                     <div id="message" class="message"> 
  34. <div id="onlinecount" style="background:#EFEFF4; font-size:12px; margin-top:10px; margin-left:10px; color:#666;"> 
  35. </div> 
  36.                     </div> 
  37.                     <div class="input-box"> 
  38.                         <div class="input"> 
  39. <input type="text" maxlength="140" placeholder="請輸入聊天內(nèi)容,按Ctrl提交" id="content" name="content"> 
  40.                         </div> 
  41.                         <div class="action"> 
  42.                             <button type="button" id="mjr_send" onclick="CHAT.submit();">提交</button> 
  43.                         </div> 
  44.                     </div> 
  45.                 </div> 
  46.             </div> 
  47.         </div> 
  48.         <script type="text/javascript" src="./client.js"></script> 
  49.     </body> 
  50. </html> 

上面的html內(nèi)容本身沒有什么好說的,我們主要看看里面的4個文件請求:

1、realtime.plhwin.com:3000/socket.io/socket.io.js

2、style.css

3、json3.min.js

4、client.js

第1個JS是Socket.IO提供的客戶端JS文件,在前面安裝服務端的步驟中,當npm安裝完socket.io并搭建起WebServer后,這個JS文件就可以正常訪問了。

第2個style.css文件沒什么好說的,就是樣式文件而已。

第3個JS只在IE8以下版本的IE瀏覽器中加載,目的是讓這些低版本的IE瀏覽器也能處理json,這是一個開源的JS,詳見:http://bestiejs.github.io/json3/

第4個client.js是完整的客戶端的業(yè)務邏輯實現(xiàn)代碼,它的內(nèi)容如下:

  1. (function () { 
  2.     var d = document, 
  3.     w = window, 
  4.     p = parseInt, 
  5.     dd = d.documentElement, 
  6.     db = d.body, 
  7.     dc = d.compatMode == 'CSS1Compat'
  8.     dx = dc ? dd: db, 
  9.     ec = encodeURIComponent; 
  10.      
  11.      
  12.     w.CHAT = { 
  13.         msgObj:d.getElementById("message"), 
  14.         screenheight:w.innerHeight ? w.innerHeight : dx.clientHeight, 
  15.         username:null
  16.         userid:null
  17.         socket:null
  18.         //讓瀏覽器滾動條保持在最低部 
  19.         scrollToBottom:function(){ 
  20.             w.scrollTo(0, this.msgObj.clientHeight); 
  21.         }, 
  22.         //退出,本例只是一個簡單的刷新 
  23.         logout:function(){ 
  24.             //this.socket.disconnect(); 
  25.             location.reload(); 
  26.         }, 
  27.         //提交聊天消息內(nèi)容 
  28.         submit:function(){ 
  29.             var content = d.getElementById("content").value; 
  30.             if(content != ''){ 
  31.                 var obj = { 
  32.                     userid: this.userid, 
  33.                     username: this.username, 
  34.                     content: content 
  35.                 }; 
  36.                 this.socket.emit('message', obj); 
  37.                 d.getElementById("content").value = ''
  38.             } 
  39.             return false
  40.         }, 
  41.         genUid:function(){ 
  42.             return new Date().getTime()+""+Math.floor(Math.random()*899+100); 
  43.         }, 
  44.         //更新系統(tǒng)消息,本例中在用戶加入、退出的時候調(diào)用 
  45.         updateSysMsg:function(o, action){ 
  46.             //當前在線用戶列表 
  47.             var onlineUsers = o.onlineUsers; 
  48.             //當前在線人數(shù) 
  49.             var onlineCount = o.onlineCount; 
  50.             //新加入用戶的信息 
  51.             var user = o.user; 
  52.                  
  53.             //更新在線人數(shù) 
  54.             var userhtml = ''
  55.             var separator = ''
  56.             for(key in onlineUsers) { 
  57.                 if(onlineUsers.hasOwnProperty(key)){ 
  58.                     userhtml += separator+onlineUsers[key]; 
  59.                     separator = '、'
  60.                 } 
  61.             } 
  62.             d.getElementById("onlinecount").innerHTML = '當前共有 '+onlineCount+' 人在線,在線列表:'+userhtml; 
  63.              
  64.             //添加系統(tǒng)消息 
  65.             var html = ''
  66.             html += '<div class="msg-system">'
  67.             html += user.username; 
  68.             html += (action == 'login') ? ' 加入了聊天室' : ' 退出了聊天室'
  69.             html += '</div>'
  70.             var section = d.createElement('section'); 
  71.             section.className = 'system J-mjrlinkWrap J-cutMsg'
  72.             section.innerHTML = html; 
  73.             this.msgObj.appendChild(section);    
  74.             this.scrollToBottom(); 
  75.         }, 
  76.         //第一個界面用戶提交用戶名 
  77.         usernameSubmit:function(){ 
  78.             var username = d.getElementById("username").value; 
  79.             if(username != ""){ 
  80.                 d.getElementById("username").value = ''
  81.                 d.getElementById("loginbox").style.display = 'none'
  82.                 d.getElementById("chatbox").style.display = 'block'
  83.                 this.init(username); 
  84.             } 
  85.             return false
  86.         }, 
  87.         init:function(username){ 
  88.             /* 
  89.             客戶端根據(jù)時間和隨機數(shù)生成uid,這樣使得聊天室用戶名稱可以重復。 
  90.             實際項目中,如果是需要用戶登錄,那么直接采用用戶的uid來做標識就可以 
  91.             */ 
  92.             this.userid = this.genUid(); 
  93.             this.username = username; 
  94.              
  95.             d.getElementById("showusername").innerHTML = this.username; 
  96.             this.msgObj.style.minHeight = (this.screenheight - db.clientHeight + this.msgObj.clientHeight) + "px"
  97.             this.scrollToBottom(); 
  98.              
  99.             //連接websocket后端服務器 
  100.             this.socket = io.connect('ws://realtime.plhwin.com:3000'); 
  101.              
  102.             //告訴服務器端有用戶登錄 
  103.             this.socket.emit('login', {userid:this.userid, username:this.username}); 
  104.              
  105.             //監(jiān)聽新用戶登錄 
  106.             this.socket.on('login'function(o){ 
  107.                 CHAT.updateSysMsg(o, 'login');   
  108.             }); 
  109.              
  110.             //監(jiān)聽用戶退出 
  111.             this.socket.on('logout'function(o){ 
  112.                 CHAT.updateSysMsg(o, 'logout'); 
  113.             }); 
  114.              
  115.             //監(jiān)聽消息發(fā)送 
  116.             this.socket.on('message'function(obj){ 
  117.                 var isme = (obj.userid == CHAT.userid) ? true : false
  118.                 var contentDiv = '<div>'+obj.content+'</div>'
  119.                 var usernameDiv = '<span>'+obj.username+'</span>'
  120.                  
  121.                 var section = d.createElement('section'); 
  122.                 if(isme){ 
  123.                     section.className = 'user'
  124.                     section.innerHTML = contentDiv + usernameDiv; 
  125.                 } else { 
  126.                     section.className = 'service'
  127.                     section.innerHTML = usernameDiv + contentDiv; 
  128.                 } 
  129.                 CHAT.msgObj.appendChild(section); 
  130.                 CHAT.scrollToBottom();   
  131.             }); 
  132.  
  133.         } 
  134.     }; 
  135.     //通過“回車”提交用戶名 
  136.     d.getElementById("username").onkeydown = function(e) { 
  137.         e = e || event; 
  138.         if (e.keyCode === 13) { 
  139.             CHAT.usernameSubmit(); 
  140.         } 
  141.     }; 
  142.     //通過“回車”提交信息 
  143.     d.getElementById("content").onkeydown = function(e) { 
  144.         e = e || event; 
  145.         if (e.keyCode === 13) { 
  146.             CHAT.submit(); 
  147.         } 
  148.     }; 
  149. })(); 

至此所有的編碼開發(fā)工作全部完成了,在瀏覽器中打開http://demo.plhwin.com/chat/就可以看到效果了。

上面所有的客戶端和服務端的代碼可以從Github上獲得,地址:https://github.com/plhwin/nodejs-socketio-chat

git clone https://github.com/plhwin/nodejs-socketio-chat.git

下載本地后有兩個文件夾 clientserverclient文件夾是客戶端源碼,可以放在Nginx/Apache的WebServer中,也可以放在Node.js的WebServer中。后面的server文件夾里的代碼是websocket服務端代碼,放在Node.js環(huán)境中,使用npm安裝完 expresssocket.io 后,node index.js 啟動后端服務就可以了。

本例只是一個簡單的Demo,留下2個有關(guān)項目擴展的思考:

1、假設是一個在線客服系統(tǒng),里面有許多的公司使用你的服務,每個公司自己的用戶可以通過一個專屬URL地址進入該公司的聊天室,聊天是一對一的,每個公司可以新建多個客服人員,每個客服人員可以同時和客戶端的多個用戶聊天。

2、又假設是一個在線WebIM系統(tǒng),實現(xiàn)類似微信,qq的功能,客戶端可以看到好友在線狀態(tài),在線列表,添加好友,刪除好友,新建群組等,消息的發(fā)送除了支持基本的文字外,還能支持表情、圖片和文件。

有興趣的同學可以繼續(xù)深入研究。

責任編輯:徐川 來源: blog
相關(guān)推薦

2013-03-28 14:54:36

2019-07-26 14:40:58

Vue.jsSocket.IO前端

2017-09-05 15:30:00

JavascriptSocket.ioNode.js

2016-11-22 13:25:28

Apache Spar大數(shù)據(jù)

2014-03-25 14:21:18

WebSocket實時

2024-11-18 17:04:03

Vue3C#

2013-10-23 17:17:31

Node.jsdoT

2012-02-06 10:36:04

Node.js

2023-11-16 14:56:13

2012-01-09 13:24:27

2014-10-30 10:28:55

Node.js

2022-02-22 11:39:13

WebSocketsNode.js開發(fā)

2015-07-15 12:53:05

Node.jsSocket.io遠程控制

2023-11-17 09:35:58

2020-10-12 08:06:28

HTTP 服務器證書

2019-05-05 11:47:09

TypeScript開發(fā)Node.js

2024-09-02 09:31:19

2020-09-04 15:06:04

Docker容器化Node.js

2015-06-25 12:41:53

實時 Node應用性能監(jiān)測

2014-04-01 11:02:00

Node.jsWeb Socket聊天程序
點贊
收藏

51CTO技術(shù)棧公眾號