zoukankan      html  css  js  c++  java
  • Netty学习笔记(六) 简单的聊天室功能之WebSocket客户端开发实例

    在之前的Netty相关学习笔记中,学习了如何去实现聊天室的服务段,这里我们来实现聊天室的客户端,聊天室的客户端使用的是Html5和WebSocket实现,下面我们继续学习.

    创建客户端

    接着第五个笔记说,第五个笔记实现了简单的静态资源服务起,那么我们利用这个静态资源服务起为我们提供页面,创建一个socket.html页面,在这个页面中我们实现Socket连接,连接到我们的Netty搭建的聊天服务器上,因此我们需要创建一个聊天页面和Socket连接,这里我们假定Socket连接地址为 http://localhost:8080/socket 我们首先实现页面的开发和Socket的连接。

    客户端代码

    <!DOCTYPE html>
    <html>
    <head>
        <meta charset="UTF-8">
        <title>WebSocket Chat</title>
    </head>
    <body>
    <script type="text/javascript">
        var socket;
        if (!window.WebSocket) {
            window.WebSocket = window.MozWebSocket;
        }
        if (window.WebSocket) {
            socket = new WebSocket("ws://localhost:8080/socket");
            socket.onmessage = function (event) {
                var ta = document.getElementById('chatContent');
                ta.value = ta.value + '
    ' + event.data
            };
            socket.onopen = function (event) {
                var ta = document.getElementById('chatContent');
                ta.value = "连接开启!";
            };
            socket.onclose = function (event) {
                var ta = document.getElementById('chatContent');
                ta.value = ta.value + "连接被关闭";
            };
        } else {
            alert("浏览器不支持 WebSocket!");
        }
    
        function send(message) {
            if (!window.WebSocket) {
                return;
            }
            if (socket.readyState == WebSocket.OPEN) {
                socket.send(message);
            } else {
                alert("连接没有开启.");
            }
        }
    </script>
    <form onsubmit="return false;">
        <h1>基于Netty构建的聊天室</h1>
        <textarea id="chatContent" style=" 100%; height: 400px;"/>
        <hr>
        <input type="text" name="message" style=" 300px">
        <input type="button" value="发送消息" onclick="send(this.form.message.value)">
        <input type="button" onclick="javascript:document.getElementById('chatContent').value=''" value="清空记录">
    </form>
    </body>
    </html>
    

    页面效果

    启动完成后,输入http://localhost:8080/socket.html 应当可以看到如下的界面,这些方法和第五个笔记的没有什么新得东西,就是新增了一个HTML页面而已.

    [

    可以看到连接被关闭的字样,这是因为我们的服务端还没有完成,我们将服务端继续改进即可。

    完善服务端

    在第四个笔记的时候,我们只实现了一个简单的服务端,使用telnet进行测试,我们可以将起拿过来修改一下,为我们所用..

    修改内容分析

    修改传递类型

    第四篇文章中我们实现了基于String类型的数据,这里我们修改为TextWebSocketFrame 当然相应的返回类型和读取也需要对应修改(这里为了演示,移除了时间,不影响整体逻辑,只是界面效果).x修改后的代码如下:

    package com.zhoutao123.simpleChat.html;
    
    import io.netty.channel.Channel;
    import io.netty.channel.ChannelHandlerContext;
    import io.netty.channel.SimpleChannelInboundHandler;
    import io.netty.channel.group.ChannelGroup;
    import io.netty.channel.group.DefaultChannelGroup;
    import io.netty.handler.codec.http.websocketx.TextWebSocketFrame;
    import io.netty.util.concurrent.GlobalEventExecutor;
    
    import static com.zhoutao123.simpleChat.utils.DatetimeUtils.getNowDatetime;
    
    public class TextWebSocketServiceAdapter extends SimpleChannelInboundHandler<TextWebSocketFrame> {
    
      // 创建ChannelGroup 用于保存连接的Channel
      public static ChannelGroup channelGroup = new DefaultChannelGroup(GlobalEventExecutor.INSTANCE);
    
      // 当有新的Channel增加的时候
      @Override
      public void handlerAdded(ChannelHandlerContext ctx) throws Exception {
        // 获取当前的Channel
        Channel channel = ctx.channel();
        // 向其他Channel发送上线消息
        channelGroup.writeAndFlush(
            new TextWebSocketFrame(String.format("[服务器]	用户:%s 加入聊天室!
    ", channel.remoteAddress())));
        // 添加Channel到Group里面
        channelGroup.add(channel);
        // 向新用户发送欢迎信息
        channel.writeAndFlush(
            new TextWebSocketFrame(String.format("你好,%s欢迎来到Netty聊天室
    ", channel.remoteAddress())));
      }
    
      @Override
      public void handlerRemoved(ChannelHandlerContext ctx) throws Exception {
        // 用户退出后向全部Channel发送下线消息
        channelGroup.writeAndFlush(
            new TextWebSocketFrame(
                String.format("[服务器]	用户:%s 离开聊天室!
    ", ctx.channel().remoteAddress())));
        // 移除
        channelGroup.remove(ctx.channel());
      }
    
      @Override
      protected void channelRead0(ChannelHandlerContext ctx, TextWebSocketFrame msg) throws Exception {
        // 服务器接收到新的消息后之后执行
        // 获取当前的Channel
        Channel currentChannel = ctx.channel();
        // 遍历
        for (Channel channel : channelGroup) {
          String sendMessage = "";
          // 如果是当前的用户发送You的信息,不是则发送带有发送人的信息
          if (channel == currentChannel) {
            sendMessage = String.format("[You]	%s
    ", msg.text());
          } else {
            sendMessage = String.format("[%s]	 %s
    ", currentChannel.remoteAddress(), msg.text());
          }
          channel.writeAndFlush(new TextWebSocketFrame(sendMessage));
        }
      }
    
      @Override
      public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
        // 发送异常的时候通知移除
        Channel channel = ctx.channel();
        channelGroup.writeAndFlush(
            new TextWebSocketFrame(String.format("[服务器]	 用户 %s 出现异常掉线!
    ", channel.remoteAddress())));
        ctx.close();
      }
    }
    
    

    新增解码器和编码

    既然接收和发送的数据类型改变了,那么我们也要相应的修改解码器和编码器,添加以下代码即可.

    package com.zhoutao123.simpleChat.html;
    
    
    import com.waylau.netty.demo.websocketchat.TextWebSocketFrameHandler;
    import io.netty.channel.ChannelInitializer;
    import io.netty.channel.ChannelPipeline;
    import io.netty.channel.socket.SocketChannel;
    import io.netty.handler.codec.http.HttpObjectAggregator;
    import io.netty.handler.codec.http.HttpServerCodec;
    import io.netty.handler.codec.http.websocketx.WebSocketServerProtocolHandler;
    import io.netty.handler.stream.ChunkedWriteHandler;
    
    /**
     * 服务端 ChannelInitializer
     *
     * @author waylau.com
     * @date 2015-3-13
     */
    public class HttpServerInitializer extends ChannelInitializer<SocketChannel> {
    
        @Override
        public void initChannel(SocketChannel ch) throws Exception {
            ChannelPipeline pipeline = ch.pipeline();
            //将请求和应答消息编码或解码为HTTP消息
            pipeline.addLast(new HttpServerCodec());
            //将HTTP消息的多个部分组合成一条完整的HTTP消息
            pipeline.addLast(new HttpObjectAggregator(64 * 1024));
            pipeline.addLast(new ChunkedWriteHandler());
            pipeline.addLast(new HttpServerHandleAdapter());
            // 添加以下代码
            pipeline.addLast(new WebSocketServerProtocolHandler("/socket"));
            pipeline.addLast(new TextWebSocketServiceAdapter());
        }
    }
    

    测试效果

    启动服务器,打开聊天界面测试:

    聊天效果图

    可以看到实现了基本的简单的聊天效果,至于压力测试还没有测试,不知道能承载多少个用户,至此我们从丢弃服务到实现在线群聊的实现,大致对Netty有了基本的了解,后期我们将对Netty实现源码级别的学习和了解.

  • 相关阅读:
    推荐系统算法总结(转)
    【算法题】求最大子数组之和
    jQuery中的filter和find函数
    获取文件夹大小
    微博140字,英文算半个字,中文算一个字,如何实现?
    Xcode 4 添加 Three20 的方法
    應用程式的設定檔info.plist
    iphone中结束电话后返回自己的应用
    解决问题:The icon file must be 57x57 pixels, in .png format (19014)
    开发中的一些小细节代码分享
  • 原文地址:https://www.cnblogs.com/zhoutao825638/p/10382208.html
Copyright © 2011-2022 走看看