Web領域的實時推送技術,也被稱做Realtime技術。這種技術要達到的目的是讓用戶不須要刷新瀏覽器就能夠得到實時更新。javascript
應用場景:css
技術實現方案:ajax long polling(ajax長輪詢),comet(http長鏈接)、sockethtml
這裏有篇文章介紹了這幾種技術,能夠看一下。html5
http://www.ibm.com/developerworks/cn/web/wa-lo-comet/java
http://jingyan.baidu.com/article/08b6a591e07ecc14a80922f1.htmlnode
HTTP是一種基於消息(message)的請求(request )/應答(response)協議。當咱們在網頁中點擊一條連接(或者提交一個表單)的時候,瀏覽器給服務器發一個request message,而後服務器算啊算,答覆一條response message。主動發起TCP鏈接的是client,接受TCP鏈接的是server。HTTP消息只有兩種:request和response。client只能發送request message,server只能發送response message。一問一答,所以按HTTP協議自己的設計,服務器不能主動的把消息推給客戶端。jquery
所以,若是讓服務器端也能夠主動發送信息到客戶端,就能夠很大程度改進這些不足。WebSocket就是一個實現這種雙向通訊的新協議。git
WebSocket是基於HTTP的功能追加協議github
WebSocket最初由html5提出,但如今已經發展爲一個獨立的協議標準。WebSocket能夠分爲協議( Protocol )和 API 兩部分,分別由 IETF 和W3C制定了標準。web
先來看看WebSocket協議的創建過程。
爲了實現WebSocket通訊,首先須要客戶端發起一次普通HTTP請求(也就是說,WebSocket的創建是依賴HTTP的)。請求報文可能像這樣:
GET ws://websocket.example.com/ HTTP/1.1 Host: websocket.example.com Upgrade: websocket Connection: Upgrade Origin: http://example.com Sec-WebSocket-Key:pAloKxsGSHtpIHrJdWLvzQ== Sec-WebSocket-Version:13
其中HTTP頭部字段 Upgrade: websocket
和 Connection: Upgrade
很重要,告訴服務器通訊協議將發生改變,轉爲WebSocket協議。支持WebSocket的服務器端在確認以上請求後,應返回狀態碼爲 101 Switching Protocols
的響應:
HTTP/1.1 101 Switching Protocols Upgrade: websocket Connection: Upgrade Sec-WebSocket-Accept: nRu4KAPUPjjWYrnzxDVeqOxCvlM=
其中字段 Sec-WebSocket-Accept
是由服務器對前面客戶端發送的 Sec-WebSocket-Key
進行確認和加密後的結果,至關於一次驗證,以幫助客戶端確信對方是真實可用的WebSocket服務器。
驗證經過後,這個握手響應就確立了WebSocket鏈接,此後,服務器端就能夠主動發信息給客戶端了。此時的狀態比較像服務器端和客戶端接通了電話,不管是誰有什麼信息想告訴對方,開口就行了。
一旦創建了WebSocket鏈接,此後的通訊就再也不使用HTTP了,改成使用WebSocket獨立的數據幀
整個過程像這樣:
1.創建項目文件,安裝node 和express框架
socket.io http://socket.io/docs/
服務器端安裝socket.io
$ npm install socket.io
客戶端下載socket.io.js
client是客戶端文件 server是服務器端文件。
2.寫界面
個人界面是這樣的
html
<!doctype html> <html lang="en"> <head> <meta charset="UTF-8" /> <title>聊天室</title> <link rel="stylesheet" type="text/css" href="css/index.css"/> </head> <body> <div class="main"> <div class="main-top"> socket.io demo </div> <div class="main-body"> <section class="chatRoomInfo"> <div class="info">當前共有<span class="chatNum">0</span>人在線。在線列表: <span class="chatList"></span></div> </section> <!--<section class="chatRoomTip"> <div>子木加入到聊天室</div> </section> <section class="user clearfix"> <span>子木</span> <div> 測試測試測試測試測試測試測試測試測試試測試測試測試測試測試測試測試測試測試測試測試 </div> </section> <section class="server clearfix"> <span>子木</span> <div> 測試測試測試 </div> </section>--> </div> <div class="main-footer clearfix"> <div class="input"> <input type="text" name="msg" id="msg" value="" /> </div> <button type="button" class="send">發送</button> </div> </div> <script src="js/jquery-2.1.0.js" type="text/javascript" charset="utf-8"></script> <script src="js/socket.io.js" type="text/javascript" charset="utf-8"></script> <script> //do something</script> </body> </html>
css
* {
margin: 0;
padding: 0;
}
.clearfix {
zoom: 1;
}
.clearfix:after {
clear: both;
content: '.';
display: block;
width: 0;
height: 0;
visibility: hidden;
}
.main {
width: 100%;
height: 100%;
font-size: 14px;
}
.main-top {
height: 30px;
background-color: #3d3d3d;
text-indent: 15px;
color: #ffffff;
font-size: 16px;
line-height: 30px;
}
.main-body {
background-color: #efeff4;
position: absolute;
top: 30px;
bottom: 50px;
width: 100%;
overflow-y: scroll;
scrollbar-3dlight-color: ;
}
.chatRoomInfo {
padding: 10px;
font-size: 12px;
color: #666;
}
.chatRoomTip {
text-align: center;
padding: 10px;
font-size: 12px;
color: #444;
}
.user {
width: 100%;
min-height: 38px;
min-width: 36px;
margin-bottom: 15px;
}
.user span {
float: right;
}
.user div {
float: right;
min-height: 38px;
min-width: 38px;
max-width: 70%;
line-height: 38px;
padding: 0 15px;
color: #FFFFFF;
margin-right: 10px;
word-break: break-all;
background-color: #007aff;
position: relative;
border-radius: 5px;
}
.user div:after {
content: "";
position: absolute;
right: -5px;
top: 4px;
width: 0;
height: 0;
border-top: solid transparent;
border-left: 7px solid #007aff;
border-bottom: 4px solid transparent;
}
.server {
width: 100%;
min-height: 38px;
min-width: 36px;
margin-bottom: 15px;
}
.server span {
float: left;
}
.server div {
float: left;
min-height: 38px;
min-width: 38px;
max-width: 70%;
line-height: 38px;
padding: 0 15px;
color: #FFFFFF;
margin-left: 10px;
word-break: break-all;
background-color: #007aff;
position: relative;
border-radius: 5px;
}
.server div:after {
content: "";
position: absolute;
left: -5px;
top: 4px;
width: 0;
height: 0;
border-top: solid transparent;
border-right: 7px solid #007aff;
border-bottom: 4px solid transparent;
}
.main-footer{
position: absolute;
bottom: 0;
width: 100%;
height: 50px;
}
.input{
float: left;
width: 80%;
height: 40px;
margin-top: 5px;
margin-left: 1%;
margin-right: 1%;
border: 1px solid #666666;
}
.input input{
width: 100%;
height: 40px;
outline: none;
border: none;
font-size: 14px;
color: #333;
}
.send{
float: left;
width: 16%;
height: 40px;
margin-top: 5px;
margin-left: 1%;
border: none;
background-color: #e8e8e8;
color: #007aff;
outline: none;
}
如今開始寫邏輯
/*按鈕點擊效果*/ $('.send').mousedown(function(){ $(this).css({'background':"#007aff",'color':"#ffffff"}); }) $('.send').mouseup(function(){ $(this).css({'background':"#e8e8e8",'color':"#ffffff"}); }) /*socket*/ window.onload=function () { var username=prompt('請輸入您的姓名'); if (!username){ alert('姓名必填'); history.go(0); } // username="子木"; userId=genUid(); var userInfo={ 'userid':userId, 'username':username }; //鏈接socket後端服務器 var socket=io.connect("ws://127.0.0.1:4000"); //通知用戶有用戶登陸 socket.emit('login',userInfo); //監聽新用戶登陸 socket.on('login',function (o) { updateMsg(o, 'login'); }); //監聽用戶退出 socket.on('logout',function (o) { updateMsg(o, 'logout'); }); //發送消息 socket.on('message',function (obj) { if(obj.userid==userId) { var MsgHtml='<section class="user clearfix">' +'<span>'+obj.username+'</span>' +'<div>'+obj.content+'</div>' +'</section>'; }else{ var MsgHtml='<section class="server clearfix">' +'<span>'+obj.username+'</span>' +'<div>'+obj.content+'</div>' +'</section>'; } $('.main-body').append(MsgHtml); $('.main-body').scrollTop(99999); }) $('.send').click(function () { var content=$('input[name="msg"]').val(); if (content){ var obj={ 'userid':userId, 'username':username, 'content':content } socket.emit('message',obj); $('input[name="msg"]').val(""); } }) } /*用戶id生成*/ function genUid() { return new Date().getTime()+""+Math.floor(Math.random()*899+100); } function logout(){ socket.disconnect(); location.reload(); } /*監聽函數*/ function updateMsg(o,action) { //當前在線列表 var onlineUser=o.onlineUser; //當前在線數 var onlineCount=o.onlineCount; //新加用戶 var user=o.user; //更新在線人數 var userList=''; var separator = ''; for(key in onlineUser){ userList+=separator+onlineUser[key]; separator = '、'; } //跟新房間信息 $('.chatNum').text(onlineCount); $('.chatList').text(userList); //系統消息 if(action=='login'){ var sysHtml='<section class="chatRoomTip"><div>'+user.username+'進入聊天室</div></section>'; } if(action=="logout"){ var sysHtml='<section class="chatRoomTip"><div>'+user.username+'退出聊天室</div></section>'; } $(".main-body").append(sysHtml); $('.main-body').scrollTop(99999); }
服務器代碼實現 app.js
var app = require('express')(); var http=require('http').Server(app); var io=require('socket.io')(http); app.get('/socket/client/index.html',function (req,res) { res.send('<h1>welcome</h1>'); }) //在線用戶 var onlineUser={}; var onlineCount=0; io.on('connection',function (socket) { console.log('新用戶登陸'); //監聽新用戶加入 socket.on('login',function (obj) { socket.name=obj.userid; //檢查用戶在線列表 if(!onlineUser.hasOwnProperty(obj.userid)){ onlineUser[obj.userid]=obj.username; //在線人數+1 onlineCount++; } //廣播消息 io.emit('login',{onlineUser:onlineUser,onlineCount:onlineCount,user:obj}); console.log(obj.username+"加入了聊天室"); }) //監聽用戶退出 socket.on('disconnect',function () { //將退出用戶在在線列表刪除 if(onlineUser.hasOwnProperty(socket.name)){ //退出用戶信息 var obj={userid:socket.name, username:onlineUser[socket.name]}; //刪除 delete onlineUser[socket.name]; //在線人數-1 onlineCount--; //廣播消息 io.emit('logout',{onlineUser:onlineUser,onlineCount:onlineCount,user:obj}); console.log(obj.username+"退出了聊天室"); } }) //監聽用戶發佈聊天內容 socket.on('message', function(obj){ //向全部客戶端廣播發布的消息 io.emit('message', obj); console.log(obj.username+'說:'+obj.content); }); }) http.listen(4000, function(){ console.log('listening on *:4000'); });
代碼所有貼上來
源碼地址:https://github.com/zimuqi/socketChat
下載後安裝好socket.io express後進入到server 目錄下 直接node app.js。而後打開項目主頁就能夠看到效了。能夠多打開幾個窗口互動一下。
有興趣的能夠再去研究一下WebIM系統,實現相似微信,qq的功能,客戶端能夠看到好友在線狀態,在線列表,添加好友,刪除好友,新建羣組等,消息的發送除了支持基本的文字外,還能支持表情、圖片和文件。