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

com.iteaj.iot.client.SocketClient Maven / Gradle / Ivy

package com.iteaj.iot.client;

import com.iteaj.iot.IotCoreConfiguration;
import com.iteaj.iot.IotThreadManager;
import com.iteaj.iot.client.codec.ClientProtocolEncoder;
import com.iteaj.iot.client.handle.ClientServiceHandler;
import com.iteaj.iot.client.protocol.ClientInitiativeProtocol;
import com.iteaj.iot.client.protocol.ClientSocketProtocol;
import com.iteaj.iot.ProtocolException;
import com.iteaj.iot.codec.IotMessageDecoder;
import io.netty.bootstrap.Bootstrap;
import io.netty.channel.*;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.util.concurrent.BlockingOperationException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.net.ConnectException;
import java.net.NoRouteToHostException;
import java.util.concurrent.TimeUnit;
import java.util.function.Consumer;

import static com.iteaj.iot.CoreConst.*;

/**
 * client socket
 * @see TcpSocketClient
 * @see UdpSocketClient
 */
public abstract class SocketClient implements IotClient {

    private Channel channel;
    private Bootstrap bootstrap;
    private volatile boolean reconnectFlag;
    private ClientConnectProperties config;
    private ClientComponent clientComponent;

    public Logger logger = LoggerFactory.getLogger(getClass());

    public SocketClient(ClientComponent clientComponent, ClientConnectProperties config) {
        this.config = config;
        this.clientComponent = clientComponent;
        if(this.config == null) {
            throw new IllegalArgumentException("未指定连接配置[ConnectProperties]");
        }
    }

    public void init(NioEventLoopGroup clientGroup) {
        this.bootstrap = new Bootstrap().group(clientGroup)
                .channel(channel()).handler(new ChannelInitializer() {
                    @Override
                    protected void initChannel(Channel channel) throws Exception {
                        // 关掉原有的链接
                        if(SocketClient.this.channel != null) {
                            SocketClient.this.channel.close();
                        }

                        SocketClient.this.channel = channel;
                        ChannelPipeline pipeline = channel.pipeline();

                        // 设置编解码器
                        ChannelInboundHandler decoder = createProtocolDecoder();
//                        if(decoder instanceof IotMessageDecoder) {
//                            Class messageClass = getClientComponent().getMessageClass();
//                            if(((IotMessageDecoder) decoder).getMessageClass() == null) {
//                                ((IotMessageDecoder) decoder).setMessageClass(messageClass);
//                            }
//                        }

                        // 设置当前客户端连接的服务器配置信息
                        channel.attr(CLIENT_KEY).set(SocketClient.this.getConfig());

                        // 设置iot客户端编解码器
                        pipeline.addFirst(CLIENT_DECODER_HANDLER, decoder);
                        pipeline.addFirst(CLIENT_ENCODER_HANDLER, createProtocolEncoder());

                        // 自定义处理器
                        SocketClient.this.doInitChannel(channel);

                        // 业务处理器新增到最后
                        pipeline.addLast(CLIENT_SERVICE_HANDLER, new ClientServiceHandler(getClientComponent()));
                    }
                });

        this.doInitOptions(this.bootstrap);
    }

    @Override
    public ChannelFuture connect(Consumer consumer, long timeout) {
        try {
            // 只有在未激活的情况下才进行连接
            if(this.getChannel() == null || !this.getChannel().isActive()) {
                return this.doConnect(consumer == null ? (future) -> {
                    if(future.isSuccess()) {
                        if (logger.isDebugEnabled()) {
                            logger.debug("客户端({}) 连接服务器成功 - 远程主机 {}:{} - 客户端标识:{}"
                                    , getClientComponent().getName(), this.getHost(), this.getPort(), getConfig().connectKey());
                        }
                    }
                } : (Consumer) consumer, timeout);
            } else {
                return getChannel().newSucceededFuture();
            }
        } catch (Exception e) {
            return getChannel().newFailedFuture(new ProtocolException("连接失败 " + e.getMessage(), e));
        }
    }

