All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.todostudy.iot.mqtt.server.handler.TextWebSocketFrameHandler Maven / Gradle / Ivy

package com.todostudy.iot.mqtt.server.handler;

import com.todostudy.iot.mqtt.server.api.IWebSocketService;
import com.todostudy.iot.mqtt.server.api.bo.WsAuthBo;
import com.todostudy.iot.mqtt.server.common.Tools;
import com.todostudy.iot.mqtt.server.common.session.SessionStore;
import com.todostudy.iot.mqtt.server.session.SessionStoreService;
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.FullHttpResponse;
import io.netty.handler.codec.http.websocketx.TextWebSocketFrame;
import io.netty.handler.codec.http.websocketx.WebSocketCloseStatus;
import io.netty.handler.codec.http.websocketx.WebSocketServerProtocolHandler;
import io.netty.handler.timeout.IdleState;
import io.netty.handler.timeout.IdleStateEvent;
import io.netty.handler.timeout.IdleStateHandler;
import io.netty.util.AttributeKey;
import lombok.extern.slf4j.Slf4j;

import java.util.Map;
import java.util.concurrent.TimeUnit;

import static io.netty.handler.codec.http.HttpResponseStatus.UNAUTHORIZED;
import static io.netty.handler.codec.http.HttpVersion.HTTP_1_1;

@Slf4j
public class TextWebSocketFrameHandler extends SimpleChannelInboundHandler {

    private final SessionStoreService sessionStoreService;
    private final IWebSocketService iWebSocketService;
    private final String urlPath;

    public TextWebSocketFrameHandler(SessionStoreService sessionStoreService, IWebSocketService iWebSocketService, String urlPath) {
        this.sessionStoreService = sessionStoreService;
        this.iWebSocketService = iWebSocketService;
        this.urlPath = urlPath;
    }

    @Override
    public void channelRead0(ChannelHandlerContext ctx, TextWebSocketFrame msg) throws Exception {
        Object id = ctx.channel().attr(AttributeKey.valueOf(Tools.clientId)).get();
        if (id != null) {
            String message = msg.text();
            if(message.startsWith("Heartbeat")){ //心跳处理
                log.debug("ping Heartbeat");
            }else {
                iWebSocketService.onMessage(id.toString(), message);
            }
        } else {
            //未登录
            sendHttpResponse(ctx, new DefaultFullHttpResponse(HTTP_1_1, UNAUTHORIZED));
            ctx.close();
        }
    }

    @Override
    public void userEventTriggered(ChannelHandlerContext ctx, Object evt) throws Exception {

        if (evt instanceof WebSocketServerProtocolHandler.HandshakeComplete) {
            WebSocketServerProtocolHandler.HandshakeComplete complete = (WebSocketServerProtocolHandler.HandshakeComplete) evt;
            String uri = complete.requestUri();
            Map urlParams = Map.of();
            //得到参数。url传参和form传参 二选一
            if (null != uri && uri.contains(Tools.S_R)) {
                log.debug("===>uri:{}", uri); //处理uri参数数据
                urlParams = Tools.parseUrlParams(uri.substring(uri.indexOf(Tools.S_R) + 1, uri.length()));

            } else {
                //Head传值不用处理。
            }
            WsAuthBo wsAuthBo = iWebSocketService.verifyAuth(complete.requestHeaders(), urlParams);
            if (wsAuthBo.isLogin()) {
                ctx.channel().attr(AttributeKey.valueOf(Tools.clientId)).set(wsAuthBo.getSessionId());
                sessionStoreService.put(wsAuthBo.getSessionId(), new SessionStore(wsAuthBo.getSessionId(), ctx.channel()));
                log.debug("登录成功! {}", wsAuthBo.getSessionId());

            } else {
                sendHttpResponse(ctx, new DefaultFullHttpResponse(HTTP_1_1, UNAUTHORIZED));
                // log.debug("登录失败!");
                ctx.close();
            }
        }
        /**
         * 主动断开,jwt方式可以60s后断开和http差不多,长时间连接增加性能开销。 这个时间可以调长在心跳包里面改可以改成 120s
         * 如果做成iot监控设备上线,就要改成心跳包,方式。ws一般很少用于iot设备。多用于web长连接。jwt+60s连接时间是最佳的选择。
         * ws客户端需要发送 心跳包 ws.send('{"event":"ping","content":"Heartbeat Packet"}'); 或者 ws.send('Heartbeat Packet');
         */
        else if (evt instanceof IdleStateEvent) {
            IdleStateEvent event = (IdleStateEvent) evt;
            if (event.state() == IdleState.ALL_IDLE) {//
                // 连接已经断开,执行相应的断开连接逻辑
                log.debug("IdleStateEvent 关闭,状态码:{}", event.state());
                ctx.close();
            }
        }
    }

    private void sendHttpResponse(ChannelHandlerContext ctx, FullHttpResponse response) {
        // 发送HTTP响应
        ctx.writeAndFlush(response).addListener(ChannelFutureListener.CLOSE);
    }

    //有新的连接建立时 每个channel都有一个唯一的id值
  /*  @Override
    public void handlerAdded(ChannelHandlerContext ctx) throws Exception {

    }*/

    @Override
    public void channelActive(ChannelHandlerContext ctx) throws Exception {
        log.debug("客户端连接:{}", ctx.channel().id());
        // 当WebSocket连接激活时,添加IdleStateHandler
       // ctx.pipeline().addAfter("IdleStateHandler", new IdleStateHandler(0, 0, 60, TimeUnit.SECONDS));
        super.channelActive(ctx);
    }

    @Override
    public void channelInactive(ChannelHandlerContext ctx) throws Exception {
        Object id = ctx.channel().attr(AttributeKey.valueOf(Tools.clientId)).get();
        log.debug("客户端断开连接:{}", id);
        if (id != null) {
            iWebSocketService.offline(id.toString());
            sessionStoreService.remove(id.toString());
        }
        super.channelInactive(ctx);
    }


   /* @Override
    public void channelRegistered(ChannelHandlerContext ctx) throws Exception {
        super.channelRegistered(ctx);
    }

    @Override
    public void channelUnregistered(ChannelHandlerContext ctx) throws Exception {
        super.channelUnregistered(ctx);
    }*/

    @Override
    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
        Object id = ctx.channel().attr(AttributeKey.valueOf(Tools.clientId)).get();
        if (id != null) {
            log.debug("客户端断开连接:{}", id);
            ctx.close();
            iWebSocketService.offline(id.toString());
            sessionStoreService.remove(id.toString());
        }
        super.exceptionCaught(ctx, cause);
    }

}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy