| 1) Http协议是无状态的, 浏览器和服务器间的请求响应一次,下一次会重新创建连接. |
| 2) 实现基于webSocket的长连接的全双工的交互 |
| 3) 改变Http协议多次请求的约束,实现长连接了, 服务器可以发送消息给浏览器 |
| 4) 客户端浏览器和服务器端会相互感知,比如服务器关闭了,浏览器会感知,同样浏览器关闭了,服务器会感知 |
| public class MyServer { |
| public static void main(String[] args) throws Exception{ |
| |
| |
| EventLoopGroup bossGroup = new NioEventLoopGroup(1); |
| EventLoopGroup workerGroup = new NioEventLoopGroup(); |
| try { |
| |
| ServerBootstrap serverBootstrap = new ServerBootstrap(); |
| |
| serverBootstrap.group(bossGroup, workerGroup); |
| serverBootstrap.channel(NioServerSocketChannel.class); |
| serverBootstrap.handler(new LoggingHandler(LogLevel.INFO)); |
| serverBootstrap.childHandler(new ChannelInitializer<SocketChannel>() { |
| |
| @Override |
| protected void initChannel(SocketChannel ch) throws Exception { |
| ChannelPipeline pipeline = ch.pipeline(); |
| |
| |
| pipeline.addLast(new HttpServerCodec()); |
| |
| pipeline.addLast(new ChunkedWriteHandler()); |
| |
| |
| |
| |
| |
| |
| pipeline.addLast(new HttpObjectAggregator(8192)); |
| |
| |
| |
| |
| |
| |
| |
| |
| pipeline.addLast(new WebSocketServerProtocolHandler("/hello2")); |
| |
| |
| pipeline.addLast(new MyTextWebSocketFrameHandler()); |
| } |
| }); |
| |
| |
| ChannelFuture channelFuture = serverBootstrap.bind(7000).sync(); |
| channelFuture.channel().closeFuture().sync(); |
| |
| }finally { |
| bossGroup.shutdownGracefully(); |
| workerGroup.shutdownGracefully(); |
| } |
| } |
| } |
| |
| public class MyTextWebSocketFrameHandler 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 { |
| |
| 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(); |
| } |
| } |
| <!DOCTYPE html> |
| <html lang="en"> |
| <head> |
| <meta charset="UTF-8"> |
| <title>Title</title> |
| </head> |
| <body> |
| <script> |
| var socket; |
| |
| if(window.WebSocket) { |
| |
| socket = new WebSocket("ws://localhost:7000/hello2"); |
| |
| 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) { |
| return; |
| } |
| if(socket.readyState == WebSocket.OPEN) { |
| |
| 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> |
- 先启动服务端,再打开前端页面



- WebSocketServerProtocolHandler 核心功能是将 http协议升级为 ws协议 , 保持长连接

【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 阿里巴巴 QwQ-32B真的超越了 DeepSeek R-1吗?
· 10年+ .NET Coder 心语 ── 封装的思维:从隐藏、稳定开始理解其本质意义
· 【设计模式】告别冗长if-else语句:使用策略模式优化代码结构
· 字符编码:从基础到乱码解决
· 提示词工程——AI应用必不可少的技术
2021-08-09 错误处理
2021-08-09 spring boot文件上传