WebSocket是HTML5的一個新特性提供了瀏覽器端和服務(wù)器端雙向?qū)崟r通信的能力,用于實現(xiàn)一些實時在線的應(yīng)用,這篇文章將簡單介紹WebSocket的使用,并實現(xiàn)一個簡單的在線聊天的功能。
WebSocket簡介
WebSocket與HTTP
WebSocket與HTTP都是一種協(xié)議,但是HTTP是一應(yīng)一答的,無論是 HTTP 1.0 還是 HTTP 1.1,它們都需要瀏覽器端發(fā)出請求(request)后才能讓服務(wù)器返回響應(yīng)(respones),在需要實時通訊的應(yīng)用時,比如在線聊天,在線游戲等,過去只能使用輪詢的方式來獲取最新的消息,這樣會消耗大連的網(wǎng)絡(luò)資源。
但是有了WebSocket之后,我們可以讓瀏覽器端和服務(wù)器端建立一個持久的雙向通訊連接,在有新數(shù)據(jù)的時候自動發(fā)送并接收,減少請求的數(shù)量,節(jié)省網(wǎng)絡(luò)資源。
HTML5的WebSocket
在HTML5中我們可以使用 WebSocket 對象來連接WebSocket服務(wù)器
var ws=new WebSocket(URL,protocols);
| 參數(shù)名 | 參數(shù)描述 |
|---|---|
| URL | 需要連接的地址 |
| protocols | (可選)可以是一個單個的協(xié)議名字字符串或者包含多個協(xié)議名字字符串的數(shù)組。 |
當構(gòu)建成功時會返回一個WebSocket對象,失敗時返回錯誤。
WebSocket對象提供了2個方法:
-
void close(in optional unsigned long code, in optional DOMString reason);
這個方法用于關(guān)閉WebSocket連接。
參數(shù)名 參數(shù)描述 code 關(guān)閉連接的狀態(tài)號,默認為1000(正常關(guān)閉) reason 一個字符串,用于描述關(guān)閉的原因。這個字符串必須是不長于123字節(jié)的UTF-8 文本 -
void send(in DOMString data);
這個方法用于像服務(wù)端發(fā)送數(shù)據(jù)。
參數(shù)名 參數(shù)描述 data 發(fā)送的數(shù)據(jù)
這兩個方法是有瀏覽器主動執(zhí)行,服務(wù)器發(fā)過來的消息需要通過監(jiān)聽事件來實現(xiàn)。
WebSocket對象有4個事件:
-
open事件
當連接成功連接時會觸發(fā)該事事件,會傳入一個Event對象
-
message事件
當接收到消息時會觸發(fā)該事件,會傳入一個Event對象,并且可以通過Event對象data屬性獲得數(shù)據(jù)
-
error事件
當連接出錯時會觸發(fā)該事事件,并傳入一個Event對象
-
close事件
當連接關(guān)閉的時候會觸發(fā)該事件,并傳入一個Event對象
=、=有了API下面肯定就是要擼起袖子搞一波啦~~~
WebSocket的簡單應(yīng)用-在線簡易聊天室
服務(wù)器的搭建
在這里我使用nodejs搭建服務(wù)器,畢竟咋們是學前端嘛=、=
能搭建Websocket服務(wù)器的包有很多,這里我們選用 ws ,因為這個包是十分基礎(chǔ)的一個,同時API也與W3C的規(guī)范差不多,方便理解。
sever.js
var path=require('path');
var http=require('http');
var express=require('express');
var ws=require('ws');
var app=express();
app.use(express.static(path.join(__dirname, './public')));
var server=app.listen(3000,()=>{
console.log('服務(wù)器啟動');
});
var io=new ws.Server({server});
io.on('connection',function(client){
client.on('message',function(data){
console.log(data);
});
client.on('close',function(data){
console.log(data);
});
setTimeout(function() {
if(client.readyState===1){//防止已斷開連接
client.send('來之服務(wù)器的消息');
}
}, 1000);
});
這段代碼我們就簡單的搭建了一個websokect服務(wù)器,我們可以從代碼中看出,服務(wù)器在websokect連接上后,會輸出 message 和 close 請求的信息,并且我們在連接后會向瀏覽器發(fā)送一條消息。
下面是我們?yōu)g覽器的JS
var ws=new WebSocket('ws://127.0.0.1:3000/');
ws.onopen=function(event){
console.log('連接成功');
ws.onmessage=function(event){
console.log('收到消息:'+event.data);
}
ws.onclose=function(event){
console.log('連接關(guān)閉');
}
ws.send('來自瀏覽器的消息');
};
ws.onerror=function(event){
console.log('連接出錯');
};
我們將這段代碼加入到服務(wù)器public目錄的index.html文件中,然后訪問 localhost:3000 查看瀏覽器和服務(wù)器控制臺
服務(wù)器控制臺

