菜鸟|搭建WebSocket简易聊天室

Posted 白鹭引擎

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了菜鸟|搭建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

安装完成之后,使用终端工具进入服务器目录,开始编写代码:

 
   
   
 
  1. //引入ws模块

  2. var WebSocket = require('ws');

  3. //创建websocket服务,端口port为:****

  4. var WebSocketServer = WebSocket.Server,

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

  6. //引入uuid模块

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

  8. //定义一个空数组,存放客户端的信息

  9. var clients = [];

  10. //定义发送消息方法wsSend

  11. //参数为 type:类型

  12. //client_uuid:随机生成的客户端id

  13. //nickname:昵称

  14. //message:消息

  15. //clientcount:客户端个数

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

  17.    //遍历客户端

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

  19.      //声明客户端

  20.    var clientSocket = clients[i].ws;

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

  22.        //客户端发送处理过的信息

  23.      clientSocket.send(JSON.stringify({

  24.        "type": type,

  25.        "id": client_uuid,

  26.        "nickname": nickname,

  27.        "message": message,

  28.        "clientcount":clientcount,

  29.      }));

  30.    }

  31.  }

  32. }

  33. //声明客户端index默认为1

  34. var clientIndex = 1;

  35. //服务端连接

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

  37. //客户端client_uuid随机生成

  38.  var client_uuid = uuid.v4();

  39.  //昵称为游客+客户端index

  40.  var nickname = "游客"+clientIndex;

  41.  //client++

  42.  clientIndex+=1;

  43.  //将新连接的客户端push到clients数组中

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

  45.  //控制台打印连接的client_uuid

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

  47. //声明连接信息为 昵称+来了

  48.  // var connect_message = nickname + " 来了";

  49.  var connect_message =  " 来了";

  50.  //服务器广播信息 ***来了

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

  52. //当用户发送消息时

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

  54.      // 用户输入"/nick"的话为重命名消息

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

  56.      var nickname_array = message.split(' ');

  57.      if(nickname_array.length >= 2) {

  58.        var old_nickname = nickname;

  59.        nickname = nickname_array[1];

  60.        var nickname_message = "用户 " + old_nickname + " 改名为: " + nickname;

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

  62.      }

  63.    }//发送消息

  64.    else {

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

  66.    }

  67.  });

  68. //关闭socket连接时

  69.  var closeSocket = function(customMessage) {

  70.      //遍历客户端

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

  72.        //如果客户端存在

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

  74.            // 声明离开信息

  75.            var disconnect_message;

  76.            if(customMessage) {

  77.                disconnect_message = customMessage;

  78.            } else {

  79.                disconnect_message = nickname + " 走了";

  80.            }

  81.         //客户端数组中删掉

  82.          clients.splice(i, 1);

  83.          //服务广播消息

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

  85.        }

  86.    }

  87.  }

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

  89.      closeSocket();

  90.  });

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

  92.      console.log("Closing things");

  93.      closeSocket('Server has disconnected');

  94.      process.exit();

  95.  });

  96. });

服务器端主要是接收信息,判断是聊天信息还是重命名信息,然后发送广播。同时,当用户连接上服务器端或者关闭连接时,服务器也会发送广播通知其他用户。

我们封装了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对象,并执行服务器连接操作,代码如下:

 
   
   
 
  1.    public ws;

  2.    private init() {

  3.        /**WebSocket连接 */

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

  5.        this.ws.onopen = function (e) {

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

  7.        }

  8.    }

由于服务器开放了8180端口,我们也需要使用8180端口进行连接。当连接成功,可执行onopen方法。

服务器连接成功了,在控制台打印 'Connection to server opened'。

onmessage方法中读取服务器传递过来的数据,并通过appendLog方法将数据显示在对应的文本里,

使用newLabel方法并将一条新消息插入到消息框中。

 
   
   
 
  1.    private init() {

  2.        /**WebSocket连接 */

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

  4.        this.ws.onopen = function (e) {

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

  6.        }

  7.        /**昵称 */

  8.        var nickname;

  9.        var self = this;

  10.        this.ws.onmessage = function (e) {

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

  12.            nickname = data.nickname;

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

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

  15.            //插入消息

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

  17.        }

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

  19.            console.log(clientcount)

  20.            /**聊天信息 */

  21.            var messages = this.list_msg;

  22.            /**提示 */

  23.            var preface_label;

  24.            if (type === 'notification') {

  25.                preface_label = "提示:";

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

  27.                preface_label = "警告:";

  28.            } else {

  29.                preface_label = nickname;

  30.            }

  31.            self.preface_label = preface_label;

  32.            var message_text = self.message_text = message;

  33.            /**在线人数 */

  34.            self.lb_online.text = clientcount;

  35.        }

  36.        /**点击OK发送 */

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

  38.    }

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

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

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

  42.          label1.textColor = 0x000000

  43.          return label1;

  44.    }

