WebSocketIndexPageHandler.java 5.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152
  1. package com.dzdy.dim.handler.websocket;
  2. import io.netty.buffer.ByteBuf;
  3. import io.netty.buffer.Unpooled;
  4. import io.netty.channel.*;
  5. import io.netty.handler.codec.http.*;
  6. import io.netty.handler.codec.http.websocketx.WebSocketServerHandshaker;
  7. import io.netty.handler.codec.http.websocketx.WebSocketServerHandshakerFactory;
  8. import io.netty.handler.ssl.SslHandler;
  9. import io.netty.util.CharsetUtil;
  10. import static io.netty.handler.codec.http.HttpHeaderNames.CONNECTION;
  11. import static io.netty.handler.codec.http.HttpHeaderNames.CONTENT_TYPE;
  12. import static io.netty.handler.codec.http.HttpHeaderValues.CLOSE;
  13. import static io.netty.handler.codec.http.HttpHeaderValues.KEEP_ALIVE;
  14. import static io.netty.handler.codec.http.HttpMethod.GET;
  15. import static io.netty.handler.codec.http.HttpResponseStatus.*;
  16. import static io.netty.handler.codec.http.HttpVersion.HTTP_1_0;
  17. import static io.netty.handler.codec.http.HttpVersion.HTTP_1_1;
  18. /**
  19. * @author : wangzhiyong
  20. * @date : 2019/6/18 14:58
  21. * description :
  22. */
  23. @ChannelHandler.Sharable
  24. public class WebSocketIndexPageHandler extends SimpleChannelInboundHandler<FullHttpRequest> {
  25. public static final WebSocketIndexPageHandler INSTANCE = new WebSocketIndexPageHandler();
  26. private static final String WEBSOCKET_PATH = "/websocket";
  27. private WebSocketIndexPageHandler() {
  28. }
  29. @Override
  30. protected void channelRead0(ChannelHandlerContext ctx, FullHttpRequest req) throws Exception {
  31. if (updateProtocol(ctx, req)) {
  32. return;
  33. }
  34. // Handle a bad request.
  35. if (!req.decoderResult().isSuccess()) {
  36. sendHttpResponse(ctx, req, new DefaultFullHttpResponse(HTTP_1_1, BAD_REQUEST));
  37. return;
  38. }
  39. // Allow only GET methods.
  40. if (!GET.equals(req.method())) {
  41. sendHttpResponse(ctx, req, new DefaultFullHttpResponse(HTTP_1_1, FORBIDDEN));
  42. return;
  43. }
  44. // Send the index page
  45. if ("/".equals(req.uri()) || "/index.html".equals(req.uri())) {
  46. String webSocketLocation = getWebSocketLocation(ctx.pipeline(), req, WEBSOCKET_PATH);
  47. ByteBuf content = WebSocketServerIndexPage.getContent(webSocketLocation);
  48. FullHttpResponse res = new DefaultFullHttpResponse(HTTP_1_1, OK, content);
  49. res.headers().set(CONTENT_TYPE, "text/html; charset=UTF-8");
  50. HttpUtil.setContentLength(res, content.readableBytes());
  51. sendHttpResponse(ctx, req, res);
  52. } else {
  53. sendHttpResponse(ctx, req, new DefaultFullHttpResponse(HTTP_1_1, NOT_FOUND));
  54. }
  55. }
  56. /**
  57. * 协议升级
  58. * WebSocket是一种全新的协议,不属于http无状态协议,协议名为"ws",这意味着一个websocket连接地址会是这样的写法:
  59. * ws://127.0.0.1:8080/websocket。ws不是http,所以传统的web服务器不一定支持,需要服务器与浏览器同时支持, WebSocket才能正常运行,目前大部分浏览器都支持Websocket。
  60. * WebSocketServerHandshaker 服务器端Web套接字打开和关闭握手基类
  61. * WebSocketServerHandshakerFactory 自动检测正在使用的网络套接字协议的版本,并创建一个新的合适的 WebSocketServerHandshaker。
  62. *
  63. * @param ctx
  64. * @param req
  65. * @return
  66. */
  67. private boolean updateProtocol(ChannelHandlerContext ctx, FullHttpRequest req) {
  68. if ("websocket".equals(req.headers().get("Upgrade"))) {
  69. WebSocketServerHandshakerFactory wsFactory = new WebSocketServerHandshakerFactory("ws://localhost:9011/websocket", null, false);
  70. //创建一个握手协议
  71. WebSocketServerHandshaker handshaker = wsFactory.newHandshaker(req);
  72. if (handshaker == null) {
  73. /*
  74. Return that we need cannot not support the web socket version
  75. 返回不支持websocket 版本
  76. */
  77. WebSocketServerHandshakerFactory.sendUnsupportedVersionResponse(ctx.channel());
  78. } else {
  79. //开始握手
  80. handshaker.handshake(ctx.channel(), req);
  81. }
  82. return true;
  83. }
  84. return false;
  85. }
  86. /**
  87. * 返回http消息
  88. *
  89. * @param ctx
  90. * @param req
  91. * @param res
  92. */
  93. private static void sendHttpResponse(ChannelHandlerContext ctx, FullHttpRequest req, FullHttpResponse res) {
  94. // Generate an error page if response getStatus code is not OK (200).
  95. if (res.status().code() != 200) {
  96. ByteBuf buf = Unpooled.copiedBuffer(res.status().toString(), CharsetUtil.UTF_8);
  97. res.content().writeBytes(buf);
  98. buf.release();
  99. HttpUtil.setContentLength(res, res.content().readableBytes());
  100. }
  101. // Send the response and close the connection if necessary.
  102. if (!HttpUtil.isKeepAlive(req) || res.status().code() != 200) {
  103. // Tell the client we're going to close the connection.
  104. res.headers().set(CONNECTION, CLOSE);
  105. ctx.writeAndFlush(res).addListener(ChannelFutureListener.CLOSE);
  106. } else {
  107. if (req.protocolVersion().equals(HTTP_1_0)) {
  108. res.headers().set(CONNECTION, KEEP_ALIVE);
  109. }
  110. ctx.writeAndFlush(res);
  111. }
  112. }
  113. /**
  114. * 获取websocket地址
  115. *
  116. * @param cp
  117. * @param req
  118. * @param path
  119. * @return
  120. */
  121. private static String getWebSocketLocation(ChannelPipeline cp, HttpRequest req, String path) {
  122. String protocol = "ws";
  123. if (cp.get(SslHandler.class) != null) {
  124. // SSL in use so use Secure WebSockets
  125. protocol = "wss";
  126. }
  127. return protocol + "://" + req.headers().get(HttpHeaderNames.HOST) + path;
  128. }
  129. @Override
  130. public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
  131. cause.printStackTrace();
  132. ctx.close();
  133. }
  134. }