使用RocketMQ 和 Netty WebSocket 搭建一个消息服务器 (二)

这篇是关于Netty Websocket服务器的使用,不废话直接上代码.

环境同样是spring boot , maven依赖 网上找.

首先是webSocketServer :

public class WebSocketServer {

public void startService() throws Exception {

    //创建两个组

    EventLoopGroup bossGroup = new NioEventLoopGroup(1);
    EventLoopGroup workerGroup = new NioEventLoopGroup();

    try {

     //加载一些配置信息----端口号  信道了什么的

      ServerBootstrap b = new ServerBootstrap();
      b.group(bossGroup, workerGroup).channel(NioServerSocketChannel.class).option(ChannelOption.SO_BACKLOG, 1024)
          .option(ChannelOption.TCP_NODELAY, true).handler(new LoggingHandler(LogLevel.INFO))
          .childHandler(new WebSocketServerInitializer());
      Channel ch = b.bind(8080).sync().channel();
      // 阻塞处理,等待服务端链路关闭之后main函数才退出
      ch.closeFuture().sync();
    } finally {
      bossGroup.shutdownGracefully();
      workerGroup.shutdownGracefully();
    }

  }

}

我把static的main 方法该成这样,启动的话可以到spring boot的启动类注入这个类进行启动:

@SpringBootApplication
@ComponentScan("com.zy")      //包扫描
public class Application {
  public static void main(String[] args) {
    ApplicationContext app = SpringApplication.run(Application.class, args);
    WebSocketServer webSocketServer = app.getBean(WebSocketServer.class);
    try {
      webSocketServer.startService();
    } catch (Exception e) {
      e.printStackTrace();

    }

}

以上部分是服务器完整部分,下面这个处理器需要好好研究下,因为我把业务代码删除了.对于刚布置的人来说可能缺少点东西需要自己去添加(我看了下应该不会报错!!).

接下来是websocket的一个处理器:

package com.zy.service.netty;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.zy.service.netty.global.Global;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelFutureListener;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.SimpleChannelInboundHandler;
import io.netty.handler.codec.http.DefaultFullHttpResponse;
import io.netty.handler.codec.http.FullHttpRequest;
import io.netty.handler.codec.http.HttpResponseStatus;
import io.netty.handler.codec.http.HttpVersion;
import io.netty.handler.codec.http.websocketx.PingWebSocketFrame;
import io.netty.handler.codec.http.websocketx.PongWebSocketFrame;
import io.netty.handler.codec.http.websocketx.TextWebSocketFrame;
import io.netty.handler.codec.http.websocketx.WebSocketFrame;
import io.netty.handler.codec.http.websocketx.WebSocketServerHandshaker;
import io.netty.handler.codec.http.websocketx.WebSocketServerHandshakerFactory;
import io.netty.util.CharsetUtil;

public class WebSocketFrameHandler extends SimpleChannelInboundHandler<Object> {
  private static final Logger logger = LoggerFactory.getLogger(WebSocketFrameHandler.class.getName());
  private WebSocketServerHandshaker handshaker;
  /**
   * websocket请求入口
   * 
   * @param ctx
   * @param msg
   * @throws Exception
   */
  public void handlerWebSocketFrame(ChannelHandlerContext ctx, WebSocketFrame frame) throws Exception {
    // ping请求
    if (frame instanceof PingWebSocketFrame) {
      ctx.channel().write(new PongWebSocketFrame(frame.content().retain()));
      return;
    }
    // 只支持文本格式,不支持二进制消息
    if (!(frame instanceof TextWebSocketFrame)) {
      throw new Exception("仅支持文本格式");
    }
    // 客服端发送过来的消息
    String request = ((TextWebSocketFrame) frame).text();
    System.err.println("服务端收到:" + request);
  }
  /**
   * http
   * 
   * @param ctx
   * @param msg
   */
  public void handleHttpRequest(ChannelHandlerContext ctx, FullHttpRequest req) {
    if (!req.decoderResult().isSuccess()) {
      sendHttpResponse(ctx, req, new DefaultFullHttpResponse(HttpVersion.HTTP_1_1, HttpResponseStatus.BAD_REQUEST));
      return;
    }

    WebSocketServerHandshakerFactory wsFactory = new WebSocketServerHandshakerFactory(
        "ws:/" + ctx.channel() + "/websocket", null, false);
    handshaker = wsFactory.newHandshaker(req);

    if (handshaker == null) {
      // 不支持
      WebSocketServerHandshakerFactory.sendUnsupportedVersionResponse(ctx.channel());
    } else {
      handshaker.handshake(ctx.channel(), req);
    }
  }
  private void sendHttpResponse(ChannelHandlerContext ctx, FullHttpRequest req, DefaultFullHttpResponse res) {
    // 返回应答给客户端
    if (res.status().code() != 200) {
      ByteBuf buf = Unpooled.copiedBuffer(res.status().toString(), CharsetUtil.UTF_8);
      res.content().writeBytes(buf);
      buf.release();
    }
    // 如果是非Keep-Alive,关闭连接
    ChannelFuture f = ctx.channel().writeAndFlush(res);
    if (!isKeepAlive(req) || res.status().code() != 200) {
      f.addListener(ChannelFutureListener.CLOSE);
    }
  }
  private boolean isKeepAlive(FullHttpRequest req) {
    return false;
  }

