1. 程式人生 > >基於nodejs + websocket 搭建即時通訊應用

基於nodejs + websocket 搭建即時通訊應用

如果說AJAX是像手機發簡訊一樣,傳送資訊,獲取資訊,那麼websocket技術則是打電話這樣。Web領域的實時推送技術,也被稱作Realtime技術。這種技術要達到的目的是讓使用者不需要重新整理瀏覽器就可以獲得實時更新。在以前你可能會使用AJAX進行輪詢,這造成了伺服器的多重壓力,使用websocket,既可實現一次連線,保持通話的作用。而它有著廣泛的應用場景,比如線上聊天室、線上客服系統、評論系統、WebIM等。

WebSocket簡介

要做到WEB實時推送,就讓人想到WebSocket。在WebSocket出現之前,很多網站為了實現實時推送技術,通常採用的方案是輪詢 (Polling)和Comet技術,Comet又可細分為兩種實現方式,一種是長輪詢機制,一種稱為流技術,這兩種方式實際上是對輪詢技術的改進,這些方案帶來很明顯的缺點,需要由瀏覽器對伺服器發出HTTP request,大量消耗伺服器頻寬和資源。

面對這種狀況,HTML5定義了WebSocket協議,能更好的節省伺服器資源和頻寬並實現真正意義上的實時推送。

WebSocket協議本質上是一個基於TCP的協議,它由通訊協議和程式設計API組成,WebSocket能夠在瀏覽器和伺服器之間建立雙向連線, 以基於事件的方式,賦予瀏覽器實時通訊能力。既然是雙向通訊,就意味著伺服器端和客戶端可以同時傳送並響應請求,而不再像HTTP的請求和響應。

為了建立一個WebSocket連線,客戶端瀏覽器首先要向伺服器發起一個HTTP請求,這個請求和通常的HTTP請求不同,包含了一些附加頭資訊,其中附加頭資訊”Upgrade: WebSocket”表明這是一個申請協議升級的HTTP請求,伺服器端解析這些附加的頭資訊然後產生應答資訊返回給客戶端,客戶端和伺服器端的WebSocket連線就建立起來了,雙方就可以通過這個連線通道自由的傳遞資訊,並且這個連線會持續存在直到客戶端或者伺服器端的某一方主動的關閉連 接。

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


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

WebSocket實戰

我們將實現一個nodejs + websocket的聊天室

需求分析

1、相容不支援WebSocket的低版本瀏覽器。
2、允許客戶端有相同的使用者名稱。
3、進入聊天室後可以看到當前線上的使用者和線上人數。
4、使用者上線或退出,所有線上的客戶端應該實時更新。
5、使用者傳送訊息,所有客戶端實時收取。

在實際的開發過程中,為了使用WebSocket介面構建Web應用,我們首先需要構建一個實現了 WebSocket規範的服務端,服務端的實現不受平臺和開發語言的限制,只需要遵從WebSocket規範即可,目前已經出現了一些比較成熟的 WebSocket服務端實現,比如本文使用的Node.js+Socket.IO。為什麼選用這個方案呢?先來簡單介紹下他們兩。

Node.js

Node.js採用C++語言編寫而成,它不是Javascript應用,而是一個Javascript的執行環境,據Node.js創始人 Ryan Dahl回憶,他最初希望採用Ruby來寫Node.js,但是後來發現Ruby虛擬機器的效能不能滿足他的要求,後來他嘗試採用V8引擎,所以選擇了 C++語言。

Node.js支援的系統包括*nux、Windows,這意味著程式設計師可以編寫系統級或者伺服器端的Javascript程式碼,交給 Node.js來解釋執行。Node.js的Web開發框架Express,可以幫助程式設計師快速建立web站點,從2009年誕生至今,Node.js的 成長的速度有目共睹,其發展前景獲得了技術社群的充分肯定。

Socket.IO

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

Socket.IO支援4種協議:WebSocket、htmlfile、xhr-polling、jsonp-polling,它會自動根據瀏覽 器選擇適合的通訊方式,從而讓開發者可以聚焦到功能的實現而不是平臺的相容性,同時Socket.IO具有不錯的穩定性和效能。

編碼實現

安裝Node.js

在此不做過多的說明,可以查一下,網上很多。

搭建WebSocket服務端

在專案根目錄配置一個package.json

