在线聊天室的实现(3)--简易聊天室的实现

前言:
  就如前文所讲述的, 聊天室往往是最基本的网络编程的学习案例. 本文以WebSocket为底层协议, 实现一个简单的聊天室服务.
  服务器采用Netty 4.x来实现, 源于其对websocket的超强支持, 基于卓越的性能和稳定.
  本系列的文章链接如下:
  1). websocket协议和javascript版的api 
  2). 基于Netty 4.x的Echo服务器实现

初步构想:
  本文对聊天室服务的定位还是比较简单. 只需要有简单的账户体系, 能够实现简单的群聊功能即可.
  流程设计初稿:
  1). 用户登陆
  
  2). 群聊界面
  
  这里没有聊天室的选择, 只有唯一的一个. 这边有没有表情支持, 内容过滤. 一切皆从简.

协议约定:
  在websocket协议的基础之上, 我们引入应用层的聊天协议.
  协议以JSON作为数据交互格式, 并进行扩展和阐述.
  • 请求形态约定

request: {cmd:"$cmd", params: {}} 

  • 响应形态约定

response: {cmd:"$cmd", retcode:$retcode, datas:{}}	// retcode => 0: success, 1: fail

  
  1). 用户登陆请求:

  request: {cmd:"login", params: {username:"$username"}}
  response:
    成功: {cmd:"login", retcode: 0, datas:{username:$username, userid:$userid}}
    失败: {cmd:"login", retcode:1, datas:{}}

  注: 成功后, 返回分配的userid(全局唯一).
  2). 消息发送请求:

  request: {cmd:"send_message", params:{message:$message, messageid:$messageid}}
  response:
    成功: {cmd:"send_message", retcode:0, datas:{messageid:$messageid}}
    失败: {cmd:"send_message", retcode:1, datas:{messageid:$messageid}}	

  注: 这边的messageid是必须的, 可以提示那条消息成功还是失败
  3). 消息接受(OnReceive):

  刷新用户列表
    {cmd:"userlist", retcode:0, datas: {users: [{userid:$userid, username:$username}, {userid:$userid, username:$username}]}}
  接收消息
    {cmd:"receive_message", retcode:0, datas: {message:"$message", username:"$username", userid:"$userid", timestamp:"$timestamp"}}

服务端实现:
  借助Netty 4.x来构建服务器端, 其Channel的pipeline设定如下:

serverBootstrap.group(bossGroup, workerGroup)
  .channel(NioServerSocketChannel.class)
  .childHandler(new ChannelInitializer<SocketChannel>() {
    @Override
    protected void initChannel(SocketChannel socketChannel) throws Exception {
      ChannelPipeline cp = socketChannel.pipeline();
      // *) 支持http协议的解析
      cp.addLast(new HttpServerCodec());
      cp.addLast(new HttpObjectAggregator(65535));
      // *) 对于大文件支持 chunked方式写
      cp.addLast(new ChunkedWriteHandler());
      // *) 对websocket协议的处理--握手处理, ping/pong心跳, 关闭
      cp.addLast(new WebSocketServerProtocolHandler("/chatserver"));
      // *) 对TextWebSocketFrame的处理
      cp.addLast(new ChatLogicHandler(userGroupManager));
    }
  });

  注: HttpServerCodec / HttpObjectAggregator / ChunkedWriteHandler / WebSocketServerProtocolHandler, 依次引入极大简化了websocket的服务编写.
  ChatLogicHandler的定义, 其对TextWebSocketFrame进行解析处理, 并从中提取聊天协议的请求, 并进行相应的状态处理.

@Override
protected void channelRead0(ChannelHandlerContext ctx, TextWebSocketFrame twsf) throws Exception {
  String text = twsf.text();
  JSONObject json = JSON.parseObject(text);

  if ( !json.containsKey("cmd") || !json.containsKey("params") ) {
    ctx.close();
    return;
  }

  String cmd = json.getString("cmd");
  if ( "login".equalsIgnoreCase(cmd) ) {
    // *) 处理登陆事件
    handleLoginEvent(ctx, json.getJSONObject("params"));
  } else if ( "send_message".equalsIgnoreCase(cmd) ) {
    // *) 处理群聊消息事件
    handleMessageEvent(ctx, json.getJSONObject("params"));
  }
}

  其实这边还可以再加一层, 用于聊天协议的请求/响应的封装, 这样结构上更清晰. 但为了简单起见, 就省略了.

客户端的实现:
  web客户端最重要的还是, 对chatclient的封装. 其封装了websocket了, 实现了业务上的login和sendmessage函数.