    /**
     * 真正连接服务器的方法
     * @see MultiClientManager#addClient(Object, IotClient) 
* 注意:连接成功后需要加入到客户端管理器 * * @param consumer 可以自定义连接成功或的操作 * @param timeout 指定连接超时时间 */ protected ChannelFuture doConnect(Consumer consumer, long timeout) { return this.bootstrap.connect(getHost(), getPort()).addListener(future -> { try { consumer.accept((ChannelFuture) future); } finally { if (future.isSuccess()) { // 连接成功必须保存, 否则会出现获取不到客户端的情况 clientComponent.addClient(getConfig(), this); this.successCallback((ChannelFuture) future); } else { // 此处重连需要符合其中某个条件 1. 客户端是默认客户端 2. 此客户端连接成功过 if(clientComponent.getClient() == this || clientComponent.getClient(getConfig()) != null) { this.reconnection(((ChannelFuture) future).channel()); } } } }); } /** * 断开连接 */ public ChannelFuture disconnect(boolean remove) { return this.channel.disconnect().addListener(future -> { if(future.isSuccess()) { disconnectSuccessCall(remove); // 移除客户端处理 if(logger.isDebugEnabled()) { logger.debug("客户端({}) 关闭客户端(成功) - 远程地址:{}", getName(), getConfig()); } } else { logger.error("客户端({}) 关闭客户端(失败) - 远程地址:{}", getName(), getConfig(), future.cause()); } }); } /** * 断线成功后的处理 * 默认的处理方式:remove=true 移除 remove=false 重连 */ protected void disconnectSuccessCall(boolean remove) { if(remove) { // 移除 getClientComponent().removeClient(getConfig()); } else { // 重连 reconnection(this.channel); } } protected void doInitOptions(Bootstrap bootstrap) { bootstrap.option(ChannelOption.SO_KEEPALIVE, true); } protected abstract Class channel(); /** * 连接配置 * @return */ public ClientConnectProperties getConfig() { return this.config; } @Override public int getPort() { return getConfig().getPort(); } @Override public String getHost() { return getConfig().getHost(); } protected void connectLogger(ChannelFuture future) { if (logger.isDebugEnabled() && future.isSuccess()) { logger.debug("客户端({}) 连接服务器成功 - 远程主机 {}:{}" , getClientComponent().getName(), this.getHost(), this.getPort()); } else if (!future.isSuccess()) { Throwable cause = future.cause(); logger.error("客户端({}) 连接服务器失败 - 远程主机 {}:{}", getClientComponent().getName() , getConfig().getReconnect(), this.getHost(), this.getPort(), cause); } } /** * 断线重连 * @see ClientConnectProperties#getReconnect() 重连时间 * @param channel */ public synchronized void reconnection(Channel channel) { long reconnectTime = getConfig().getReconnect(); // 指定了重连时间并且客户端未激活 if(!this.reconnectFlag && reconnectTime > 0 && !this.getChannel().isActive()) { this.reconnectFlag = true; logger.warn("客户端({}) 断线重连 - 等待重连时间:{}(s) - 远程主机 {}:{} - 客户端标识:{}" , getClientComponent().getName(), reconnectTime, this.getHost(), this.getPort(), getConfig().connectKey()); IotThreadManager.instance().getDeviceManageEventExecutor().schedule(() -> { this.reconnectFlag = false; this.connect((future) -> { final ChannelFuture channelFuture = (ChannelFuture) future; if(channelFuture.isSuccess()) { logger.info("客户端({}) 重连成功 - 远程主机 {}:{} - 客户端标识:{}", getClientComponent().getName() , this.getHost(), this.getPort(), getConfig().connectKey()); } else { logger.error("客户端({}) 重连失败 - 远程主机 {}:{} - 客户端标识:{}", getClientComponent().getName() , this.getHost(), this.getPort(), getConfig().connectKey(), channelFuture.cause()); } }, 2000); }, reconnectTime, TimeUnit.SECONDS); } } /** * 写出协议报文 * @see ClientProtocolEncoder 协议编码器, 此处是真正写出报文的地方 * @param clientProtocol * @return */ public ChannelFuture writeAndFlush(ClientSocketProtocol clientProtocol) { if(this.getChannel() == null || !this.getChannel().isActive()) { try { // 尝试重连, 并且等待 return connect((arg) -> { ChannelFuture future = (ChannelFuture) arg; if(future.isSuccess() && logger.isDebugEnabled()) { logger.debug("客户端({}) 写报文重连成功 - 远程主机 {}:{} - 客户端标识:{}" , getClientComponent().getName(), this.getHost(), this.getPort(), getConfig().connectKey()); } }, 3000).addListener((future) -> { final ChannelFuture channelFuture = (ChannelFuture) future; // 重连成功后写出报文 if(channelFuture.isSuccess()) { this.writeAndFlush(clientProtocol); } }).syncUninterruptibly(); } catch (BlockingOperationException e) { throw new ProtocolException("写报文尝试重连失败 连接同步死锁", e); } catch (Exception e) { throw new ProtocolException("写报文尝试重连失败 " + e.getMessage() , e); } } else if(this.getChannel().isWritable()){ return this.getChannel().writeAndFlush(clientProtocol); } else { return this.getChannel().newFailedFuture(new UnWritableProtocolException(clientProtocol)); } } protected void doInitChannel(Channel channel) { } /** * 连接成功后的回调 * @param future */ protected void successCallback(ChannelFuture future) { } /** * 创建客户端socket解码器 * @see io.netty.handler.codec.ByteToMessageDecoder 此对象的子类不能使用 {@link io.netty.channel.ChannelHandler.Sharable} * @return */ protected abstract ChannelInboundHandler createProtocolDecoder(); /** * 创建socket编码器 * @return 默认使用iot框架实现的编码器 {@link ClientProtocolEncoder} */ protected ChannelOutboundHandlerAdapter createProtocolEncoder() { return new ClientProtocolEncoder(getClientComponent()); } public ClientComponent getClientComponent() { return clientComponent; } public void setClientComponent(ClientComponent clientComponent) { this.clientComponent = clientComponent; } public String getName() { return this.clientComponent.getName(); } public Bootstrap getBootstrap() { return bootstrap; } public Channel getChannel() { return channel; } public void setChannel(Channel channel) { this.channel = channel; } }




© 2015 - 2025 Weber Informatics LLC | Privacy Policy