搭建WebSocket簡易聊天室

寫在前面:隨着越來越多的新人開始接觸白鷺引擎,創作屬於自己的遊戲。考慮到初學者會遇到一些實際操作問題,我們近期整理推出“菜鳥”系列技術文檔,以便更好的讓這些開發者們快速上手,Egret大神們可以自動忽略此類內容。

本文,我們通過Egret和Node.js實現一個在線聊天室的demo。主要包括:聊天,改用戶名,查看其他用戶在線狀態的功能。大致流程爲,用戶訪問網頁,即進入聊天狀態,成爲新遊客,通過底部的輸入框,可以輸入自己想說的話,點擊發布,信息呈現給所有在聊天的人的頁面。用戶可以實時修改自己的暱稱,用戶離線上線都會實時廣播給其他用戶。

體驗鏈接 http://7hds.com:8888/

下圖爲最終制作完成的聊天面板

WebSocket服務器可以用其他語言編寫,本文采用的方法建立在Node.js上 。

在Node.js中我們使用ws第三方模塊來實現服務器業務邏輯的快速搭建,還需使用uuid模塊生成隨機id,你需要使用npm包管理器來安裝ws、uuid模塊。使用以下命令:

  1. npm install ws -g
  2. npm install uuid -g

安裝完成之後,使用終端工具進入服務器目錄,開始編寫代碼:

//引入ws模塊

var WebSocket = require('ws');

//創建websocket服務,端口port爲:****

var WebSocketServer = WebSocket.Server,

    wss = new WebSocketServer({ port: 8180 });

//引入uuid模塊

var uuid = require('node-uuid');

//定義一個空數組,存放客戶端的信息

var clients = [];

//定義發送消息方法wsSend

//參數爲 type:類型

//client_uuid:隨機生成的客戶端id

//nickname:暱稱

//message:消息

//clientcount:客戶端個數

function wsSend(type, client_uuid, nickname, message, clientcount) {

    //遍歷客戶端

    for (var i = 0; i < clients.length; i++) {

        //聲明客戶端

        var clientSocket = clients[i].ws;

        if (clientSocket.readyState === WebSocket.OPEN) {

            //客戶端發送處理過的信息

            clientSocket.send(JSON.stringify({

                "type": type,

                "id": client_uuid,

                "nickname": nickname,

                "message": message,

                "clientcount": clientcount,

            }));

        }

    }

}

//聲明客戶端index默認爲1

var clientIndex = 1;

//服務端連接

wss.on('connection', function (ws) {

    //客戶端client_uuid隨機生成

    var client_uuid = uuid.v4();

    //暱稱爲遊客+客戶端index

    var nickname = "遊客" + clientIndex;

    //client++

    clientIndex += 1;

    //將新連接的客戶端push到clients數組中

    clients.push({ "id": client_uuid, "ws": ws, "nickname": nickname });

    //控制檯打印連接的client_uuid

    console.log('client [%s] connected', client_uuid);

    //聲明連接信息爲 暱稱+來了

    // var connect_message = nickname + " 來了";

    var connect_message = " 來了";

    //服務器廣播信息 ***來了

    wsSend("notification", client_uuid, nickname, connect_message, clients.length);

    //當用戶發送消息時

    ws.on('message', function (message) {

        // 用戶輸入"/nick"的話爲重命名消息

        if (message.indexOf('/nick') === 0) {

            var nickname_array = message.split(' ');

            if (nickname_array.length >= 2) {

                var old_nickname = nickname;

                nickname = nickname_array[1];

                var nickname_message = "用戶 " + old_nickname + " 改名爲: " + nickname;

                wsSend("nick_update", client_uuid, nickname, nickname_message, clients.length);

            }

        }//發送消息

        else {

            wsSend("message", client_uuid, nickname, message, clients.length);

        }

    });

    //關閉socket連接時

    var closeSocket = function (customMessage) {

        //遍歷客戶端

        for (var i = 0; i < clients.length; i++) {

            //如果客戶端存在

            if (clients[i].id == client_uuid) {

                // 聲明離開信息

                var disconnect_message;

                if (customMessage) {

                    disconnect_message = customMessage;

                } else {

                    disconnect_message = nickname + " 走了";

                }

                //客戶端數組中刪掉

                clients.splice(i, 1);

                //服務廣播消息

                wsSend("notification", client_uuid, nickname, disconnect_message, clients.length);

            }

        }

    }

    ws.on('close', function () {

        closeSocket();

    });

    process.on('SIGINT', function () {

        console.log("Closing things");

        closeSocket('Server has disconnected');

        process.exit();

    });

});

服務器端主要是接收信息,判斷是聊天信息還是重命名信息,然後發送廣播。同時,當用戶連接上服務器端或者關閉連接時,服務器也會發送廣播通知其他用戶。

我們封裝了wsSend函數用來處理消息的廣播。對每個連接的用戶,我們默認給他分配爲遊客。爲了實現廣播,我們用clients數組來保存連接的用戶。

將編寫好的文件保存爲server.js,在終端工具中,使用node server.js來啓動你剛剛編寫的服務器。如果終端沒有報錯,證明你的代碼已經正常運行。

在實際項目中,服務器邏輯遠遠比此示例複雜得多。服務器端完成後,再來編寫客戶端代碼。

界面非常簡單,我們通過兩張圖片來實現界面效果,首先創建我們的聊天界面,此項目中爲了方便我們使用EUI進行快速開發。如下圖:

首先創建一個Image來放置我們的背景圖。

創建三個Label對象,一個作爲title:“多人在線聊天室”,一個作爲提示:“當前在線人數”,還有一個id爲lb_online的作爲在線人數顯示文本。

創建一個EditableText對象id爲input_msg作爲消息發送輸入框,用戶可以在此輸入消息進行發送。

創建一個Button對象id爲btn_ok,點擊按鈕可以執行發送消息動作。

創建界面的操作和WebSocket對象創建動作在同時進行,在init方法中創建WebSocket對象,並執行服務器連接操作,代碼如下:

public ws;

private init() {

    /**WebSocket連接 */

    this.ws = new WebSocket('ws://127.0.01:8180');

    this.ws.onopen = function (e) {

        console.log('Connection to server opened');

    }

}

由於服務器開放了8180端口,我們也需要使用8180端口進行連接。當連接成功,可執行onopen方法。

服務器連接成功了,在控制檯打印 'Connection to server opened'。

onmessage方法中讀取服務器傳遞過來的數據,並通過appendLog方法將數據顯示在對應的文本里,

使用newLabel方法並將一條新消息插入到消息框中。

private init() {

    /**WebSocket連接 */

    this.ws = new WebSocket('ws://127.0.01:8180');

    this.ws.onopen = function (e) {

        console.log('Connection to server opened');

    }

    /**暱稱 */

    var nickname;

    var self = this;

    this.ws.onmessage = function (e) {

        var data = JSON.parse(e.data);

        nickname = data.nickname;

        appendLog(data.type, data.nickname, data.message, data.clientcount);

        console.log("ID: [%s] = %s", data.id, data.message);

        //插入消息

        self.group_msg.addChild(self.newLabel(data.nickname, data.message))

    }

    function appendLog(type, nickname, message, clientcount) {

        console.log(clientcount)

        /**聊天信息 */

        var messages = this.list_msg;

        /**提示 */

        var preface_label;

        if (type === 'notification') {

            preface_label = "提示:";

        } else if (type === 'nick_update') {

            preface_label = "警告:";

        } else {

            preface_label = nickname;

        }

        self.preface_label = preface_label;

        var message_text = self.message_text = message;

        /**在線人數 */

        self.lb_online.text = clientcount;

    }

    /**點擊OK發送 */

    this.btn_ok.addEventListener(egret.TouchEvent.TOUCH_TAP, this.sendMessage, this);

}

    private newLabel(name: string, msg: string) {

    var label1: eui.Label = new eui.Label();

    label1.text = name + ":" + msg;

    label1.textColor = 0x000000

    return label1;

}

最後我們來編寫發送消息的函數,在btnok中egret.TouchEvent.TOUCHTAP點擊之後的相應函數爲sendMessage方法。

/**發送消息 */

private sendMessage() {

    var message = this.input_msg.text;

    if (message.length < 1) {

    // console.log("不能發送空內容!");

    return;

    }

    this.ws.send(message);

    /**清空輸入框內容 */

    this.input_msg.text = "";

    }

如果輸入框中內容不爲空的話就將數據通過 this.ws.send(message); 發送給服務器,並清除輸入框的內容。

最終運行後,我們就可以實現多人在線聊天功能了。

完整版代碼如下:

class Chat extends eui.Component implements eui.UIComponent {

    /**在線人數文本 */

    public lb_online: eui.Label;

    /**聊天窗口 */

    public scr_msg: eui.Scroller;

    /**聊天信息 */

    public list_msg: eui.List;

    /**輸入框 */

    public input_msg: eui.EditableText;

    /**確定按鈕 */

    public btn_ok: eui.Button;

    /**聊天窗口消息組 */

    public group_msg: eui.Group;

    public constructor() {

        super();

    }

    protected partAdded(partName: string, instance: any): void {

        super.partAdded(partName, instance);

    }

    protected childrenCreated(): void {

        this.init();

        super.childrenCreated();

    }

    /**WebSocket */

    public ws;

    public preface_label;

    public message_text;

    private init() {

        /**WebSocket連接 */

        //線上測試鏈接,服務端代碼需在服務器啓動

        //this.ws = new WebSocket('ws://7hds.com:8180');

        this.ws = new WebSocket('ws://127.0.01:8180');

        this.ws.onopen = function (e) {

            console.log('Connection to server opened');

        }

        /**暱稱 */

        var nickname;

        var self = this;

        this.ws.onmessage = function (e) {

            var data = JSON.parse(e.data);

            nickname = data.nickname;

            appendLog(data.type, data.nickname, data.message, data.clientcount);

            console.log("ID: [%s] = %s", data.id, data.message);

            //插入消息

            self.group_msg.addChild(self.newLabel(data.nickname, data.message))

        }

        function appendLog(type, nickname, message, clientcount) {

            console.log(clientcount)

            /**聊天信息 */

            var messages = this.list_msg;

            /**提示 */

            var preface_label;

            if (type === 'notification') {

                preface_label = "提示:";

            } else if (type === 'nick_update') {

                preface_label = "警告:";

            } else {

                preface_label = nickname;

            }

            self.preface_label = preface_label;

            var message_text = self.message_text = message;

            /**在線人數 */

            self.lb_online.text = clientcount;

        }

        /**點擊OK發送 */

        this.btn_ok.addEventListener(egret.TouchEvent.TOUCH_TAP, this.sendMessage, this);

    }

    /**發送消息 */

    private sendMessage() {

        var message = this.input_msg.text;

        if (message.length < 1) {

            // console.log("不能發送空內容!");

            return;

        }

        this.ws.send(message);

        /**清空輸入框內容 */

        this.input_msg.text = "";

        // console.log(this.ws.bufferedAmount);

    }

    private newLabel(name: string, msg: string) {

        var label1: eui.Label = new eui.Label();

        label1.text = name + ":" + msg;

        label1.textColor = 0x000000

        return label1;

    }

}

本文的demo增加了客戶端與服務器的互動,同時也實現了客戶端之間的聯繫。

轉自https://www.sohu.com/a/252528284_609455

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