我相信有很多人是看到撩妹才進來的,別急,我們要一步一步來。
websocket簡介
WebSocket是HTML5開始提供的一種瀏覽器與服務器間進行全雙工通訊的網絡技術。
現很多網站爲了實現即時通訊,所用的技術都是輪詢(polling)。輪詢是在特定的的時間間隔(如每1秒),由瀏覽器對服務器發出HTTP請求,然後由服務器返回最新的數據給客服端的瀏覽器,這種方式有一個很大的弊端,就是會佔用很多的帶寬。
最新的輪詢效果是Comet – 用了AJAX。但這種技術雖然可達到全雙工通信,但依然需要發出請求。
使用WebSocket,瀏覽器和服務器只需要要做一個握手的動作,然後,瀏覽器和服務器之間就形成了一條快速通道,兩者之間就直接可以數據互相傳送。而且它爲我們實現即時服務帶來了兩大好處:
節省資源:互相溝通的Header是很小的-大概只有 2 Bytes。
推送信息:不需要客戶端請求,服務器可以主動傳送數據給客戶端。
實現了websocket的瀏覽器:
Tables | Are |
---|---|
chrome | Supported in version 4+ |
Firefox | Supported in version 4+ |
Internet Explorer | Supported in version 10+ |
Opera | Supported in version 10+ |
Safari | Supported in version 5+ |
有些迫不及待想撩妹的 很生氣,不是講socket.io麼,你扯websocket幹嘛。我只能給你個關愛智障的眼神-.-
socket.io
Socket.IO是一個WebSocket庫,包括了客戶端的js和服務器端的nodejs,它的目標是構建可以在不同瀏覽器和移動設備上使用的實時應用。
socket.io的特點
- 易用性:socket.io封裝了服務端和客戶端,使用起來非常簡單方便。
- 跨平臺:socket.io支持跨平臺,這就意味着你有了更多的選擇,可以在自己喜歡的平臺下開發實時應用。
- 自適應:它會自動根據瀏覽器從WebSocket、AJAX長輪詢、Iframe流等等各種方式中選擇最佳的方式來實現網絡實時應用,非常方便和人性化,而且支持的瀏覽器最低達IE5.5。
體驗socket.io
首先,創建一個項目,安裝socket.io模塊
npm install socket.io
接下來,新建一個test.js文件:
var io = require('socket.io');
var io = require('socket.io')(80);//監聽80端口
爲什麼要監聽80端口呢?因爲我懶得寫3000。。。80是默認端口,可以不輸入的。
接下來,註冊一個事件:
io.on('connection',function(socket){
//連接成功...
socket.on('disconnect',function(){
//用戶已經離開...
});
});
connection事件在客戶端成功連接到服務端時觸發,有了這個事件,我們可以隨時掌握用戶連接到服務端的信息。
當客戶端成功建立連接時,在connection事件的回調函數中,我們還是可以爲socket註冊一些常用的事件,如:disconnect事件,它在客戶端連接斷開是觸發,這時候我就知道用戶已經離開了。
目前爲止,我們已經搭建好了一個最簡單的socket服務器,爲了在瀏覽器中能夠訪問到我們的服務,我們還需要在服務端搭建一個簡單的web服務器,讓瀏覽器能夠訪問我們的客戶端頁面。
爲了便捷,我們選用node.js中常用的express框架來實現web服務,示例如下:
var express = require('express');
var app = express();
app.get('/',function(req,res){
res.status(200).send('歡迎學習nodejs!');
});
var server = require('http').createServer(app);
var io = require('socket.io')(server);
io.on('connection',function(socket){
});
server.listen(80);
服務端構建完畢,下面看一看客戶端應該如何使用。
服務端運行後會在根目錄動態生成socket.io的客戶端js文件,客戶端可以通過固定路徑/socket.io/socket.io.js添加引用。
首先添加網頁index.html,並在網頁中引用客戶端js文件
<script src="/socket.io/socket.io.js"></script>
當然這樣的客戶端引用方式並不是必須的,我們也可以引用官方的cdn或者下載到本地的客戶端文件。一般情況下推薦引用動態生成的客戶端文件,因爲這樣客戶端和服務端的版本可以保持一致,減少出錯的機率。
//官方CDN
<script src="https://cdn.socket.io/socket.io-1.2.1.js"></script>
當客戶端成功加載socket.io客戶端文件後會獲取到一個全局對象io,我們將通過io.connect函數來向服務端發起連接請求。
var socket = io.connect('/');
socket.on('connect',function(){
//連接成功
});
socket.on('disconnect',function(data){
//連接斷開
});
connect函數可以接受一個url參數,url可以socket服務的http完整地址,也可以是相對路徑,如果省略則表示默認連接當前路徑。與服務端類似,客戶端也需要註冊相應的事件來捕獲信息,不同的是客戶端連接成功的事件是connect。
瞭解了客戶端如何使用,下面我們創建網頁index.html,並添加如下內容(保存):
<html>
<head>
<script src="/socket.io/socket.io.js"></script>
<script>
window.onload = function(){
var socket = io.connect('/');
socket.on('connect',function(){
document.write('連接成功!');
});
};
</script>
</head>
<body>
</body>
</html>
頁面添加完畢還要記得在服務端app.js中爲它添加路由,讓我們可以訪問測試網頁:
app.get('/index',function(req,res){
res.sendFile('index.html',{root:__dirname});
});
最後一步:通信
示例-客戶端向服務端發送消息(index.html):
var socket = io.connect('/');
socket.on('connect',function(){
//客戶端連接成功後發送消息'hello world!'
socket.send('hello world!');
});
socket.on('message',function(data){
alert(data);
});
連接成功後,我們向服務端發送消息hello world!,還爲socket註冊了message事件,它是send函數對應的接收消息的事件,當服務端向客戶端send消息時,我們就可以在message事件中接收到發送過來的消息。
服務端向客戶端發送消息也可以通過send的方式,示例 - 服務端向客戶端發送消息(app.js):
var io = require('scoket.io');
io.on('connection',function(socket){
socket.send('welcome!');
socket.on('message',function(data){
//收到消息
console.log(data);
});
});
到現在爲止,socket.io的基本用法就結束了,看起來蠻簡單的,接下來,稍微提一下socket.io的其他用法及原理。
socket.io是如何發送消息的?
在socket.io中,emit函數用於發送數據,還記得在入門篇中,我們使用send的方式實現了信息的互發,其實send函數只是emit的封裝,實際上還是使用了emit,且看send函數是如何實現的:
function send(){
var args = toArray(arguments);
args.unshift('message');
this.emit.apply(this, args);
return this;
}
在send函數中,獲取到原來的參數,並在原來的基礎上插入了一個參數message,然後調用了emit函數。通過send函數的實現,我們也學會了emit函數的用法,它有連個參數,第一個參數是事件名稱,在接收端註冊該事件就可以接收到發送過去的信息,事件名稱可以自由定義,在不同的場景下,我們可以定義不同的事件來接收消息。第二個參數纔是發送的數據。瞭解清楚了工作原理,下面來將send替換成emit函數發送信息:
//app.js
io.on('connection',function(socket){
socket.emit('message','連接成功!');
socket.on('message',function(data){
});
});
socket.io的命名空間
命名空間着實是一個非常實用好用的功能。我們可以通過命名空間,劃分出不同的房間,在房間裏的廣播和通信都不會影響到房間以外的客戶端。
那麼如何創建房間呢?在服務端,通過of(“”)的方式來劃分新的命名空間:
io.of('chat').on('connection',function(socket){
});
示例中,我們創建一個名爲chat的房間,客戶端可以通過如下方式連接到指定的房間:
var socket = io.connect('/chat');
雖然連接到指定的房間,但是我們也可以在服務端操作,自由的進出房間:
socket.join('chat');//進入chat房間
socket.leave('chat');//離開chat房間
socket.io的廣播消息
在實時應用中,廣播是一個不可或缺的功能,socket.io提供兩種服務端廣播方式。
第一種廣播方式可以稱之爲’全局廣播’,顧名思義,全局廣播就是所有連接到服務器的客戶端都會受到廣播的信息:
socket.broadcast.emit('DATA',data);
但是,在實際應用場景中,我們很多時候並不需要所有用戶都收到廣播信息,有的廣播信息只發送給一部分客戶端,比如某個房間裏面的用戶,那麼可以使用如下方式:
socket.broadcast.to('chat').emit('DATA',data);
當使用to()的方式廣播信息時,只有該命名空間下的客戶端纔會收到廣播信息,是不是很方便呢。
sockei.io的中間件
socket.io提供中間件功能,我們可以通過中間件來對請求進行預處理,比如身份驗證:
io.use(function(socket, next){
if (socket.request.headers.cookie) return next();
next(new Error('Authentication error'));
});
示例中展示了通過中間件進行身份驗證,當沒有cookie的時候拋出異常。
socket.io傳遞參數的方法
在很多應用場景中,客戶端發起連接請求時都需要傳遞參數,這些參數可能是身份驗證、初始化設置等等,那麼socket.io發起連接時如何傳遞參數呢?
var socket = io.connect('/');
由於connect函數發起連接的參數是一個url,你可能會想到把參數拼接到url上,如http://xxxx?xx=xxxx,但是很遺憾這樣是行不通的,我們可以通過這樣的方式來傳遞參數:
var socket = io.connect('/',{ _query:'sid=123456' });
在服務端可以這樣獲取到傳遞的參數:
io.use(function(socket){
var query = socket.request._query;
var sid = query.sid;
});
客戶端傳遞的參數已經被解析成了一個json對象,這個對象就是_query。
好了,socket.io的講解就到這裏,接下來是大家最喜歡的撩妹環節。
撩妹技巧之聊天的實現
1。創建一個express項目(方法還是那個方法)
2。假如socket.io模塊
3。修改routes目錄下的index.js:
var express = require('express');
var router = express.Router();
var socket_io = require('socket.io');
router.get('/', function(req, res, next) {
res.send('response');
});
router.prepareSocketIO = function (server) {
var io = socket_io.listen(server);
io.sockets.on('connection',function (socket) {
socket.on('join',function (user) {
socket.user = user;
socket.emit('state','server',true);
socket.broadcast.emit('state','server',user+"online");
});
socket.on('sendMsg',function (msg) {
socket.emit('chat',socket.user,msg);
socket.broadcast.emit('chat',socket.user,msg);
});
});
}
module.exports = router;
4。在app.js中添加代碼:
app.ready=function(server){
routes.prepareSocketIO(server);
}
5。在www文件裏添加代碼
app.ready(server);
6。在public目錄下創建chat.html:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title></title>
<script src="http://code.jquery.com/jquery-2.1.1.min.js"></script>
<script src="/socket.io/socket.io.js"></script>
<link href="/stylesheets/style.css" type="text/css" rel="stylesheet">
</head>
<body>
<div>
<table id="content">
</table>
</div>
<table class="tool">
<tr>
<td class="left">
<div id="textContent" contenteditable="true" type="text"></div>
</td>
<td>
<button id="send">發送</button>
</td>
</tr>
</table>
<script>
var socket = io.connect('http://127.0.0.1:3000');
var userName = "訪客某某";
socket.on('connect', function () {
userName = prompt("請輸入你的姓名?") || userName;
socket.emit('join', userName);
});
socket.on('chat', function (user, data) {
if (user === userName) {
$('<tr class="align-right"><td><span><span class="msg">' + data + '</span>' + user + '</span></td></tr>').appendTo($('#content'));
} else {
$('<tr class="align-left"><td><span >' + user + '<span class="msg">' + data + '</span></span></td></tr>').appendTo($('#content'));
}
p.className = direct;
});
$('#send').click(function () {
var content = $('#textContent').html();
if (content = content.replace(" ", "")) {
socket.emit('sendMsg', content);
$('#textContent').html("");
}
});
</script>
</body>
</html>
7。在public/stylesheets目錄下的style.css中添加:
table {
width: 100%;
}
table.tool {
position: fixed;
bottom: 0;
left: 0;
right: 0;
padding: 10px;
}
#content {
height: auto;
padding: 10px;
padding-bottom: 32px;
}
#content tr {
margin-bottom: 10px;
}
#textContent {
border: 1px solid grey;
border-radius: 5px;
padding: 6px;
}
.left {
width: 88%;
}
#send {
width: auto;
padding: 2px 12px;
line-height: 26px !important;
border-radius: 5px;
font-weight: bold;
color: white;
background-color: #ff4400;
}
tr.align-left td > span {
float: left;
}
tr.align-left td > span span {
display: inline-block;
}
tr.align-right td > span {
float: right;
}
tr.align-right td > span span {
display: inline-block;
}
span .msg {
padding: 6px;
background-color: #00B7FF;
border: 1px solid #00B7ee;
border-radius: 5px;
}
.timer {
display: block;
text-align: center;
}
目錄結構:
之後就可以愉快的聊天了。啓動服務,在瀏覽器中輸入http://localhost:3000/chat.html,同時打開兩個頁面就可以聊天了。
如圖:
項目代碼放在github上:https://github.com/CleverFan/nodejsStudy/tree/master/expressTest/day03_socketio
ps 具體怎麼撩妹我還沒想好,你們自己想吧,哈哈哈哈哈