zoukankan      html  css  js  c++  java
  • 《菜鸟教程》| Egret搭建WebSocket简易聊天室

    本文,我们通过Egret和Node.js实现一个在线聊天室的demo。主要包括,聊天,改用户名,查看其他用户在线状态的功能。大致流程为,用户访问网页,即进入聊天状态,成为新游客,通过底部的输入框,可以输入自己想说的话,点击发布,信息呈现给所有在聊天的人的页面。用户可以实时修改自己的昵称,用户离线上线都会实时广播给其他用户。

    体验链接 http://7hds.com:8888/

    下图为最终制作完成的聊天面板

     

    WebSocket服务器可以用其他语言编写,本文采用的方法建立在Node.js上 。

    在Node.js中我们使用ws第三方模块来实现服务器业务逻辑的快速搭建,还需使用uuid模块生成随机id,你需要使用npm包管理器来安装ws、uuid模块。使用以下命令:

    npm install ws -g
    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;
        }

    最后我们来编写发送消息的函数,在btn_ok中egret.TouchEvent.TOUCH_TAP点击之后的相应函数为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增加了客户端与服务器的互动,同时也实现了客户端之间的联系。

     

     

  • 相关阅读:
    Java实现 LeetCode 50 Pow(x,n)
    Java实现 LeetCode 50 Pow(x,n)
    Java实现 LeetCode 49 字母异位词分组
    Java实现 LeetCode 49 字母异位词分组
    Java实现 LeetCode 49 字母异位词分组
    Java实现 LeetCode 48 旋转图像
    Java实现 LeetCode 48 旋转图像
    Java实现 LeetCode 48 旋转图像
    Java实现 LeetCode 47 全排列 II(二)
    Java实现 LeetCode 47 全排列 II(二)
  • 原文地址:https://www.cnblogs.com/egret-edcation/p/9627608.html
Copyright © 2011-2022 走看看