瀏覽器控制臺

我們可以看到WebSocket服務(wù)器已經(jīng)成功的運行了起來
簡易聊天室的實現(xiàn)
我們這里只實現(xiàn)一個簡單的聊天室的功能,發(fā)送和接受以及展示。
首先我我們把聊天室的頁面進行簡單的編寫,實現(xiàn)簡單的樣式
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>websokect簡易聊天室</title>
<style>
.messageBox{
background: lightslategrey;
}
p{
font-size: 16px;
margin: 5px;
}
.messageBox-sys{
text-align: center;
color:yellow
}
.messageBox-other{
text-align: left;
color:black;
}
.messageBox-my{
text-align: right;
color:white;
}
input{
height: 20px;
margin: 3px;
border: 1px;
}
</style>
</head>
<body>
<div style="width:60%;margin:0 auto;background: grey">
<div id="messageBox" class="messageBox">
<p class="messageBox-sys">系統(tǒng)消息</p>
<p class="messageBox-other">別人的消息</p>
<p class="messageBox-my">我的消息</p>
</div>
<div class="control">
<div style="margin: auto;width: 80%;position: relative">
<input id="message" style="width: 70%" type="" name="" placeholder="輸入聊天內(nèi)容" value="">
<input id="btn-send" style="width:20%" type="button" value="發(fā)送">
</div>
</div>
</div>
</body>
</html>

