• Netty5 + WebSocket 练习


    1. 了解WebSocket知识
      略
    2. websocket实现系统简单反馈时间

      WebSocketServerHandler.java

     1 package com.jieli.nettytest.websocketserver;
     2 
     3 import io.netty.bootstrap.ServerBootstrap;
     4 import io.netty.channel.ChannelFuture;
     5 import io.netty.channel.ChannelInitializer;
     6 import io.netty.channel.EventLoopGroup;
     7 import io.netty.channel.nio.NioEventLoopGroup;
     8 import io.netty.channel.socket.SocketChannel;
     9 import io.netty.channel.socket.nio.NioServerSocketChannel;
    10 import io.netty.handler.codec.http.HttpObjectAggregator;
    11 import io.netty.handler.codec.http.HttpServerCodec;
    12 import io.netty.handler.stream.ChunkedWriteHandler;
    13 
    14 public class WebSocketServer {
    15     
    16     public void run(int port){
    17         EventLoopGroup bossGroup = new NioEventLoopGroup();
    18         EventLoopGroup workerGroup = new NioEventLoopGroup();
    19         try {
    20             ServerBootstrap b = new ServerBootstrap();
    21             b.group(bossGroup, workerGroup)
    22              .channel(NioServerSocketChannel.class)
    23              .childHandler(new ChannelInitializer<SocketChannel>() {
    24                 @Override
    25                 protected void initChannel(SocketChannel ch) throws Exception {
    26                     //HttpServerCodec将请求和应答消息编码或解码为HTTP消息
    27                     //通常接收到的http是一个片段,如果想要完整接受一次请求所有数据,我们需要绑定HttpObjectAggregator
    28                     //然后就可以收到一个FullHttpRequest完整的请求信息了
    29                     //ChunkedWriteHandler 向客户端发送HTML5文件,主要用于支持浏览器和服务器进行WebSocket通信
    30                     //WebSocketServerHandler自定义Handler
    31                     ch.pipeline().addLast("http-codec", new HttpServerCodec())
    32                                  .addLast("aggregator", new HttpObjectAggregator(65536)) //定义缓冲大小
    33                                  .addLast("http-chunked", new ChunkedWriteHandler())
    34                                  .addLast("handler", new WebSocketServerHandler());
    35                 }
    36             });
    37             
    38             ChannelFuture f = b.bind(port).sync();
    39             System.out.println("start...");
    40             f.channel().closeFuture().sync();
    41         } catch (Exception e) {
    42             e.printStackTrace();
    43         } finally {
    44             workerGroup.shutdownGracefully();
    45             bossGroup.shutdownGracefully();
    46         }
    47     }
    48     
    49     public static void main(String[] args) {
    50         new WebSocketServer().run(7777);
    51     }
    52 }

      WebSocketServerHandler.java

      1 package com.jieli.nettytest.websocketserver;
      2 
      3 import java.util.logging.Level;
      4 import java.util.logging.Logger;
      5 
      6 import io.netty.buffer.ByteBuf;
      7 import io.netty.buffer.Unpooled;
      8 import io.netty.channel.ChannelFuture;
      9 import io.netty.channel.ChannelFutureListener;
     10 import io.netty.channel.ChannelHandlerContext;
     11 import io.netty.channel.SimpleChannelInboundHandler;
     12 import io.netty.handler.codec.http.DefaultFullHttpResponse;
     13 import io.netty.handler.codec.http.FullHttpRequest;
     14 import io.netty.handler.codec.http.FullHttpResponse;
     15 import io.netty.handler.codec.http.HttpHeaderUtil;
     16 import io.netty.handler.codec.http.HttpResponseStatus;
     17 import io.netty.handler.codec.http.HttpVersion;
     18 import io.netty.handler.codec.http.websocketx.CloseWebSocketFrame;
     19 import io.netty.handler.codec.http.websocketx.PingWebSocketFrame;
     20 import io.netty.handler.codec.http.websocketx.PongWebSocketFrame;
     21 import io.netty.handler.codec.http.websocketx.TextWebSocketFrame;
     22 import io.netty.handler.codec.http.websocketx.WebSocketFrame;
     23 import io.netty.handler.codec.http.websocketx.WebSocketServerHandshaker;
     24 import io.netty.handler.codec.http.websocketx.WebSocketServerHandshakerFactory;
     25 import io.netty.util.CharsetUtil;
     26 
     27 public class WebSocketServerHandler extends SimpleChannelInboundHandler<Object>{
     28 
     29     /**
     30      * 日志
     31      */
     32     private static final Logger logger = 
     33             Logger.getLogger(WebSocketServerHandler.class.getName());
     34     /**
     35      * 全局websocket
     36      */
     37     private WebSocketServerHandshaker handshaker;
     38     
     39     @Override
     40     protected void messageReceived(ChannelHandlerContext ctx, Object msg)
     41             throws Exception {
     42         //普通HTTP接入
     43         if(msg instanceof FullHttpRequest){
     44             handleHttpRequest(ctx, (FullHttpRequest) msg);
     45         }else if(msg instanceof WebSocketFrame){ //websocket帧类型 已连接
     46             //BinaryWebSocketFrame CloseWebSocketFrame ContinuationWebSocketFrame 
     47             //PingWebSocketFrame PongWebSocketFrame TextWebScoketFrame
     48             handleWebSocketFrame(ctx, (WebSocketFrame) msg);
     49         }
     50     }
     51     
     52     @Override
     53     public void channelReadComplete(ChannelHandlerContext ctx) throws Exception {
     54         ctx.flush();
     55     }
     56     
     57     private void handleHttpRequest(ChannelHandlerContext ctx, FullHttpRequest request){
     58         //如果http解码失败 则返回http异常 并且判断消息头有没有包含Upgrade字段(协议升级)
     59         if(!request.decoderResult().isSuccess() 
     60                 || (!"websocket".equals( request.headers().get("Upgrade")))    ){
     61             sendHttpResponse(ctx, request, new DefaultFullHttpResponse(
     62                     HttpVersion.HTTP_1_1, HttpResponseStatus.BAD_REQUEST));
     63             return ;
     64         }
     65         //构造握手响应返回
     66         WebSocketServerHandshakerFactory ws = new WebSocketServerHandshakerFactory("", null, false);
     67         handshaker = ws.newHandshaker(request);
     68         if(handshaker == null){
     69             //版本不支持
     70             WebSocketServerHandshakerFactory.sendUnsupportedVersionResponse(ctx.channel());
     71         }else{
     72             handshaker.handshake(ctx.channel(), request);
     73         }
     74     }
     75     /**
     76      * websocket帧
     77      * @param ctx
     78      * @param frame
     79      */
     80     private void handleWebSocketFrame(ChannelHandlerContext ctx, WebSocketFrame frame){
     81         //判断是否关闭链路指令
     82         if(frame instanceof CloseWebSocketFrame){
     83             handshaker.close(ctx.channel(), (CloseWebSocketFrame) frame.retain());
     84             return ;
     85         }
     86         //判断是否Ping消息 -- ping/pong心跳包
     87         if(frame instanceof PingWebSocketFrame){
     88             ctx.channel().write(new PongWebSocketFrame(frame.content().retain()));
     89             return ;
     90         }
     91         //本程序仅支持文本消息, 不支持二进制消息
     92         if(!(frame instanceof TextWebSocketFrame)){
     93             throw new UnsupportedOperationException(
     94                     String.format("%s frame types not supported", frame.getClass().getName()));
     95         }
     96         
     97         //返回应答消息 text文本帧
     98         String request = ((TextWebSocketFrame) frame).text();
     99         //打印日志
    100         if(logger.isLoggable(Level.FINE)){
    101             logger.fine(String.format("%s received %s", ctx.channel(), request));
    102         }
    103         //发送到客户端websocket
    104         ctx.channel().write(new TextWebSocketFrame(request 
    105                 + ", 欢迎使用Netty WebSocket服务, 现在时刻:" 
    106                 + new java.util.Date().toString()));
    107     }
    108     
    109     /**
    110      * response
    111      * @param ctx
    112      * @param request
    113      * @param response
    114      */
    115     private static void sendHttpResponse(ChannelHandlerContext ctx, 
    116             FullHttpRequest request, FullHttpResponse response){
    117         //返回给客户端
    118         if(response.status().code() != HttpResponseStatus.OK.code()){
    119             ByteBuf buf = Unpooled.copiedBuffer(response.status().toString(), CharsetUtil.UTF_8);
    120             response.content().writeBytes(buf);
    121             buf.release();
    122             HttpHeaderUtil.setContentLength(response, response.content().readableBytes());
    123         }
    124         //如果不是keepalive那么就关闭连接
    125         ChannelFuture f = ctx.channel().writeAndFlush(response);
    126         if(!HttpHeaderUtil.isKeepAlive(response) 
    127                 || response.status().code() != HttpResponseStatus.OK.code()){
    128             f.addListener(ChannelFutureListener.CLOSE);
    129         }
    130     }
    131     
    132     /**
    133      * 异常 出错
    134      */
    135     @Override
    136     public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause)
    137             throws Exception {
    138         cause.printStackTrace();
    139         ctx.close();
    140     }
    141 }

      WebSocketServer.html (这个随便放都可以,到时候双击打开这个即可,不由服务器提供)

     1 <html>
     2     <head>
     3         <meta charset="utf-8">
     4         <title>Netty websocket 时间服务器</title>
     5     </head>
     6     <body>
     7         <form action="" onsubmit="return false;">
     8             <input type="text" name="message" value="..."/>
     9             <br>
    10             <input type="button" value="send" value="发送websocket请求消息" onclick="send(this.form.message.value);" /> 
    11             <hr color="blue">
    12             <h3>服务器返回信息</h3>
    13             <textarea id="responseText" rows="10" cols=""></textarea>
    14         </form>
    15     </body>
    16     
    17     <script type="text/javascript">
    18         var socket;
    19         if(!window.WebSocket){
    20             window.WebSocket = window.MozWebSocket;
    21         }
    22         if(window.WebSocket){
    23             socket = new WebSocket("ws://localhost:7777/websocket");
    24             socket.onmessage = function(event){
    25                 var ta = document.getElementById('responseText');
    26                 ta.value="";
    27                 ta.value=event.data;
    28             };
    29             socket.onopen = function(event){
    30                 var ta = document.getElementById('responseText');
    31                 ta.value = "打开websocket服务正常";
    32             }
    33             socket.onclose = function(event){
    34                 var ta = document.getElementById('responseText');
    35                 ta.value="";
    36                 ta.value="websocket关闭";
    37             }
    38         }else{
    39             alert("对不起,您的浏览器不支持WebSocket.");
    40         }
    41         
    42         function send(message){
    43             if(!window.WebSocket){
    44                 return ;
    45             }
    46             if(socket.readyState == WebSocket.OPEN){
    47                 socket.send(message);
    48             }else{
    49                 alert("WebSocket 连接创建失败.");
    50             }
    51         }
    52     </script>
    53 </html>

      运行结果

    3. websocket实现简单聊天室

      WebSocketChatServer.java

     1 package com.jieli.nettytest.websocket;
     2 
     3 import io.netty.bootstrap.ServerBootstrap;
     4 import io.netty.channel.ChannelFuture;
     5 import io.netty.channel.ChannelOption;
     6 import io.netty.channel.EventLoopGroup;
     7 import io.netty.channel.nio.NioEventLoopGroup;
     8 import io.netty.channel.socket.nio.NioServerSocketChannel;
     9 
    10 public class WebsocketChatServer {
    11     
    12     private int port;
    13     
    14     public WebsocketChatServer(int port){
    15         this.port = port;
    16     }
    17     
    18     public void run() throws Exception{
    19         EventLoopGroup bossGroup = new NioEventLoopGroup();
    20         EventLoopGroup workerGroup = new NioEventLoopGroup();
    21         try {
    22             ServerBootstrap b = new ServerBootstrap();
    23             b.group(bossGroup, workerGroup)
    24              .channel(NioServerSocketChannel.class)
    25              .childHandler(new WebsocketChatServerInitializer())
    26              .option(ChannelOption.SO_BACKLOG, 128)
    27              .childOption(ChannelOption.SO_KEEPALIVE, true);
    28             
    29             System.out.println("websocket start..");
    30 
    31             ChannelFuture f = b.bind(port).sync();
    32             
    33             f.channel().closeFuture().sync();
    34         } catch (Exception e) {
    35             e.printStackTrace();
    36         } finally {
    37             workerGroup.shutdownGracefully();
    38             bossGroup.shutdownGracefully();
    39             System.out.println("websocket close.");
    40         }
    41     }
    42     
    43     public static void main(String[] args) throws Exception{
    44         new WebsocketChatServer(8080).run();
    45     }
    46 }

      WebsocketChatServerInitializer.java

     1 package com.jieli.nettytest.websocket;
     2 
     3 import io.netty.channel.ChannelInitializer;
     4 import io.netty.channel.ChannelPipeline;
     5 import io.netty.channel.socket.SocketChannel;
     6 import io.netty.handler.codec.http.HttpObjectAggregator;
     7 import io.netty.handler.codec.http.HttpServerCodec;
     8 import io.netty.handler.codec.http.websocketx.WebSocketServerProtocolHandler;
     9 import io.netty.handler.stream.ChunkedWriteHandler;
    10 
    11 public class WebsocketChatServerInitializer extends ChannelInitializer<SocketChannel>{
    12 
    13     @Override
    14     protected void initChannel(SocketChannel ch) throws Exception {
    15         ChannelPipeline pipeline = ch.pipeline();
    16         pipeline.addLast(new HttpServerCodec())
    17                 .addLast(new HttpObjectAggregator(64*1024))
    18                 .addLast(new ChunkedWriteHandler())
    19                 .addLast(new HttpRequestHandler("/ws")) //如果访问的是RUI"/ws",处理WebSocket升级
    20                 .addLast(new WebSocketServerProtocolHandler("/ws"))
    21                 .addLast(new TextWebSocketFrameHandler());
    22     }
    23     
    24 }

       HttpRequestHandler.java

      1 package com.jieli.nettytest.websocket;
      2 
      3 import java.io.File;
      4 import java.io.RandomAccessFile;
      5 import java.net.URL;
      6 
      7 import io.netty.channel.Channel;
      8 import io.netty.channel.ChannelFuture;
      9 import io.netty.channel.ChannelFutureListener;
     10 import io.netty.channel.ChannelHandlerContext;
     11 import io.netty.channel.DefaultFileRegion;
     12 import io.netty.channel.SimpleChannelInboundHandler;
     13 import io.netty.handler.codec.http.DefaultHttpResponse;
     14 import io.netty.handler.codec.http.FullHttpRequest;
     15 import io.netty.handler.codec.http.FullHttpResponse;
     16 import io.netty.handler.codec.http.HttpHeaderNames;
     17 import io.netty.handler.codec.http.HttpHeaderUtil;
     18 import io.netty.handler.codec.http.HttpHeaderValues;
     19 import io.netty.handler.codec.http.HttpResponse;
     20 import io.netty.handler.codec.http.HttpResponseStatus;
     21 import io.netty.handler.codec.http.HttpVersion;
     22 import io.netty.handler.codec.http.LastHttpContent;
     23 import io.netty.handler.ssl.SslHandler;
     24 import io.netty.handler.stream.ChunkedNioFile;
     25 
     26 public class HttpRequestHandler extends SimpleChannelInboundHandler<FullHttpRequest>{
     27     //扩展SimpleChannelInboundHandler用于处理FullHttpRequest信息
     28     private final String wsuri;
     29 //    private static final File index;
     30 //    
     31 //    static {
     32 //        URL location = HttpRequestHandler.class
     33 //                .getProtectionDomain().getCodeSource().getLocation();
     34 //        try {
     35 //            String path = location.toURI() + "html/index.html";
     36 //            path = !path.contains("file:") ? path : path.substring(5);
     37 //            index = new File(path);
     38 //        } catch (Exception e) {
     39 //            e.printStackTrace();
     40 //            throw new IllegalStateException("can't find index.html");
     41 //        }
     42 //    }
     43     
     44     public HttpRequestHandler(String wsuri){
     45         this.wsuri = wsuri;
     46     }
     47     
     48     @Override
     49     protected void messageReceived(ChannelHandlerContext ctx,
     50             FullHttpRequest request) throws Exception {
     51         if(wsuri.equalsIgnoreCase(request.uri())){
     52             //如果请求的是WebSocket升级,将其传递给在ChannelPipeline中的下一个ChannelInboundHandler处理
     53             //这里跟第一个例子的websocket协议升级判断方式是不同的 因为只只是判断uri路径而已
     54             //对应的js请求路径 socket = new WebSocket("ws://localhost:8080/ws");
     55             ctx.fireChannelRead(request.retain());
     56         }else{
     57             //处理100continue
     58             if(HttpHeaderUtil.is100ContinueExpected(request)){
     59                 send100Continue(ctx);
     60             }
     61             //读取默认页
     62             RandomAccessFile file = new RandomAccessFile("C:\html\index.html", "r");
     63             
     64             HttpResponse response = new DefaultHttpResponse(
     65                     request.protocolVersion(), HttpResponseStatus.OK);
     66             response.headers().set(HttpHeaderNames.CONTENT_TYPE, 
     67                     "text/html; charset=UTF-8");
     68             
     69             boolean keepAlive = HttpHeaderUtil.isKeepAlive(request);
     70             if(keepAlive){
     71                 response.headers().setInt(HttpHeaderNames.CONTENT_LENGTH, (int) file.length());
     72                 response.headers().set(HttpHeaderNames.CONNECTION, HttpHeaderValues.KEEP_ALIVE);
     73             }
     74             ctx.write(response);
     75             
     76             if(ctx.pipeline().get(SslHandler.class) == null){
     77                 //如果不是https安全连接的话 要达到最大效率的话,可以通过把index.html直接存储在DefaultFileRegion中
     78                 //实现零拷贝传输 就是不拷贝到内存,直接读取文件通过文件输出流进行处理
     79                 ctx.write(new DefaultFileRegion(file.getChannel(), 0, file.length()));
     80             }else{
     81                 //否则要读取全部文件,然后处理加密,在发送,只不过这一切都有netty内部处理
     82                 //A ChunkedInput that fetches data from a file chunk by chunk using NIO FileChannel.
     83                 //If your operating system supports zero-copy file transfer such as sendfile(), 
     84                 //you might want to use FileRegion instead.
     85                 ctx.write(new ChunkedNioFile(file.getChannel()));
     86             }
     87             ChannelFuture future = ctx.writeAndFlush(LastHttpContent.EMPTY_LAST_CONTENT);
     88             
     89             if(!keepAlive){
     90                 future.addListener(ChannelFutureListener.CLOSE);
     91             }
     92             file.close();
     93         }
     94     }
     95 
     96     private static void send100Continue(ChannelHandlerContext ctx){
     97         FullHttpResponse response = (FullHttpResponse) new DefaultHttpResponse(
     98                 HttpVersion.HTTP_1_1, HttpResponseStatus.CONTINUE);
     99         ctx.writeAndFlush(response);
    100     }
    101     
    102     @Override
    103     public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause)
    104             throws Exception {
    105         Channel incoming = ctx.channel();
    106         System.out.println("Client:"+incoming.remoteAddress()+"exception.");
    107         cause.printStackTrace();
    108         ctx.close();
    109     }
    110 }
    View Code

      html/index.html

     1 <!DOCTYPE html>
     2 <html>
     3 <head>
     4 <meta charset="UTF-8">
     5 <title>WebSocket Chat</title>
     6 </head>
     7 <body>
     8     <script type="text/javascript">
     9         var socket;
    10         if (!window.WebSocket) {
    11             window.WebSocket = window.MozWebSocket;
    12         }
    13         if (window.WebSocket) {
    14             socket = new WebSocket("ws://localhost:8080/ws");
    15             socket.onmessage = function(event) {
    16                 var ta = document.getElementById('responseText');
    17                 ta.value = ta.value + '
    ' + event.data
    18             };
    19             socket.onopen = function(event) {
    20                 var ta = document.getElementById('responseText');
    21                 ta.value = "连接开启!";
    22             };
    23             socket.onclose = function(event) {
    24                 var ta = document.getElementById('responseText');
    25                 ta.value = ta.value + "连接被关闭";
    26             };
    27         } else {
    28             alert("你的浏览器不支持 WebSocket!");
    29         }
    30 
    31         function send(message) {
    32             if (!window.WebSocket) {
    33                 return;
    34             }
    35             if (socket.readyState == WebSocket.OPEN) {
    36                 socket.send(message);
    37             } else {
    38                 alert("连接没有开启.");
    39             }
    40         }
    41     </script>
    42     <form onsubmit="return false;">
    43         <h3>WebSocket 聊天室:</h3>
    44         <textarea id="responseText" style=" 500px; height: 300px;"></textarea>
    45         <br> 
    46         <input type="text" name="message"  style=" 300px" value="Welcome to localhost">
    47         <input type="button" value="发送消息" onclick="send(this.form.message.value)">
    48         <input type="button" onclick="javascript:document.getElementById('responseText').value=''" value="清空聊天记录">
    49     </form>
    50     <br>
    51         Netty SEO 优化 
    52         Netty 是什么
    53         Netty 怎么样
    54         Netty4 Netty5 区别
    55         Netty 效率
    56         Netty 版本区别
    57         Netty 和 Mina
    58         Netty 网络编程
    59         Netty Java 网络编程
    60         Netty Java Socket NIO
    61         NIO 编程
    62         Netty NIO 开发
    63         Netty3 Netty4 Netty5
    64         Netty 好处
    65         Netty 一般注意什么
    66         Netty 例子程序
    67         Netty Hello World
    68         Netty 聊天程序
    69         Netty Web HTML HTTP FTP SSL 
    70         Netty UDP TCP WebSocket 练习
    71         Netty 连接数
    72         Netty 源码
    73     <br>
    74 </body>
    75 </html>
    View Code

      运行结果,依次运行1,2,3

      服务器打印结果

    参考资料:
      《Netty 权威指南》
      https://zh.wikipedia.org/wiki/WebSocket
      https://www.zhihu.com/question/20215561
      http://waylau.com/netty-websocket-chat/

    本文地址:http://www.cnblogs.com/wunaozai/p/5240006.html

  • 相关阅读:
    关于jQuery的两对小括号()()的说明
    高效能 DBA 的七个习惯
    Div+CSS网站设计的优点
    .Net上传图片按比例自动缩小或放大
    SEO草根技术基础—DIV+CSS
    asp.net连接Mysql(connector/net 5.0)
    大型网站(高访问、海量数据)技术架构
    ISO Latin1字符集
    CuteEditor学习总结技巧
    Craigslist 的数据库架构
  • 原文地址:https://www.cnblogs.com/wunaozai/p/5240006.html
Copyright © 2020-2023  润新知