image.png

Server
  1. public class Server {
  2. public static void main(String[] args) throws InterruptedException {
  3. EventLoopGroup bossGroup = new NioEventLoopGroup(1);
  4. EventLoopGroup workerGroup = new NioEventLoopGroup();
  5. try {
  6. ServerBootstrap serverBootstrap = new ServerBootstrap();
  7. serverBootstrap.group(bossGroup, workerGroup)
  8. .channel(NioServerSocketChannel.class)
  9. .handler(new LoggingHandler(LogLevel.INFO))
  10. .childHandler(new ChannelInitializer<SocketChannel>() {
  11. @Override
  12. protected void initChannel(SocketChannel ch) throws Exception {
  13. ChannelPipeline pipeline = ch.pipeline();
  14. //使用Http的编解码器
  15. pipeline.addLast(new HttpServerCodec());
  16. //以块方式写,添加ChunkedWriteHandler处理器
  17. pipeline.addLast(new ChunkedWriteHandler());
  18. /*
  19. 1.Http协议在传输过程中是分段的,HttpObjectAggregator就是可以将多个段聚合
  20. 2.这就是为什么当浏览器发送大量数据时,就会发出多次http请求的原因
  21. */
  22. pipeline.addLast(new HttpObjectAggregator(8192));
  23. /*
  24. 1.对应WebSocket,它的数据是以帧(frame)的形式传递
  25. 2.可以看到WEbSocketFrame 下面有六个子类
  26. WebSocketServerProtocolHandler作用:
  27. 3.浏览器发送请求时 ws://localhost:7000/hello--识别请求的资源
  28. 4.将一个Http协议升级为WS协议,即WebSocket协议--保存长连接
  29. */
  30. pipeline.addLast(new WebSocketServerProtocolHandler("/hello"));
  31. //自定义handler,处理业务逻辑
  32. pipeline.addLast(new TextWebSocketFrameHandler());
  33. }
  34. });
  35. ChannelFuture future = serverBootstrap.bind(7000).sync();
  36. future.channel().closeFuture().sync();
  37. } finally {
  38. bossGroup.shutdownGracefully();
  39. workerGroup.shutdownGracefully();
  40. }
  41. }
  42. }

Handler
  1. //TextWebSocketFrame表示一个文本帧(frame)
  2. public class TextWebSocketFrameHandler extends SimpleChannelInboundHandler<TextWebSocketFrame> {
  3. @Override
  4. protected void channelRead0(ChannelHandlerContext ctx, TextWebSocketFrame msg) throws Exception {
  5. System.out.println("服务器收到消息" + msg.text());
  6. //回复消息
  7. ctx.channel().writeAndFlush(new TextWebSocketFrame("服务器时间" + LocalDateTime.now() + "" + msg.text()));
  8. }
  9. //感知连接
  10. @Override
  11. public void handlerAdded(ChannelHandlerContext ctx) throws Exception {
  12. //id是一个唯一的值, LongText是唯一的
  13. System.out.println("handlerAdded 被调用" + ctx.channel().id().asLongText());
  14. //ShortText不是唯一的
  15. System.out.println("handlerAdded 被调用" + ctx.channel().id().asShortText());
  16. }
  17. //感知断开连接
  18. @Override
  19. public void handlerRemoved(ChannelHandlerContext ctx) throws Exception {
  20. System.out.println("handlerRemoved被调用" + ctx.channel().id().asLongText());
  21. }
  22. //异常
  23. @Override
  24. public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
  25. System.out.println(cause.getMessage());
  26. ctx.close(); //关闭连接
  27. }
  28. }
  1. <!DOCTYPE html>
  2. <html lang="en">
  3. <head>
  4. <meta charset="UTF-8">
  5. <title>Title</title>
  6. </head>
  7. <!--复制到Vscode-->
  8. <body>
  9. <script>
  10. var socket;
  11. //判断当前浏览器是否支持websocket
  12. if(window.WebSocket) {
  13. //go on
  14. socket = new WebSocket("ws://localhost:7000/hello");
  15. //相当于channelReado, ev 收到服务器端回送的消息
  16. socket.onmessage = function (ev) {
  17. var rt = document.getElementById("responseText");
  18. rt.value = rt.value + "\n" + ev.data;
  19. }
  20. //相当于连接开启(感知到连接开启)
  21. socket.onopen = function (ev) {
  22. var rt = document.getElementById("responseText");
  23. rt.value = "连接开启了.."
  24. }
  25. //相当于连接关闭(感知到连接关闭)
  26. socket.onclose = function (ev) {
  27. var rt = document.getElementById("responseText");
  28. rt.value = rt.value + "\n" + "连接关闭了.."
  29. }
  30. } else {
  31. alert("当前浏览器不支持websocket")
  32. }
  33. //发送消息到服务器
  34. function send(message) {
  35. if(!window.socket) { //先判断socket是否创建好
  36. return;
  37. }
  38. if(socket.readyState == WebSocket.OPEN) {
  39. //通过socket 发送消息
  40. socket.send(message)
  41. } else {
  42. alert("连接没有开启");
  43. }
  44. }
  45. </script>
  46. <form onsubmit="return false">
  47. <textarea name="message" style="height: 300px; width: 300px"></textarea>
  48. <input type="button" value="发送消息" onclick="send(this.form.message.value)">
  49. <textarea id="responseText" style="height: 300px; width: 300px"></textarea>
  50. <input type="button" value="清空内容" onclick="document.getElementById('responseText').value=''">
  51. </form>
  52. </body>
  53. </html>