Netty5 + WebSocket 練習

1. 瞭解WebSocket知識
  略
2. websocket實現系統簡單反饋時間javascript

  WebSocketServerHandler.javahtml

 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.javajava

  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 (這個隨便放均可以,到時候雙擊打開這個便可,不禁服務器提供)web

 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實現簡單聊天室bootstrap

  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.htmlwebsocket

 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 + '\n' + 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="width: 500px; height: 300px;"></textarea>
45         <br> 
46         <input type="text" name="message"  style="width: 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

相關文章
相關標籤/搜索