  /**

   * websocket的入口方法:客户端第一次请求websocket第一次请求发送的信息是OBject类的进入websocket服务器会默认第一个调用这个方法将请求分为http和websocket我们只需要在websocket的请求中进行业务处理就可以了http不用管,因为我们做的是websocket服务,可以发现代码中将Object类信息强转成了websocket类信息.
   */
  @Override
  protected void channelRead0(ChannelHandlerContext ctx, Object msg) throws Exception {
    if (msg instanceof FullHttpRequest) {
      handleHttpRequest(ctx, (FullHttpRequest) msg);
    } else if (msg instanceof WebSocketFrame) {
      // ws://xxxx
      handlerWebSocketFrame(ctx, (WebSocketFrame) msg);
    }
  }
  /**
   * 客户端断开连接
   */
  @Override
  public void channelInactive(ChannelHandlerContext ctx) throws Exception {
    String key = ctx.channel().attr(Global.ATTRIBUTE_KEY).get();
    if (key != null) {
      Global.removeGroup(key, ctx.channel());
    }
  }
  /**
   * 心跳检测
   */
  @Override
  public void channelActive(ChannelHandlerContext ctx) throws Exception {
    super.channelActive(ctx);
  }
  /**
   * 服务端断开连接
   */
  @Override
  public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
    // 出异常的时候关闭channel
    ctx.close();
  }

}

服务器的初始化:

package com.zy.service.netty;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.socket.SocketChannel;
import io.netty.handler.codec.http.HttpObjectAggregator;
import io.netty.handler.codec.http.HttpServerCodec;
import io.netty.handler.stream.ChunkedWriteHandler;
public class WebSocketServerInitializer extends ChannelInitializer<SocketChannel> {

  @Override
  public void initChannel(SocketChannel ch) throws Exception {
//    ChannelPipeline pipeline = ch.pipeline();
    ch.pipeline().addLast("http-codec", new HttpServerCodec());
    ch.pipeline().addLast("aggregator", new HttpObjectAggregator(65536));
    ch.pipeline().addLast("http-chunked", new ChunkedWriteHandler());
    ch.pipeline().addLast("handler", new WebSocketFrameHandler());
  }

}

前端页面:

<html>
<head>
<meta charset="UTF-8">
<title>netty</title>
</head>
<body>
<script type="text/javascript">
    var socket;
    if (!window.WebSocket) {
      window.WebSocket = window.MozWebSocket;
    }
    if (window.WebSocket) {
      socket = new WebSocket("ws://localhost:8080");
      socket.onmessage = function(event) {
        var d = document.getElementById('responseText');
        d.value = d.value + "\n" + event.data;
      };
      socket.onopen = function(event) {
        console.log("websocket 打开了");
      };
      socket.onclose = function(event) {
        console.log("websocket 关闭了");
      };
    }
    function send(message) {
      if (!window.WebSocket) {
        return;
      }
      if (socket.readyState == WebSocket.OPEN) {
        socket.send(message);
      } else {
        alert("The socket is not open.");
      }
    }
  </script>
<h3>Output</h3>
<textarea id="responseText" style="width: 500px; height: 300px;" autocomplete="false"></textarea>
</body>

</html>


如果项目跑不起来应该是我删的东西删多了,我把对应的业务代码都删除了只剩下基本的一个websocket服务器,如果出什么错误可以根据我加的注释自己把缺失的部分补齐...


评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值