(function(window) {

  ChatEvent.LOGIN_ACK = 1001;
  ChatEvent.SEND_MESSAGE_ACK = 1002;
  ChatEvent.USERLIST = 2001;
  ChatEvent.RECEIVE_MESSAGE = 2002;
  ChatEvent.UNEXPECT_ERROR = 10001;

  function ChatEvent(type, retcode, msg) {
    this.type = type;
    this.retcode = retcode; // retcode : 0 => success, 1 => fail
    this.msg = msg;
  }

  WebChatClient.UNCONNECTED = 0;
  WebChatClient.CONNECTED = 1;
  WebChatClient.LOGINED = 1;

  function WebChatClient() {
    this.websocket = null;
    this.state = WebChatClient.UNCONNECTED;
    this.chatEventListener = null;
  }

  WebChatClient.prototype.init = function(wsUrl, chatEventListener) {
    this.state = WebChatClient.UNCONNECTED;
    this.chatEventListener = chatEventListener;
    this.websocket = new WebSocket(wsUrl); //创建WebSocket对象
    var self = this;
    this.websocket.onopen = function(evt) {
      self.state = WebChatClient.CONNECTED;
    }
    this.websocket.onmessage = function(evt) {
      var res = JSON.parse(evt.data);
      if ( !res.hasOwnProperty("cmd") || !res.hasOwnProperty("retcode") || !res.hasOwnProperty("datas") ) {
        self.chatEventListener(new ChatEvent(ChatEvent.UNEXPECT_ERROR, 0));
      } else {
        var cmd = res["cmd"];
        var retcode = res["retcode"];
        var datas = res["datas"];
        switch(cmd) {
          case "login":
            self.chatEventListener(new ChatEvent(ChatEvent.LOGIN_ACK, retcode, datas));
            break;
          case "send_message":
            self.chatEventListener(new ChatEvent(ChatEvent.SEND_MESSAGE_ACK, retcode, datas));
            break;
          case "userlist":
            self.chatEventListener(new ChatEvent(ChatEvent.USERLIST, retcode, datas));
            break;
          case "receive_message":
            self.chatEventListener(new ChatEvent(ChatEvent.RECEIVE_MESSAGE, retcode, datas));
            break;
        }
      }
    }
    this.websocket.onerror = function(evt) {
    }
  }

  WebChatClient.prototype.login = function(username) {
    if ( this.websocket.readyState == WebSocket.OPEN && this.state == WebChatClient.CONNECTED ) {
      var msgdata = JSON.stringify({cmd:"login", params:{username:username}});
      this.websocket.send(msgdata);
    }
  }

  WebChatClient.prototype.sendMessage = function(message) {
    if ( this.websocket.readyState == WebSocket.OPEN && this.state == WebChatClient.LOGINED ) {
      var msgdata = JSON.stringify({cmd:"send_message", params:{message:message}});
      this.websocket.send(msgdata);
    }
  }

  // export
  window.ChatEvent = ChatEvent;
  window.WebChatClient = WebChatClient;

})(window);

代码下载:
  由于采用websocket作为底层网络通讯协议, 因此需要浏览器支持(最好为Chrome).
  服务器和web客户端的源码下载地址为: http://pan.baidu.com/s/1pJDYo3p.
  文件的目录结构如下:
  

后期展望:
  编写完初步版本后, 一群有爱的小伙伴们帮我友情测试了一把. 中间反馈了很多体验上的改进意见. 比如Enter键自动发送, 最新留言与页面底部同步, 自己名称高亮显示.
  也有反馈添加表情等高大上的功能, ^_^. 总之感觉棒棒哒, 觉得再做一件很伟大的事情. oh yeah.

写在最后:
  
如果你觉得这篇文章对你有帮助, 请小小打赏下. 其实我想试试, 看看写博客能否给自己带来一点小小的收益. 无论多少, 都是对楼主一种由衷的肯定.

  

时间: 2024-10-09 09:59:11

在线聊天室的实现(3)--简易聊天室的实现的相关文章

PHP制作简易聊天程序

近几天来,突然在查阅PHP中文手册时,看到了用PHP制作聊天室的示例,于是一时心血来潮也用PHP写了一个简易的WEB聊天室应用,在写聊天室的过程中,我遇到的问题其实不算很多,因为用php制作聊天室的原理是非常简单的,也许这与我自身思考问题的方式有关,我往往喜欢把任何看似复杂的东西简单化,我相信表面复杂的事物,其实都蕴含着简易原理,我的这种思维模式在这次WEB聊天应用的制作中起到了作用,下面我就将PHP制作WEB聊天室的原理给大家做一个介绍: 首先我们要建立一个WEB框架,这个框架也就是利用HTM

基于C/S模式的简易聊天室

