nodejs死亡筆記之socket.io那些事(附可用來撩妹的聊天功能)

我相信有很多人是看到撩妹才進來的,別急,我們要一步一步來。

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 具體怎麼撩妹我還沒想好,你們自己想吧,哈哈哈哈哈

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章