com.farsunset.cim.sdk.server.handler.CIMNioSocketAcceptor.java Source code

Java tutorial

Introduction

Here is the source code for com.farsunset.cim.sdk.server.handler.CIMNioSocketAcceptor.java

Source

/**
 * Copyright 2013-2023 Xia Jun(3979434@qq.com).
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 *
 ***************************************************************************************
 *                                                                                     *
 *                        Website : http://www.farsunset.com                           *
 *                                                                                     *
 ***************************************************************************************
 */
package com.farsunset.cim.sdk.server.handler;

import java.io.IOException;
import java.util.HashMap;
import java.util.concurrent.ConcurrentHashMap;

import com.farsunset.cim.sdk.server.constant.CIMConstant;
import com.farsunset.cim.sdk.server.filter.ServerMessageDecoder;
import com.farsunset.cim.sdk.server.filter.ServerMessageEncoder;
import com.farsunset.cim.sdk.server.model.HeartbeatRequest;
import com.farsunset.cim.sdk.server.model.SentBody;
import com.farsunset.cim.sdk.server.session.CIMSession;

import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.Channel;
import io.netty.channel.ChannelHandler.Sharable;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.ChannelOption;
import io.netty.channel.SimpleChannelInboundHandler;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioServerSocketChannel;
import io.netty.handler.logging.LogLevel;
import io.netty.handler.logging.LoggingHandler;
import io.netty.handler.timeout.IdleState;
import io.netty.handler.timeout.IdleStateEvent;
import io.netty.handler.timeout.IdleStateHandler;
import io.netty.util.AttributeKey;

@Sharable
public class CIMNioSocketAcceptor extends SimpleChannelInboundHandler<SentBody> {

    /**
     * websocket??handler
     */
    public final static String WEBSOCKET_HANDLER_KEY = "client_websocket_handshake";
    /**
     * ?handler
     */
    public final static String CIMSESSION_CLOSED_HANDLER_KEY = "client_closed";

    private HashMap<String, CIMRequestHandler> innerHandlerMap = new HashMap<String, CIMRequestHandler>();
    private CIMRequestHandler outerRequestHandler;
    private ConcurrentHashMap<String, Channel> channelGroup = new ConcurrentHashMap<String, Channel>();
    private int port;

    // 
    public static final int READ_IDLE_TIME = 150;// 

    // 
    public static final int WRITE_IDLE_TIME = 120;// 

    public static final int PING_TIME_OUT = 30;// ? 30

    public void bind() throws IOException {

        /**
         * websocket??
         */
        innerHandlerMap.put(WEBSOCKET_HANDLER_KEY, new WebsocketHandler());

        ServerBootstrap bootstrap = new ServerBootstrap();
        bootstrap.group(new NioEventLoopGroup(), new NioEventLoopGroup());
        bootstrap.childOption(ChannelOption.TCP_NODELAY, true);
        bootstrap.childOption(ChannelOption.SO_KEEPALIVE, true);
        bootstrap.channel(NioServerSocketChannel.class);
        bootstrap.childHandler(new ChannelInitializer<SocketChannel>() {
            @Override
            public void initChannel(SocketChannel ch) throws Exception {

                ch.pipeline().addLast(new ServerMessageDecoder());
                ch.pipeline().addLast(new ServerMessageEncoder());
                ch.pipeline().addLast(new LoggingHandler(LogLevel.INFO));
                ch.pipeline().addLast(new IdleStateHandler(READ_IDLE_TIME, WRITE_IDLE_TIME, 0));
                ch.pipeline().addLast(CIMNioSocketAcceptor.this);
            }
        });

        bootstrap.bind(port);
    }

    /**
     * sentbody?handler
     * 
     * @param outerRequestHandler
     */
    public void setAppSentBodyHandler(CIMRequestHandler outerRequestHandler) {
        this.outerRequestHandler = outerRequestHandler;
    }

    @Override
    protected void channelRead0(ChannelHandlerContext ctx, SentBody body) throws Exception {

        CIMSession session = new CIMSession(ctx.channel());

        CIMRequestHandler handler = innerHandlerMap.get(body.getKey());
        /**
         * handler??
         */
        if (handler != null) {
            handler.process(session, body);
            return;
        }

        /**
         * ?sentbody
         */
        outerRequestHandler.process(session, body);
    }

    @Override
    public void channelInactive(ChannelHandlerContext ctx) {
        channelGroup.remove(ctx.channel().id().asShortText());

        CIMSession session = new CIMSession(ctx.channel());
        SentBody body = new SentBody();
        body.setKey(CIMSESSION_CLOSED_HANDLER_KEY);
        outerRequestHandler.process(session, body);

    }

    @Override
    public void channelActive(ChannelHandlerContext ctx) {
        channelGroup.put(ctx.channel().id().asShortText(), ctx.channel());
    }

    @Override
    public void userEventTriggered(ChannelHandlerContext ctx, Object evt) throws Exception {
        if (evt instanceof IdleStateEvent && ((IdleStateEvent) evt).state().equals(IdleState.WRITER_IDLE)) {
            ctx.channel().attr(AttributeKey.valueOf(CIMConstant.HEARTBEAT_KEY)).set(System.currentTimeMillis());
            ctx.channel().writeAndFlush(HeartbeatRequest.getInstance());
        }

        // ?30?
        if (evt instanceof IdleStateEvent && ((IdleStateEvent) evt).state().equals(IdleState.READER_IDLE)) {

            Long lastTime = (Long) ctx.channel().attr(AttributeKey.valueOf(CIMConstant.HEARTBEAT_KEY)).get();
            if (lastTime != null && System.currentTimeMillis() - lastTime >= PING_TIME_OUT) {
                ctx.channel().close();
            }

            ctx.channel().attr(AttributeKey.valueOf(CIMConstant.HEARTBEAT_KEY)).set(null);
        }
    }

    public void setPort(int port) {
        this.port = port;
    }

    public Channel getManagedChannel(String id) {
        if (id == null) {
            return null;
        }
        return channelGroup.get(id);
    }
}