使用Netty处理WebSocket请求

使用Netty处理WebSocket请求


前言

前面我根据网上的一些资料,整理了一个使用Netty处理WebSocket的案例代码出来SpringBoot整合Netty处理WebSocket(支持url参数),后来觉得那份代码不是最优解,因此,我决定重新写了以下代码,更好的处理URL和请求头。


依赖

<dependency>
  <groupId>io.netty</groupId>
  <artifactId>netty-all</artifactId>
  <version>4.1.51.Final</version>
</dependency>

服务端代码

@Slf4j
public class WebSocketServer implements Closeable {

  private EventLoopGroup bossGroup;
  private EventLoopGroup workGroup;
  private ChannelFuture future;

  public WebSocketServer(int port, String websocketPath) throws InterruptedException {
    log.info("Netty WebSocket server starting...");
    try {
      this.bossGroup = new NioEventLoopGroup(2);
      this.workGroup = new NioEventLoopGroup(2);

      ServerBootstrap bootstrap = new ServerBootstrap();
      bootstrap.group(bossGroup, workGroup)
          .option(ChannelOption.SO_BACKLOG, 1 << 10)
          .channel(NioServerSocketChannel.class)
          .localAddress(port)
          .childHandler(new ChannelInitializer<SocketChannel>() {
            @Override
            protected void initChannel(SocketChannel channel) {
              channel.pipeline()
                  .addLast(new HttpServerCodec())
                  .addLast(new ChunkedWriteHandler())
                  .addLast(new HttpObjectAggregator(1 << 13))
                  .addLast(new WebSocketServerProtocolHandler(websocketPath, true))
                  .addLast(new WebSocketMessageHandler(websocketPath));
            }
          });

      future = bootstrap.bind().sync();
    } finally {
      if (future != null && future.isSuccess())
        log.info("Netty WebSocket server started.");
      else
        close();
    }
  }

  @Override
  public void close() {
    log.info("Netty WebSocket server closing...");
    try {
      if (future != null)
        future = future.channel().closeFuture();
      if (bossGroup != null)
        bossGroup.shutdownGracefully().sync();
      if (workGroup != null)
        workGroup.shutdownGracefully().sync();
      if (future != null)
        future.sync();
    } catch (InterruptedException e) {
      log.error(e.getMessage(), e);
    }
    log.info("Netty WebSocket server closed.");
  }
}


连接、消息处理

@Slf4j
@RequiredArgsConstructor
public class WebSocketMessageHandler extends SimpleChannelInboundHandler<WebSocketFrame> {

  private final String websocketPath;
  private ChannelHandlerContext ctx;

  @Override
  public void channelActive(ChannelHandlerContext ctx) throws Exception {
    super.channelActive(ctx);
    this.ctx = ctx;
    log.info("客户端连接:{}", ctx.channel().id().asLongText());
  }

  @Override
  public void channelInactive(ChannelHandlerContext ctx) throws Exception {
    super.channelInactive(ctx);
    log.info("客户端断开:{}", ctx.channel().id().asLongText());
  }

  @Override
  public void userEventTriggered(ChannelHandlerContext ctx, Object evt) throws Exception {
    super.userEventTriggered(ctx, evt);
    if (evt instanceof WebSocketServerProtocolHandler.HandshakeComplete) {
      WebSocketServerProtocolHandler.HandshakeComplete complete = (WebSocketServerProtocolHandler.HandshakeComplete) evt;
      String uri = complete.requestUri(); // 获取请求路径,接着根据自己的业务逻辑判断以及获取自己想要的参数
      String regex = "^" + websocketPath + "/(\\w+)$";
      if (!uri.matches(regex)) {
        log.info("请求路径不合法:{}", uri);
        ctx.close();
        return;
      }
      log.info("用户名:{}", uri.replaceAll(regex, "$1"));
    }
  }

  @Override
  protected void channelRead0(ChannelHandlerContext ctx, WebSocketFrame msg) {
    if (msg instanceof TextWebSocketFrame) {
      TextWebSocketFrame frame = (TextWebSocketFrame) msg;
      log.info("文本消息内容:{}", frame.text());
    } else if (msg instanceof BinaryWebSocketFrame) {
//      BinaryWebSocketFrame frame = (BinaryWebSocketFrame) msg;
      log.info("接收到二进制消息");
    }
  }

  @Override
  public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) {
    log.error(cause.getMessage(), cause);
  }

  public void writeAndFlush(String msg) {
    this.ctx.writeAndFlush(new TextWebSocketFrame(msg));
  }
}

程序演示

public static void main(String[] args) throws InterruptedException {
  WebSocketServer server = new WebSocketServer(8012, "/ws");
  Runtime.getRuntime().addShutdownHook(new Thread(server::close));
}
在Ruoyi微服务框架中使用Netty集成WebSocket,你可以按照以下步骤进行操作: 1. 首先,在你的Ruoyi微服务项目中,添加NettyWebSocket的依赖。可以在pom.xml文件中添加以下依赖: ``` <dependency> <groupId>io.netty</groupId> <artifactId>netty-all</artifactId> <version>4.1.63.Final</version> </dependency> <dependency> <groupId>io.netty</groupId> <artifactId>netty-transport-native-epoll</artifactId> <version>4.1.63.Final</version> <classifier>linux-x86_64</classifier> <scope>optional</scope> </dependency> <dependency> <groupId>io.netty</groupId> <artifactId>netty-transport-native-kqueue</artifactId> <version>4.1.63.Final</version> <classifier>osx-x86_64</classifier> <scope>optional</scope> </dependency> ``` 2. 创建一个Netty WebSocket服务器类,用于启动和管理WebSocket服务器。可以创建一个类,例如NettyWebSocketServer.java,并在其中编写以下代码: ```java public class NettyWebSocketServer { public void run(int port) { EventLoopGroup bossGroup = new NioEventLoopGroup(1); EventLoopGroup workerGroup = new NioEventLoopGroup(); try { ServerBootstrap bootstrap = new ServerBootstrap(); bootstrap.group(bossGroup, workerGroup) .channel(NioServerSocketChannel.class) .childHandler(new ChannelInitializer<SocketChannel>() { @Override protected void initChannel(SocketChannel ch) throws Exception { ChannelPipeline pipeline = ch.pipeline(); pipeline.addLast(new HttpServerCodec()); pipeline.addLast(new HttpObjectAggregator(65536)); pipeline.addLast(new WebSocketServerProtocolHandler("/websocket")); pipeline.addLast(new WebSocketServerHandler()); } }); ChannelFuture future = bootstrap.bind(port).sync(); future.channel().closeFuture().sync(); } catch (Exception e) { e.printStackTrace(); } finally { bossGroup.shutdownGracefully(); workerGroup.shutdownGracefully(); } } public static void main(String[] args) { NettyWebSocketServer server = new NettyWebSocketServer(); server.run(8080); } } ``` 3. 创建一个WebSocket服务器处理器类,实现Netty的SimpleChannelInboundHandler接口。在这个类中,你可以重写一些方法,例如`channelActive`用于处理连接建立时的逻辑,`channelRead0`用于处理接收到的消息等。 4. 在Ruoyi微服务框架的Controller中,你可以使用Netty WebSocket服务器来处理WebSocket连接和消息。例如,在某个请求处理方法中,可以通过Netty WebSocket服务器来发送消息给客户端。 这样,你就可以在Ruoyi微服务框架中使用Netty集成WebSocket了。希望能对你有所帮助!如果还有其他问题,请随时提问。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值