本文, 我们通过 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 增加了客户端与服务器的互动, 同时也实现了客户端之间的联系.
来源: http://blog.51cto.com/11960887/2173213