一.任务简要描述 移动互联网技术的广泛应用为人们提供了非常便捷的沟通方式.QQ.微信和微博等是便携式聊天系统的典型代表,它们的功能非常强大. 本系统利用TCP/IP协议的Socket和ServerSocket类,实现基于C/S模式的简易聊天室.该聊天室包括服务端和客户端两部分,服务端是客户端发送消息的中转站:客户端之间可以直接通信,也可以与服务器通信.聊天结束后客户端断开与服务端的连接,服务器也可以停止信息中转服务. 二.系统需求分析 本系统采用C/S软件架构,服务器端负责监听客户端发来的消息,

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

1.前言 公司游戏里面有个简单的聊天室,了解了之后才知道是node+websocket做的,想想php也来做个简单的聊天室.于是搜集各种资料看文档.找实例自己也写了个简单的聊天室. http连接分为短连接和长连接.短连接一般可以用ajax实现,长连接就是websocket.短连接实现起来比较简单,但是太过于消耗资源.websocket高效不过兼容存在点问题.websocket是html5的资源 如果想要详细了解websocket长连接的原理请看https://www.zhihu.com/ques

《基于Node.js实现简易聊天室系列之详细设计》

一个完整的项目基本分为三个部分:前端.后台和数据库.依照软件工程的理论知识,应该依次按照以下几个步骤:需求分析.概要设计.详细设计.编码.测试等.由于缺乏相关知识的储备,导致这个Demo系列的文章层次不是很清楚,索性这一章将所有的过程(前后端以及数据库)做一个介绍,下一章写完总结就OK了吧. (1)前端部分 涉及到的技术:html.css.bootstrap.jquery.jquery UI 登录/注册界面使用的是bootstrap响应式布局,即支持不同尺寸的客户端,以此提高用户的体验.在这之前

php_3_“简易聊天室 ”实现的关键技术 详解

                  PHP+MySQL实现Internet上一个简易聊天室的关键技术  系统目标: 聊天室使用数据库汇集每个人的发言,并可将数据库内的发言信息显示在页面,让每个用户都可以看到,具体功能如下: a.用户登录:用户发言时显示其登录名信息 b.用户发言:用户输入说的话 c.显示发言信息:用户浏览所有发言信息 设计思路: (1).建立聊天室数据库及相关数据表 (2).实现用户登录页面(login.php) (3).实现发言页面(speak.php) (4).实现发言显示页

Socket编程(简易聊天室客户端/服务器编写、CocoaAsyncSocket)

Socket编程(简易聊天室客户端/服务器编写.CocoaAsyncSocket) 一.Socket 1.1 Socket简介 Socket就是为网络服务提供的一种机制.网络通信其实就是Socket间的通信,通信的两端都是Socket,数据在两个Socket间通过IO传输. 在Web服务大行其道的今天,调用Web服务的代价是高昂的,尤其是仅仅是抓取少量数据的时候尤其如此.而使用Socket,可以只传送数据本身而不用进行XML封装,大大降低数据传输的开销.Socket允许使用长连接,允许应用程序运

在线聊天室的实现(4)--分布式聊天室的基础架构

前言: 前面都在讲述如何实现一个简单的聊天室, 并回顾了websocket的协议, 以及Netty 4.x的简单使用. 但如果仅局限于单机的聊天室实现, 那显然难登"大雅之堂". 借这个机会, 想尝试聊一下千万级聊天室的实现. 同时浅谈一下游戏中, 公共的聊天室资源服务定位. 本系列的文章链接如下: 1). websocket协议和javascript版的api 2). 基于Netty 4.x的Echo服务器实现  3). 简易聊天室的实现 架构演进: 这边讲述一下聊天室服务的思考过程

socket.io入门,简易聊天室

介绍 通常我们web使用的是http协议,但是 HTTP 协议有一个缺陷:通信只能由客户端发起. 所以我们需要一个可以由服务端主动发出的协议,即WebSocket. WebSocket是HTML5新增的一种通信协议,其特点是服务端可以主动向客户端推送信息,客户端也可以主动向服务端发送信息,是真正的双向平等对话,属于服务器推送技术的一种. Socket.IO 是一个基于 Node.js 的实时应用程序框架,在即时通讯.通知与消息推送,实时分析等场景中有较为广泛的应用. socket.io 包含两个

docker搭建swoole简易聊天室

docker搭建swoole的简易聊天室 首先pull镜像 docker pull docker.io/kong36088/nginx-php7-swoole 创建容器 docker run --name {自己创建的名字} -p 9501:9501 -p 8089:80 -d -it kong36088/nginx-php7-swoole /bin/bash 进入容器 docker exec -it {容器名字或id} /bin/bash 进入容器之后进入nginx配置文件 cd /etc/n