Server
public class Server { public static void main(String[] args) throws InterruptedException { EventLoopGroup bossGroup = new NioEventLoopGroup(1); EventLoopGroup workerGroup = new NioEventLoopGroup(); try { ServerBootstrap serverBootstrap = new ServerBootstrap(); serverBootstrap.group(bossGroup, workerGroup) .channel(NioServerSocketChannel.class) .handler(new LoggingHandler(LogLevel.INFO)) .childHandler(new ChannelInitializer<SocketChannel>() { @Override protected void initChannel(SocketChannel ch) throws Exception { ChannelPipeline pipeline = ch.pipeline(); //使用Http的编解码器 pipeline.addLast(new HttpServerCodec()); //以块方式写,添加ChunkedWriteHandler处理器 pipeline.addLast(new ChunkedWriteHandler()); /* 1.Http协议在传输过程中是分段的,HttpObjectAggregator就是可以将多个段聚合 2.这就是为什么当浏览器发送大量数据时,就会发出多次http请求的原因 */ pipeline.addLast(new HttpObjectAggregator(8192)); /* 1.对应WebSocket,它的数据是以帧(frame)的形式传递 2.可以看到WEbSocketFrame 下面有六个子类 WebSocketServerProtocolHandler作用: 3.浏览器发送请求时 ws://localhost:7000/hello--识别请求的资源 4.将一个Http协议升级为WS协议,即WebSocket协议--保存长连接 */ pipeline.addLast(new WebSocketServerProtocolHandler("/hello")); //自定义handler,处理业务逻辑 pipeline.addLast(new TextWebSocketFrameHandler()); } }); ChannelFuture future = serverBootstrap.bind(7000).sync(); future.channel().closeFuture().sync(); } finally { bossGroup.shutdownGracefully(); workerGroup.shutdownGracefully(); } }}
Handler
//TextWebSocketFrame表示一个文本帧(frame)public class TextWebSocketFrameHandler 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())); } //感知连接 @Override public void handlerAdded(ChannelHandlerContext ctx) throws Exception { //id是一个唯一的值, LongText是唯一的 System.out.println("handlerAdded 被调用" + ctx.channel().id().asLongText()); //ShortText不是唯一的 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(); //关闭连接 }}
<!DOCTYPE html><html lang="en"><head> <meta charset="UTF-8"> <title>Title</title></head><!--复制到Vscode--><body><script> var socket; //判断当前浏览器是否支持websocket if(window.WebSocket) { //go on socket = new WebSocket("ws://localhost:7000/hello"); //相当于channelReado, 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) { if(!window.socket) { //先判断socket是否创建好 return; } if(socket.readyState == WebSocket.OPEN) { //通过socket 发送消息 socket.send(message) } else { alert("连接没有开启"); } }</script><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></html>