WSServer
package com.oy.ws; import io.netty.bootstrap.ServerBootstrap; import io.netty.channel.*; import io.netty.channel.nio.NioEventLoopGroup; import io.netty.channel.socket.SocketChannel; import io.netty.channel.socket.nio.NioServerSocketChannel; 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; public class WSServer { private int port; public WSServer(int port) { this.port = port; } public static void main(String[] args) { new WSServer(8002).run(); } public void run() { EventLoopGroup boss = new NioEventLoopGroup(1); EventLoopGroup work = new NioEventLoopGroup(); try { ServerBootstrap bootstrap = new ServerBootstrap(); bootstrap .group(boss, work) .channel(NioServerSocketChannel.class) .childHandler(new ChannelInitializer<SocketChannel>() { @Override protected void initChannel(SocketChannel ch) throws Exception { ChannelPipeline pipeline = ch.pipeline(); // 因為基於 HTTP 協議,使用 http 的編碼解碼器 pipeline.addLast(new HttpServerCodec()); // 以塊的方式寫 pipeline.addLast(new ChunkedWriteHandler()); // 因為 http 數據在傳輸過程中時分段的,HttpObjectAggregator 就可以將多個段聚合 pipeline.addLast(new HttpObjectAggregator(8192)); // websocket 數據是以幀(frame)的形式傳遞 // webSocketFrame 下面有六個子類 // WebSocketServerProtocolHandler:將 http 協議升級為 ws 協議,保持長連接 pipeline.addLast(new WebSocketServerProtocolHandler("/hello")); pipeline.addLast(new WSServerHandler()); } }); // 綁定端口,啟動服務 ChannelFuture future = bootstrap.bind(port).sync(); System.out.println("server started and listen " + port); // 監聽關閉 future.channel().closeFuture().sync(); } catch (Exception e) { e.printStackTrace(); } finally { boss.shutdownGracefully(); work.shutdownGracefully(); } } }
WSServerHandler
package com.oy.ws; import io.netty.channel.ChannelHandlerContext; import io.netty.channel.SimpleChannelInboundHandler; import io.netty.handler.codec.http.websocketx.TextWebSocketFrame; import java.time.LocalDateTime; /** * WebSocketFrame: 表示一個文本幀 */ public class WSServerHandler extends SimpleChannelInboundHandler<TextWebSocketFrame> { @Override protected void channelRead0(ChannelHandlerContext ctx, TextWebSocketFrame msg) throws Exception { System.out.println("服務器收到消息:" + msg.text()); // 回復消息 ctx.channel().writeAndFlush(new TextWebSocketFrame("服務器時間:" + LocalDateTime.now() + ", " + msg.text())); } /** * 當 web 客戶端連接后,觸發該方法 */ @Override public void handlerAdded(ChannelHandlerContext ctx) throws Exception { // longText 是唯一的 System.out.println("handlerAdded 被調用" + ctx.channel().id().asLongText()); System.out.println("handlerAdded 被調用" + ctx.channel().id().asShortText()); } @Override public void handlerRemoved(ChannelHandlerContext ctx) throws Exception { System.out.println("handlerRemoved 被調用" + ctx.channel().id().asLongText()); } @Override public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception { System.out.println("發生異常:" + cause.getMessage()); ctx.close(); } }
hello.html
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> </head> <body> <form onsubmit="return false"> <textarea name="message" style="height:300px;width:300px;"></textarea> <input type="button" value="發送消息" onclick="send(this.form.message.value)"/> <textarea id="responseText" style="height:300px;width:300px;"></textarea> <input type="button" value="清空" onclick="document.getElementById('responseText').value=''"/> </form> </body> <script> var socket; // 判斷當前瀏覽器是否支持 websocket if(window.WebSocket){ socket = new WebSocket("ws://localhost:8002/hello"); // ev:收到服務器返回的消息 socket.onmessage = function (ev) { var rt = document.getElementById("responseText"); rt.value = rt.value + "\n" + ev.data; } // 連接開啟 socket.onopen = function (ev) { var rt = document.getElementById("responseText"); rt.value = "連接開啟了"; } // 連接關閉 socket.onclose = function (ev) { var rt = document.getElementById("responseText"); rt.value = rt.value + "\n" + "連接關閉了"; } } else { alert("當前瀏覽器不支持 WebSocket"); } // 客戶端發送消息給服務器 function send(message) { // 先判斷 socket 是否創建好 if (!window.socket) { return; } if (socket.readyState == WebSocket.OPEN) { // 通過 socket 發送消息 socket.send(message); } else { alert("連接失敗"); } } </script> </html>
測試:
1)啟動服務端 WSServer;
2)啟動客戶端
3)客戶端發送消息給服務器,服務器返回消息給客戶端
---