我們在這里使用JSON格式的數(shù)據(jù)進行數(shù)據(jù)發(fā)送,包含文本內(nèi)容和發(fā)送時間
document.getElementById('btn-send').addEventListener('click',sendMessage);
function sendMessage(){
if(ws.readyState!==1){
alert('連接為建立');
return;
}
var msg=document.getElementById('message').value;
ws.send(JSON.stringify({
msg:msg,
date:new Date().getTime()
}));
var p=document.createElement('p');
p.innerHTML="msg";
p.className="messageBox-my";
document.getElementById('messageBox').appendChild(p);
}
然后監(jiān)聽服務(wù)器發(fā)回的消息
ws.onmessage=function(event){
var data=event.data;
var type=data.type||0;
var msg=data.msg||'';
var date=data.date||new Date();
}
頁面寫好了,我們只需要服務(wù)器簡單處理下就可以了
服務(wù)器需要實現(xiàn)
收到消息轉(zhuǎn)發(fā)給別人
當有人加入/離開時進行提示
我們只需要一個數(shù)組存儲好加入進來的每個連接,我們就可以進行統(tǒng)一的轉(zhuǎn)發(fā)和處理。
當一個新的連接加入時,我們就可以遍歷數(shù)組向其他連接發(fā)送消息。
var linkarr=[];
function join(client){
linkarr.forEach((pepole)=>{
pepole.send(JSON.stringify({
type:0,
msg:"有新的成員加入!現(xiàn)在聊天室有 "+(linkarr.length+1)+" 個人",
date:new Date().getTime()
}));
});
client.send(JSON.stringify({
type:0,
msg:"歡迎加入!現(xiàn)在聊天室有 "+(linkarr.length+1)+" 個人",
date:new Date().getTime()
}));
linkarr.push(client);
}
同理離開和發(fā)送信息時也一樣
function leave(client){
var index=linkarr.indexOf(client);
linkarr.splice(index,1);
linkarr.forEach((pepole)=>{
pepole.send(JSON.stringify({
type:0,
msg:"有一位成員離開!現(xiàn)在聊天室有 "+(linkarr.length)+" 個人",
date:new Date().getTime()
}));
});
}
function sendData(client,data){
linkarr.forEach((pepole)=>{
if(pepole===client)return;
pepole.send(JSON.stringify({
type:1,
msg:data.msg,
date:data.date
}));
});
}
然后在我們的需要調(diào)用函數(shù)的地方加上,我們的服務(wù)器就完成啦?。。?/p>
var path=require('path');
var http=require('http');
var express=require('express');
var ws=require('ws');
var app=express();
app.use(express.static(path.join(__dirname, './public')));
var server=app.listen(3000,()=>{
console.log('服務(wù)器啟動');
});
var io=new ws.Server({server});
var linkarr=[];
io.on('connection',function(client){
join(client);
client.on('message',function(data){
sendData(client,JSON.parse(data));
});
client.on('close',function(data){
leave(client);
});
});
function join(client){
linkarr.forEach((pepole)=>{
pepole.send(JSON.stringify({
type:0,
msg:"有新的成員加入!現(xiàn)在聊天室有 "+(linkarr.length+1)+" 個人",
date:new Date().getTime()
}));
});
client.send(JSON.stringify({
type:0,
msg:"歡迎加入!現(xiàn)在聊天室有 "+(linkarr.length+1)+" 個人",
date:new Date().getTime()
}));
linkarr.push(client);
}
function leave(client){
var index=linkarr.indexOf(client);
linkarr.splice(index,1);
linkarr.forEach((pepole)=>{
pepole.send(JSON.stringify({
type:0,
msg:"有一位成員離開!現(xiàn)在聊天室有 "+(linkarr.length)+" 個人",
date:new Date().getTime()
}));
});
}
function leave(client){
var index=linkarr.indexOf(client);
linkarr.splice(index,1);
linkarr.forEach((pepole)=>{
pepole.send(JSON.stringify({
type:0,
msg:"有一位成員離開!現(xiàn)在聊天室有 "+(linkarr.length)+" 個人",
date:new Date().getTime()
}));
});
}
function sendData(client,data){
linkarr.forEach((pepole)=>{
if(pepole===client)return;
pepole.send(JSON.stringify({
type:1,
msg:data.msg,
date:data.date
}));
});
}
同理我們的頁面也能快速的寫出來
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>websokect簡易聊天室</title>
<style>
.messageBox{
background: lightslategrey;
}
p{
font-size: 16px;
margin: 5px;
}
.messageBox-sys{
text-align: center;
color:yellow
}
.messageBox-other{
text-align: left;
color:black;
}
.messageBox-my{
text-align: right;
color:white;
}
input{
height: 20px;
margin: 3px;
border: 1px;
}
</style>
</head>
<body>
<div style="width:60%;margin:0 auto;background: grey">
<div id="messageBox" class="messageBox">
</div>
<div class="control">
<div style="margin: auto;width: 80%;position: relative">
<input id="message" style="width: 70%" type="" name="" placeholder="輸入聊天內(nèi)容" value="">
<input id="btn-send" style="width:20%" type="button" value="發(fā)送">
</div>
</div>
</div>
<script type="text/javascript">
var ws=new WebSocket('ws://127.0.0.1:3000/');
ws.onopen=function(event){
console.log('連接成功');
ws.onmessage=function(event){
var data=JSON.parse(event.data);
var type=data.type||0;
var msg=data.msg||'';
var date=data.date||new Date();
var p=document.createElement('p');
p.innerHTML=msg;
switch (type){
case 0:p.className="messageBox-sys";break;
case 1:p.className="messageBox-other";break;
default:p.className="messageBox-sys";
}
document.getElementById('messageBox').appendChild(p);
}
ws.onclose=function(event){
console.log('連接關(guān)閉');
}
};
ws.onerror=function(event){
console.log('連接出錯');
};
document.getElementById('btn-send').addEventListener('click',sendMessage);
function sendMessage(){
if(ws.readyState!==1){
alert('連接為建立');
return;
}
var msg=document.getElementById('message').value;
ws.send(JSON.stringify({
msg:msg,
date:new Date().getTime()
}));
var p=document.createElement('p');
p.innerHTML=msg;
p.className="messageBox-my";
document.getElementById('messageBox').appendChild(p);
}
</script>
</body>
</html>
于是很簡單的就完成了一個在線的簡易聊天室,=、=下面是一些簡單的效果結(jié)果


詳細DEMO可以查看參考資料
后記
這個DEMO的功能做的很簡單,離正真的聊天室功能還差很多,但是。。。不然為嘛叫DEMO呢=、=
有興趣的話我會慢慢完善功能-、-,所不定我會掛在上我的博客~~~~
啦啦啦
參考資料
END
2017-3-5 完成
2017-3-5 立項