{
  "name": "realtime-server",
  "version": "0.0.1",
  "description": "my first realtime server",
  "dependencies": {}
}

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

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

npm包安裝完後,工作目錄下生成了一個名為node_modules的資料夾,裡面分別是expresssocket.io,可能還有一些相關的依賴包

先看一下專案結構:socket_io是服務端的程式碼,public是客戶端的程式碼(博主在客戶端編碼,node_modules在伺服器端,所以下列專案結構沒有


服務端

我們先看一下服務端的編碼(此處需要了解一下express的基礎)

index.js是支援ws通訊的程式碼(服務端程式碼),監聽3000埠(此埠在客戶端指令碼要使用,可以用ip:3000訪問);app.js是搭建客戶端訪問網頁的程式碼,監聽8080埠

index.js

var app = require('express')();
var http = require('http').Server(app);
var io = require('socket.io')(http);
 
app.get('/', function(req, res){
    res.send('<h1>Welcome Realtime Server</h1>');
});
 
//線上使用者
var onlineUsers = {};
//當前線上人數
var onlineCount = 0;
 
io.on('connection', function(socket){
    console.log('a user connected');
     
    //監聽新使用者加入
    socket.on('login', function(obj){
        //將新加入使用者的唯一標識當作socket的名稱,後面退出的時候會用到
        socket.name = obj.userid;
         
        //檢查線上列表,如果不在裡面就加入
        if(!onlineUsers.hasOwnProperty(obj.userid)) {
            onlineUsers[obj.userid] = obj.username;
            //線上人數+1
            onlineCount++;
        }
         
        //向所有客戶端廣播使用者加入
        io.emit('login', {onlineUsers:onlineUsers, onlineCount:onlineCount, user:obj});
        console.log(obj.username+'加入了聊天室');
    });
     
    //監聽使用者退出
    socket.on('disconnect', function(){
        //將退出的使用者從線上列表中刪除
        if(onlineUsers.hasOwnProperty(socket.name)) {
            //退出使用者的資訊
            var obj = {userid:socket.name, username:onlineUsers[socket.name]};
             
            //刪除
            delete onlineUsers[socket.name];
            //線上人數-1
            onlineCount--;
             
            //向所有客戶端廣播使用者退出
            io.emit('logout', {onlineUsers:onlineUsers, 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(3000, function(){
    console.log('listening on *:3000');
});

app.js

const express = require('express')
const path = require('path')
const app = express()
//使用靜態資源訪問,public為根目錄
app.use(express.static(path.join(__dirname, 'public')))

app.listen(8080, () => {
  console.log(`App listening at port 8080`)
})

客戶端

客戶端有三個檔案,index.html是聊天室的html靜態頁面,client.js是客戶端連線ws通訊的js指令碼,socket.io.js是支援socket.io客戶端的js(也有人說在前面npm安裝完socket.io並搭建起WebServer後,這個JS檔案就可以正常訪問了,我當時測試的時候沒有這個檔案,就github下了一個。丟個傳送門https://socket.io/ ;或者使用cdn

<script src="https://cdn.bootcss.com/socket.io/2.0.4/socket.io.js"></script>

index.html

<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8">
    <meta name="format-detection" content="telephone=no"/>
    <meta name="format-detection" content="email=no"/>
    <meta content="width=device-width, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0, user-scalable=0" name="viewport">
    <title>T</title>
    <script src="https://cdn.bootcss.com/socket.io/2.0.4/socket.io.js"></script>
</head>
<body>
<div id="loginbox">
    <div style="width:260px;margin:200px auto;">
        請先輸入你在聊天室的暱稱
        <br/>
        <br/>
        <input type="text" style="width:180px;" placeholder="請輸入使用者名稱" id="username" name="username" />
        <input type="button" style="width:50px;" value="提交" onclick="CHAT.usernameSubmit();"/>
    </div>
</div>
<div id="chatbox" style="display:none;">
    <div style="background:#3d3d3d;height: 28px; width: 100%;font-size:12px;">
        <div style="line-height: 28px;color:#fff;">
            <span style="text-align:left;margin-left:10px;">Websocket多人聊天室</span>
            <span style="float:right; margin-right:10px;"><span id="showusername"></span> |
                    <a href="javascript:;" onclick="CHAT.logout()" style="color:#fff;">退出</a></span>
        </div>
    </div>
    <div id="doc">
        <div id="chat">
            <div id="message" class="message">
                <div id="onlinecount" style="background:#EFEFF4; font-size:12px; margin-top:10px; margin-left:10px; color:#666;">
                </div>
            </div>
            <div class="input-box">
                <div class="input">
                    <input type="text" maxlength="140" placeholder="請輸入聊天內容,按Ctrl提交" id="content" name="content">
                </div>
                <div class="action">
                    <button type="button" id="mjr_send" onclick="CHAT.submit();">提交</button>
                </div>
            </div>
        </div>
    </div>
</div>
<script type="text/javascript" src="./client.js"></script>
</body>
</html>

client.js

(function () {
    var d = document,
        w = window,
        p = parseInt,
        dd = d.documentElement,
        db = d.body,
        dc = d.compatMode == 'CSS1Compat',
        dx = dc ? dd: db,
        ec = encodeURIComponent;


    w.CHAT = {
        msgObj:d.getElementById("message"),
        screenheight:w.innerHeight ? w.innerHeight : dx.clientHeight,
        username:null,
        userid:null,
        socket:null,
        //讓瀏覽器滾動條保持在最低部
        scrollToBottom:function(){
            w.scrollTo(0, this.msgObj.clientHeight);
        },
        //退出,本例只是一個簡單的重新整理
        logout:function(){
            //this.socket.disconnect();
            location.reload();
        },
        //提交聊天訊息內容
        submit:function(){
            var content = d.getElementById("content").value;
            if(content != ''){
                var obj = {
                    userid: this.userid,
                    username: this.username,
                    content: content
                };
                this.socket.emit('message', obj);
                d.getElementById("content").value = '';
            }
            return false;
        },
        genUid:function(){
            return new Date().getTime()+""+Math.floor(Math.random()*899+100);
        },
        //更新系統訊息,本例中在使用者加入、退出的時候呼叫
        updateSysMsg:function(o, action){
            //當前線上使用者列表
            var onlineUsers = o.onlineUsers;
            //當前線上人數
            var onlineCount = o.onlineCount;
            //新加入使用者的資訊
            var user = o.user;

            //更新線上人數
            var userhtml = '';
            var separator = '';
            for(key in onlineUsers) {
                if(onlineUsers.hasOwnProperty(key)){
                    userhtml += separator+onlineUsers[key];
                    separator = '、';
                }
            }
            d.getElementById("onlinecount").innerHTML = '當前共有 '+onlineCount+' 人線上,線上列表:'+userhtml;

            //新增系統訊息
            var html = '';
            html += '<div class="msg-system">';
            html += user.username;
            html += (action == 'login') ? ' 加入了聊天室' : ' 退出了聊天室';
            html += '</div>';
            var section = d.createElement('section');
            section.className = 'system J-mjrlinkWrap J-cutMsg';
            section.innerHTML = html;
            this.msgObj.appendChild(section);
            this.scrollToBottom();
        },
        //第一個介面使用者提交使用者名稱
        usernameSubmit:function(){
            var username = d.getElementById("username").value;
            if(username != ""){
                d.getElementById("username").value = '';
                d.getElementById("loginbox").style.display = 'none';
                d.getElementById("chatbox").style.display = 'block';
                this.init(username);
            }
            return false;
        },
        init:function(username){
            /*
            客戶端根據時間和隨機數生成uid,這樣使得聊天室使用者名稱稱可以重複。
            實際專案中,如果是需要使用者登入,那麼直接採用使用者的uid來做標識就可以
            */
            this.userid = this.genUid();
            this.username = username;

            d.getElementById("showusername").innerHTML = this.username;
            this.msgObj.style.minHeight = (this.screenheight - db.clientHeight + this.msgObj.clientHeight) + "px";
            this.scrollToBottom();

            //連線websocket後端伺服器
            this.socket = io.connect('ws://139.199.163.196:3000/');

            //告訴伺服器端有使用者登入
            this.socket.emit('login', {userid:this.userid, username:this.username});

            //監聽新使用者登入
            this.socket.on('login', function(o){
                CHAT.updateSysMsg(o, 'login');
            });

            //監聽使用者退出
            this.socket.on('logout', function(o){
                CHAT.updateSysMsg(o, 'logout');
            });

            //監聽訊息傳送
            this.socket.on('message', function(obj){
                var isme = (obj.userid == CHAT.userid) ? true : false;
                var contentDiv = '<div>'+obj.content+'</div>';
                var usernameDiv = '<span>'+obj.username+'</span>';

                var section = d.createElement('section');
                if(isme){
                    section.className = 'user';
                    section.innerHTML = contentDiv + usernameDiv;
                } else {
                    section.className = 'service';
                    section.innerHTML = usernameDiv + contentDiv;
                }
                CHAT.msgObj.appendChild(section);
                CHAT.scrollToBottom();
            });

        }
    };
    //通過“回車”提交使用者名稱
    d.getElementById("username").onkeydown = function(e) {
        e = e || event;
        if (e.keyCode === 13) {
            CHAT.usernameSubmit();
        }
    };
    //通過“回車”提交資訊
    d.getElementById("content").onkeydown = function(e) {
        e = e || event;
        if (e.keyCode === 13) {
            CHAT.submit();
        }
    };
})();

到此所有編碼已經完成,放進伺服器後,cd進入專案目錄

node index.js
node app.js

開啟客戶端和服務端的專案,就可以通過域名+埠 或者 ip + 埠訪問了

最終效果圖

(富強 文明 明主 和諧)




相關推薦

基於nodejs + websocket 搭建即時通訊應用

如果說AJAX是像手機發簡訊一樣,傳送資訊,獲取資訊,那麼websocket技術則是打電話這樣。Web領域的實時推送技術,也被稱作Realtime技術。這種技術要達到的目的是讓使用者不需要重新整理瀏覽器就可以獲得實時更新。在以前你可能會使用AJAX進行輪詢,這造成了伺服器的多

基於Openfire Smack開發即時通訊應用搭建Openfire伺服器(一)

一:Openfire是什麼? Openfire 是基於XMPP 協議的IM 的伺服器端的一個實現,雖然當兩個使用者連線後,可以通過點對點的方式來發送訊息,但是使用者還是需要連線到伺服器來獲取一些連線資訊和通訊資訊的,所以伺服器端是必須要實現的。Openf

基於node.js的即時通訊

open 註意 ges size lose 雙屏 ner 事件驅動 發現 最近都在學習HTML5,做canvas遊戲之類的,發現HTML5中除了canvas這個強大的工具外,還有WebSocket也很值得註意。可以用來做雙屏互動遊戲,何為雙屏互動遊戲?就是通過移動端設備來控

Socket搭建即時通訊伺服器

即時通訊 相關程式碼Demo地址, 內附服務端程式碼和iOS端聊天室測試Demo 原文地址: Socket搭建即時通訊伺服器 即時通訊(Instant messaging,簡稱IM)是一個終端服務,允許兩人或多人使用網路即時的傳遞文字訊息、檔案、語音與視訊交流 即時通訊按

php從零搭建即時通訊(一.點對點聊天)

目錄 零.在thinkphp5環境下搭建gatewayWork環境 2.下載完成後解壓到你TP5專案的vendor目錄下即可,如圖 3.點選start_for_win.bat檔案即可成功啟動你的webSocked伺服器,如果你是lin

php從零搭建即時通訊(二.專案架構)

零.序言 經過上面一系列的學習,我們已經學會了gatewaywork的基本使用,下面讓我們來動手將我們這個即時通訊的專案的骨架搭建起來吧 一.資料結構的定義 在上面我們已經說了,客戶端與服務的資料互動,只能是字串, 為了方便我們將那些字串格式化為json 在這

Android WebSocket實現即時通訊/推送

使用java-websocket實現即時通訊/推送模組; 支援即時發訊息和收訊息,訊息型別有開發者自定義;; 該開源專案支援客戶端client和服務端server的配置使用,並提供示例test;   1,Android 客戶端使用需要配置網路許可權; 2,需要寫一個自

php從零搭建即時通訊(一.gatewayWork安裝及使用)

目錄   零.在thinkphp5環境下搭建gatewayWork環境 一.在檢視檔案建立與webSocked伺服器的連線 二.websocked伺服器向客戶端傳送訊息 三.伺服器接受客戶端傳送的訊息 四.客戶端向websocked伺服器傳送訊息 五.客戶端接受

php從零搭建即時通訊(零.專案介紹)

首先我的採用了,gatewayWork搭建長連線,使用tp5進行api及前端頁面的實現,資料庫就順便用個mysql了 實現效果如下 零.即時通訊的原理 即時通訊跟我們平時進行的前後端的互動有什麼不同呢 很簡單,三次握手與長連線的區別 三次握手太常見了就不細談了,我們主要講

瀏覽器上的音訊即時通訊應用開發建議

現在網上很多朋友在弄網頁的視訊聊天室 通過學習,我自己也做了個簡單的小例子,幾十行JavaScript指令碼就能輕鬆實現視訊通話;也不用去下載指定的什麼瀏覽器,因為IE、firefox、chrome等windows平臺主流瀏覽器全部通過,完美執行。下邊就跟大夥分享分享我的成

基於Qt的區域網即時通訊軟體

以前寫的一篇日誌,不小心刪了,竟然沒得恢復。萬惡的CSDN,重排版。 第一個寫的軟體。基本上還是很粗糙,而且程式碼也大多不是自己寫的,不過也著實讓我過了一把癮。接下來簡單地講講整個過程和學到的東西吧。 首先是軟體的框架,自己主觀地YY了很久,因為之前沒有相關的經驗,所以好

openfire+smack4.2.2構建即時通訊應用遇到的各種坑

第一次寫部落格。 發現在搭建伺服器,寫程式碼的過程中等過程中,遇到了許多坑,費了許多時間。如果不記下來,會忘記許多。 正好現在新版smack的資料比較少,查的可費勁了還查不到需要的。 分享給大家。 首先,回顧下流程。 在官網下載openfire,spark,smack。op

Linux下搭建即時通訊聊天服務Tigase 實戰詳解

Mar 27, 2015 11:41:56 AM tigase.conf.ConfiguratorAbstract setProperties Mar 27, 2015 11:41:58 AM tigase.db.jdbc.DataRepositoryImpl initRepository SEVERE: n

iOS之WebSocket即時通訊、實時接受廣播訊息)

在專案中使用到了廣播訊息,例如廣播通知 首先匯入 pod 'SocketRocket', '~> 0.5.0' 引入4個庫 libicucore.dylib,CFNetwork.framework, Security.framework, Fo

Android WebSocket實現即時通訊功能

最近做這個功能,分享一下。即時通訊(Instant Messaging)最重要的毫無疑問就是即時,不能有明顯的延遲,要實現IM的功能其實並不難,目前有很多第三方,比如極光的JMessage,都比較容易實現。但是如果專案有特殊要求(如不能使用外網),那就得自己做了,所以我們需要使用WebSocket。 WebS

[NodeJS]NodeJS基於WebSocket的多用戶點對點即時通訊聊天

round serve i++ conn console 即時通訊 .get () str 最近給一個客戶做了一個聊天,所以就用NodeJS做了一個 原理就是用戶第一次進入後,記錄它的ID和該用戶的ws 等有人發數據的時候,就去查ID,然後找到ID對應的ws,進行消息發

Nodejs腳手架搭建基於express的應用

gen str 記錄 orm sta epo port XP strong 原文鏈接:https://www.cnblogs.com/FE-yanyi1993/p/6413042.html 這篇寫的非常詳細,此處只做記錄。 1.安裝生成器 $ npm install

使用 Django WebSocket Redis 搭建線上即時通訊工具

話不多說先上效果圖演示   專案:http://112.74.164.107:9990/ 1、安裝組建 redis: yum install redis/apt install redis 2、建立虛擬化環境並進入 python3/python -m venv

[原始碼和文件分享]使用同一資料庫基於TCP Socket和Websocket實現的相互即時通訊系統

摘 要 隨著網路通訊和計算機技術的發展,人們越來越希望能夠即時傳送和接收網際網路訊息。與此同時隨著網際網路的發展在HTML5中提出了websocket協議,能更好的節省伺服器資源和頻寬並且伺服器和瀏覽器能夠雙向實時通訊。為了能讓使用者體驗傳統客戶端和web帶來的即時通訊結合的超爽體驗,本次畢業設

NodeJs 實現 WebSocket 即時通訊(簡單版)

    服務端程式碼 var ws = require("nodejs-websocket"); console.log("開始建立連線...") var game1 = null,game2 = null , game1Ready = false , game2Read