最后我们来编写发送消息的函数,在btnok中egret.TouchEvent.TOUCHTAP点击之后的相应函数为sendMessage方法。

 
   
   
 
  1.    /**发送消息 */

  2.    private sendMessage() {

  3.        var message = this.input_msg.text;

  4.        if (message.length < 1) {

  5.            // console.log("不能发送空内容!");

  6.            return;

  7.        }

  8.        this.ws.send(message);

  9.        /**清空输入框内容 */

  10.        this.input_msg.text = "";

  11.    }

如果输入框中内容不为空的话就将数据通过 this.ws.send(message); 发送给服务器,并清除输入框的内容。

最终运行后,我们就可以实现多人在线聊天功能了。

完整版代码如下:

 
   
   
 
  1. class Chat extends eui.Component implements eui.UIComponent {

  2.    /**在线人数文本 */

  3.    public lb_online: eui.Label;

  4.    /**聊天窗口 */

  5.    public scr_msg: eui.Scroller;

  6.    /**聊天信息 */

  7.    public list_msg: eui.List;

  8.    /**输入框 */

  9.    public input_msg: eui.EditableText;

  10.    /**确定按钮 */

  11.    public btn_ok: eui.Button;

  12.    /**聊天窗口消息组 */

  13.    public group_msg: eui.Group;

  14.    public constructor() {

  15.        super();

  16.    }

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

  18.        super.partAdded(partName, instance);

  19.    }

  20.    protected childrenCreated(): void {

  21.        this.init();

  22.        super.childrenCreated();

  23.    }

  24.    /**WebSocket */

  25.    public ws;

  26.    public preface_label;

  27.    public message_text;

  28.    private init() {

  29.        /**WebSocket连接 */

  30.         //线上测试链接,服务端代码需在服务器启动

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

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

  33.        this.ws.onopen = function (e) {

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

  35.        }

  36.        /**昵称 */

  37.        var nickname;

  38.        var self = this;

  39.        this.ws.onmessage = function (e) {

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

  41.            nickname = data.nickname;

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

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

  44.            //插入消息

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

  46.        }

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

  48.            console.log(clientcount)

  49.            /**聊天信息 */

  50.            var messages = this.list_msg;

  51.            /**提示 */

  52.            var preface_label;

  53.            if (type === 'notification') {

  54.                preface_label = "提示:";

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

  56.                preface_label = "警告:";

  57.            } else {

  58.                preface_label = nickname;

  59.            }

  60.            self.preface_label = preface_label;

  61.            var message_text = self.message_text = message;

  62.            /**在线人数 */

  63.            self.lb_online.text = clientcount;

  64.        }

  65.        /**点击OK发送 */

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

  67.    }

  68.    /**发送消息 */

  69.    private sendMessage() {

  70.        var message = this.input_msg.text;

  71.        if (message.length < 1) {

  72.            // console.log("不能发送空内容!");

  73.            return;

  74.        }

  75.        this.ws.send(message);

  76.        /**清空输入框内容 */

  77.        this.input_msg.text = "";

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

  79.    }

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

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

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

  83.        label1.textColor = 0x000000

  84.        return label1;

  85.    }

  86. }

本文的demo增加了客户端与服务器的互动,同时也实现了客户端之间的联系。



更多初级开发文档



 





联系我们

开发者技术交流区:bbs.egret.com

合作开放平台:open.egret.com

商务合作:bd@egret.com

招聘邮箱:hr@egret.com

产品合作QQ:248843012

 渠道合作QQ:2669903485



以上是关于菜鸟|搭建WebSocket简易聊天室的主要内容,如果未能解决你的问题,请参考以下文章

websocket搭建简易的聊天室--勿喷

swoole用WebSocket服务器搭建一个简易的聊天室功能

swoole用WebSocket服务器搭建一个简易的聊天室功能

php+websocket搭建简易聊天室实践

搭建Websocket简易聊天室

nodejs构建多房间简易聊天室