原生socket客户端与springboot整合的netty-socket服务端进行ssl双向认证通讯(附Git地址)

此项目将原生socket服务端与netty-socket服务端整合在一起,ssl加密与不加密的socket可以实现配置切换,可根据需求增减模块。

客户端与服务端报文采用以下格式

1字节的特殊标识68H(01101000B)作为报文开头flag + 4字节的数据长度字符串 + 6字节的业务编码字符串(根据它执行不同的业务) + 变长的数据域 + 1字节的特殊标识16H(00010110B)作为报文结束flag

{head:{tag1:value1,tag2:value2,...},body:{tag3:value3,tag4:value4, params:[{ param1:values1 },{ paramn:valuesn }],...}}

head存放协议信息,body存放业务信息。

项目 原生socket与netty共存,即application.main中同时启动2个socket服务

package com.lance;

import com.lance.api.business.socketserver.nativesocket.ServerSocket;
import com.lance.api.business.socketserver.netty.NettyServer;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.CommandLineRunner;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration;

import java.util.concurrent.Executor;

/**
 * <p>启动类
 *
 * @author lance
 * @since 2018-10-15
 */
@SpringBootApplication(exclude = {DataSourceAutoConfiguration.class})
public class Application implements CommandLineRunner
{
    @Autowired
    private NettyServer nettyServer;
    @Autowired
    private ServerSocket serverSocket;
    @Autowired
    private Executor threadAsyncServiceProvider;

    public static void main(String[] args)
    {
        SpringApplication.run(Application.class, args);
    }

    @Override
    public void run(String... args) throws Exception
    {
        serverSocket.init();
        // 启动新线程是为了原生socket与netty共存启动
        threadAsyncServiceProvider.execute(serverSocket);
        // 启动netty-socket
        nettyServer.start();
    }
}

原生ssl socket服务端如下:

package com.lance.api.business.socketserver.nativesocket;


import com.lance.api.business.util.DealSocket;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.core.io.ClassPathResource;
import org.springframework.core.io.Resource;
import org.springframework.stereotype.Component;

import javax.net.ssl.KeyManagerFactory;
import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLServerSocket;
import javax.net.ssl.TrustManagerFactory;
import java.net.Socket;
import java.security.KeyStore;


/**
 * ssl socket
 *
 * @author lance
 * @since 2018-10-10
 */
@Component("serverSocket")
@Slf4j
public class ServerSocket implements Runnable
{
    @Value("${sslSocket.port}")
    private int port;
    @Value("${sslSocket.server_key_store_password}")
    private String serverKeyStorePassword;
    @Value("${sslSocket.server_trust_key_store_password}")
    private String serverTrustKeyStorePassword;

    @Autowired
    private DealSocket dealSocket;

    private SSLServerSocket serverSocket;


    /**
     * 启动程序
     */
    @Override
    public void run()
    {
        if (serverSocket == null)
        {
            log.error("ERROR:启动socket服务失败");
            return;
        }
        while (true)
        {
            try
            {
                Socket socket = serverSocket.accept();

                dealSocket.analysisAndDealMsg(socket);
            }
            catch (Exception e)
            {
                e.printStackTrace();
            }
        }
    }

    /**
     * 初始化
     */
    public void init()
    {
        try
        {
            SSLContext ctx = SSLContext.getInstance("SSL");

            KeyManagerFactory kmf = KeyManagerFactory.getInstance("SunX509");
            TrustManagerFactory tmf = TrustManagerFactory.getInstance("SunX509");

            KeyStore ks = KeyStore.getInstance("JKS");
            KeyStore tks = KeyStore.getInstance("JKS");
            Resource fileResourceK = new ClassPathResource("keystores/kserver.keystore");
            Resource fileResourceT = new ClassPathResource("keystores/tserver.keystore");
            ks.load(fileResourceK.getInputStream(), serverKeyStorePassword.toCharArray());
            tks.load(fileResourceT.getInputStream(), serverTrustKeyStorePassword.toCharArray());

            kmf.init(ks, serverKeyStorePassword.toCharArray());
            tmf.init(tks);

            ctx.init(kmf.getKeyManagers(), tmf.getTrustManagers(), null);

            serverSocket = (SSLServerSocket) ctx.getServerSocketFactory().createServerSocket(port);
            serverSocket.setNeedClientAuth(true);
        }
        catch (Exception e)
        {
            e.printStackTrace();
        }
    }
}

ComposeUtil与DealSocket下面做介绍,暂时略过

netty服务端如下:

package com.lance.api.business.socketserver.netty;


import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.*;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioServerSocketChannel;
import io.netty.handler.ssl.SslHandler;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.core.io.ClassPathResource;
import org.springframework.stereotype.Component;

import javax.annotation.PreDestroy;
import javax.net.ssl.KeyManagerFactory;
import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLEngine;
import javax.net.ssl.TrustManagerFactory;
import java.security.KeyStore;

/**
 * netty服务启动类
 *
 * @author lance
 */
@Component
@Slf4j
public class NettyServer extends ChannelInitializer
{
    @Value("${sslSocket.nettyPort}")
    private int port;
    @Value("${sslSocket.sslFlag:false}")
    private boolean ssl;
    @Value("${sslSocket.server_key_store_password}")
    private String serverKeyStorePassword;
    @Value("${sslSocket.server_trust_key_store_password}")
    private String serverTrustKeyStorePassWord;

    private static final EventLoopGroup BOSS_GROUP = new NioEventLoopGroup();
    private static final EventLoopGroup WORKER_GROUP = new NioEventLoopGroup();

    /**
     * 业务处理器
     */
    @Autowired
    private ServerHandler serverHandler;

    /**
     * 关闭服务
     */
    @PreDestroy
    public void close()
    {
        log.info("关闭服务....");
        BOSS_GROUP.shutdownGracefully();
        WORKER_GROUP.shutdownGracefully();
    }

    /**
     * 开启服务
     */
    public void start() throws Exception
    {
        final SSLContext sslCtx;
        if (ssl)
        {
            sslCtx = SSLContext.getInstance("SSL");

            KeyManagerFactory kmf = KeyManagerFactory.getInstance("SunX509");
            TrustManagerFactory tmf = TrustManagerFactory.getInstance("SunX509");

            KeyStore ks = KeyStore.getInstance("JKS");
            KeyStore tks = KeyStore.getInstance("JKS");

            org.springframework.core.io.Resource frk = new ClassPathResource("keystores/kserver.keystore");
            org.springframework.core.io.Resource frt = new ClassPathResource("keystores/tserver.keystore");
            ks.load(frk.getInputStream(), serverKeyStorePassword.toCharArray());
            tks.load(frt.getInputStream(), serverTrustKeyStorePassWord.toCharArray());

            kmf.init(ks, serverKeyStorePassword.toCharArray());
            tmf.init(tks);

            sslCtx.init(kmf.getKeyManagers(), tmf.getTrustManagers(), null);
        }
        else
        {
            sslCtx = null;
        }
        ServerBootstrap serverBootstrap = new ServerBootstrap();
        serverBootstrap.group(BOSS_GROUP, WORKER_GROUP);
        serverBootstrap.channel(NioServerSocketChannel.class);
        serverBootstrap.option(ChannelOption.SO_BACKLOG, 100);

        try
        {
            //设置事件处理
            serverBootstrap.childHandler(new ChannelInitializer<SocketChannel>()
            {
                @Override
                protected void initChannel(SocketChannel ch) throws Exception
                {
                    if (sslCtx != null)
                    {
                        SSLEngine sslEngine = sslCtx.createSSLEngine();
                        sslEngine.setUseClientMode(false);
                        ch.pipeline().addFirst(new SslHandler(sslEngine));
                    }
                    ChannelPipeline pipeline = ch.pipeline();
                    // 解码器
                    pipeline.addLast(new MessageDecoder());
                    // 编码器
                    pipeline.addLast(new MessageEncoder());
                    // 业务处理器
                    pipeline.addLast(serverHandler);

                }
            });
            log.info("netty服务在[{}]端口启动监听", port);
            ChannelFuture future = serverBootstrap.bind(port).sync();
            future.channel().closeFuture().sync();
        }
        catch (InterruptedException e)
        {
            log.info("[出现异常] 释放资源");
            BOSS_GROUP.shutdownGracefully();
            WORKER_GROUP.shutdownGracefully();
        }
    }

    @Override
    protected void initChannel(Channel channel) throws Exception
    {

    }
}

netty需要三个适配器:业务处理前解码 messageDecoder;业务处理 serverHandler;业务处理后编码 messageEncoder

解码器是将传入的字节数组转换为javaBean,代码如下:

package com.lance.api.business.socketserver.netty;

import com.lance.api.business.pojo.model.EntryModel;
import com.lance.api.business.util.ByteDataBuffer;
import com.lance.api.business.util.ComposeUtil;
import io.netty.buffer.ByteBuf;
import io.netty.channel.ChannelHandler;
import io.netty.channel.ChannelHandlerContext;
import io.netty.handler.codec.ByteToMessageDecoder;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import java.util.List;

/**
 * 报文解码
 *
 * @author lance
 */
public class MessageDecoder extends ByteToMessageDecoder
{

    /**
     * 从ByteBuf中获取字节,转换成对象
     *
     * @param ctx
     * @param buffer
     * @param out
     * @throws Exception
     */
    @Override
    protected void decode(ChannelHandlerContext ctx, ByteBuf buffer, List<Object> out) throws Exception
    {
        int len = buffer.readableBytes();
        byte[] req = new byte[len];
        buffer.getBytes(0, req);

        ByteDataBuffer dataBuffer = new ByteDataBuffer(req);

        EntryModel entryModel = ComposeUtil.deSplit(dataBuffer);
        out.add(entryModel);
        buffer.skipBytes(len);
    }
}

编码器是将对象转为固定格式的字节数组,传输给客户端,代码如下:

package com.lance.api.business.socketserver.netty;

import com.lance.api.business.pojo.model.ReturnModel;
import com.lance.api.business.util.ComposeUtil;
import io.netty.buffer.ByteBuf;
import io.netty.channel.ChannelHandlerContext;
import io.netty.handler.codec.MessageToByteEncoder;
import org.springframework.stereotype.Component;

/**
 * 报文组装
 *
 * @author lance
 */
public class MessageEncoder extends MessageToByteEncoder<ReturnModel>
{
    @Override
    protected void encode(ChannelHandlerContext channelHandlerContext, ReturnModel returnModel, ByteBuf out) throws Exception
    {
        byte[] bytes = null;
        try
        {
            bytes = ComposeUtil.doCanProcess(returnModel.getMap(), returnModel.getServCode(), returnModel.getMsgId());
        }
        catch (Exception e)
        {
            e.printStackTrace();
        }
        out.writeBytes(bytes);
    }
}

业务处理适配器:

package com.lance.api.business.socketserver.netty;
// 记录调用方法的元信息的类

import com.lance.api.business.pojo.model.EntryModel;
import com.lance.api.business.pojo.model.ReturnModel;
import com.lance.api.business.util.DealSocket;
import io.netty.channel.ChannelHandler.Sharable;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInboundHandlerAdapter;
import lombok.extern.slf4j.Slf4j;
import org.slf4j.MDC;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

/**
 * netty业务处理类
 *
 * @author lance
 */
@Component
@Sharable
@Slf4j
public class ServerHandler extends ChannelInboundHandlerAdapter
{
    @Autowired
    private DealSocket dealSocket;

    @Override
    public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception
    {
        EntryModel entryModel = (EntryModel) msg;

        String servCode = entryModel.getHeadModel().getServCode();
        String msgId = entryModel.getHeadModel().getMsgId();

        // do business
        MDC.put("logId", msgId);
        ReturnModel result = dealSocket.doBusiness(servCode, msgId, entryModel);
        MDC.clear();
        ctx.writeAndFlush(result);
    }

    @Override
    public void channelReadComplete(ChannelHandlerContext ctx) throws Exception
    {

    }

    @Override
    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause)
    {
        // Close the connection when an exception is raised.
        cause.printStackTrace();
        ctx.close();
    }
}

ComposeUtil为自定义的解析、构建 先前描述的格式报文的工具类

package com.lance.api.business.util;


import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.alibaba.fastjson.TypeReference;
import com.lance.api.business.pojo.model.EntryBodyModel;
import com.lance.api.business.pojo.model.EntryHeadModel;
import com.lance.api.business.pojo.model.EntryModel;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;

import java.io.UnsupportedEncodingException;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;


/**
 * <p>报文组装解析类
 *
 * @author lance
 * @since 2018-10-11
 */
@Slf4j
@Component
public class ComposeUtil
{
    private static final byte BLOCK_BGN_SIGN = 0x68;
    private static final byte BLOCK_END_SIGN = 0x16;
    private static final String ENCODING = "UTF-8";

    /**
     * 组装报文
     *
     * @param paraMap
     * @param serverCode
     * @param msgId
     * @return
     * @throws Exception
     */
    public static byte[] doCanProcess(Map paraMap, String serverCode, String msgId) throws Exception
    {
        Map<String, Object> map = new HashMap<>();
        Map<String, String> headMap = new HashMap<>();
        // 服务编码
        String servCode = serverCode;
        headMap.put("version", "1.0.0");
        // 服务编码
        headMap.put("servCode", servCode);

        // msgId 32位不可重复的流水
        headMap.put("msgId", msgId);
        headMap.put("msgTime", new SimpleDateFormat("yyyyMMddHHmmss").format(new Date()));
        headMap.put("extend", "");
        map.put("head", headMap);
        map.put("body", paraMap);
        String message = JSONObject.toJSONString(map).replace("\\", "");

        log.info("返回的报文为:\n{}", message);
        ByteDataBuffer bdb = new ByteDataBuffer();
        bdb.setInBigEndian(false);
        // 开始字节
        bdb.writeInt8((byte) BLOCK_BGN_SIGN);
        int len = Integer.parseInt(getLen(message, 4));
        // 用于计算数据域的长度是否大于4字节
        // 报文长度
        bdb.writeInt32(len);
        // 服务编码
        bdb.writeString(servCode, 6);
        // 报文frame
        bdb.writeBytes(message.getBytes());
        // 结束字节
        bdb.writeInt8((byte) BLOCK_END_SIGN);
        return bdb.getBytes();
    }

    /**
     * 解析报文
     *
     * @param obj
     * @return
     * @throws Exception
     */
    public static EntryModel deSplit(ByteDataBuffer obj) throws Exception
    {
        ByteDataBuffer dataBuf = obj;
        dataBuf.setEncoding(ENCODING);
        dataBuf.setInBigEndian(false);
        // 长度
        int totalLen = 0;
        byte sign = dataBuf.readInt8();
        if (sign != BLOCK_BGN_SIGN)
        {
            log.info("无法找到起始标记!");
            return null;
        }
        totalLen = dataBuf.readInt32();
        dataBuf.readString(6);
        byte[] dataBytes = new byte[totalLen];
        dataBuf.readBytes(dataBytes);
        String message = new String(dataBytes);
        log.info("请求的报文为:\n{}", message);

        // 报文是json格式,把json报文转换成对象类型的
        EntryModel entryModel = JSON.parseObject(message, EntryModel.class);
        EntryHeadModel headModel = JSON.parseObject(entryModel.getHead(), new TypeReference<EntryHeadModel>()
        {
        });
        EntryBodyModel bodyModel = JSON.parseObject(entryModel.getBody(), new TypeReference<EntryBodyModel>()
        {
        });
        entryModel.setHeadModel(headModel);
        entryModel.setBodyModel(bodyModel);

        return entryModel;
    }


    /**
     * 计算报文长度,不足四位左补零
     *
     * @param text    报文信息
     * @param needLen 报文长度规定的字符数
     * @return
     */
    private static String getLen(String text, int needLen)
    {
        if (text != null)
        {
            int len;
            try
            {
                len = text.getBytes("utf-8").length;
                String lenStr = String.valueOf(len);
                StringBuffer sb = new StringBuffer(lenStr);
                while (sb.length() < needLen)
                {
                    sb.insert(0, "0");

                }
                return sb.toString();
            }
            catch (UnsupportedEncodingException e)
            {
                e.printStackTrace();
            }
        }
        return null;
    }
}

DealSocket为解析传入的6位业务编码,找到对应的业务包装类,进行业务处理

package com.lance.api.business.util;


import com.lance.api.business.pojo.model.EntryModel;
import com.lance.api.business.pojo.model.ReturnModel;
import com.lance.api.business.service.wrap.core.BaseServiceWrapper;
import com.lance.api.business.service.wrap.core.ServiceWrapperContainer;
import com.lance.api.common.base.model.Communication;
import com.lance.api.common.constant.ResponseCode;
import lombok.Data;
import lombok.extern.slf4j.Slf4j;
import org.slf4j.MDC;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;

import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.Socket;
import java.util.List;
import java.util.Map;

/**
 * <p>socket业务分发类
 *
 * @author lance
 * @since 2018-10-15
 */
@Data
@Slf4j
@Component
@SuppressWarnings("unchecked")
public class DealSocket
{
    /**
     * service包装类容器
     */
    @Autowired
    private ServiceWrapperContainer serviceWrapperContainer;

    /**
     * 解析并处理报文
     *
     * @param socket
     * @throws Exception
     */
    @Async
    public void analysisAndDealMsg(Socket socket) throws Exception
    {
        /*
          1.获取socket数据
        */
        InputStream input = socket.getInputStream();
        OutputStream output = socket.getOutputStream();
        BufferedInputStream bis = new BufferedInputStream(input);
        BufferedOutputStream bos = new BufferedOutputStream(output);
        byte[] buffer = new byte[36000];
        bis.read(buffer);

        // 解析报文
        EntryModel entryModel = ComposeUtil.deSplit(new ByteDataBuffer(buffer));
        if (entryModel == null)
        {
            noSignFlag(bos);
            return;
        }
        // 业务请求码
        String servCode = entryModel.getHeadModel().getServCode();
        // 报文标识
        String msgId = entryModel.getHeadModel().getMsgId();
        /*
          2.业务处理
        */
        MDC.put("logId", msgId);
        doBusiness(servCode, msgId, entryModel, bos);
        MDC.clear();

    }

    /**
     * 处理业务(原生socket使用)
     *
     * @param servCode
     * @param msgId
     * @param entryModel
     * @param bos
     */
    @Async
    public void doBusiness(String servCode, String msgId, EntryModel entryModel, BufferedOutputStream bos)
    {
        Map<String, Object> resultMap = null;
        try
        {
            if (StringUtils.isEmpty(servCode) || StringUtils.isEmpty(msgId))
            {
                resultMap = Communication.getInstance().getFailedMap(ResponseCode._0000.getKey(), ResponseCode._0000.getValue());
            }
            else
            {
                log.info("业务处理开始");

                // 根据servCode查询所对应的service业务处理类
                BaseServiceWrapper baseServiceWrapper = serviceWrapperContainer.getService(servCode);
                // 或有业务存在servCode相同的情况,需加以判断是否存在servCode相同的情况
                if (baseServiceWrapper.hasSameService())
                {
                    // 根据servCode查询出所有servCode相同的service
                    List<BaseServiceWrapper> baseServiceWrappers = serviceWrapperContainer.findSameService(servCode);
                    for (BaseServiceWrapper bs : baseServiceWrappers)
                    {
                        if (bs.sameIsMyDo(entryModel))
                        {
                            resultMap = bs.handle(entryModel);
                        }
                    }
                }
                else
                {
                    resultMap = baseServiceWrapper.handle(entryModel);
                }
                log.info("业务处理完毕");
            }
        }
        catch (Exception e)
        {
            log.error("业务处理发生异常:\n{}", e.getMessage());
            e.printStackTrace();
            resultMap = Communication.getInstance().getFailedMap(ResponseCode._0009.getKey(), ResponseCode._0009.getValue());
        }
        finally
        {
            try
            {
                bos.write(ComposeUtil.doCanProcess(resultMap, servCode, msgId));
                bos.flush();
            }
            catch (Exception e)
            {
                e.printStackTrace();
            }
        }
    }

    /**
     * 处理业务(netty使用)
     *
     * @param servCode
     * @param msgId
     * @param entryModel
     */
    public ReturnModel doBusiness(String servCode, String msgId, EntryModel entryModel)
    {
        ReturnModel resultModel = new ReturnModel();
        resultModel.setServCode(servCode);
        resultModel.setMsgId(msgId);
        try
        {
            if (StringUtils.isEmpty(servCode) || StringUtils.isEmpty(msgId))
            {
                return resultModel.setMap(Communication.getInstance().getFailedMap(ResponseCode._0000.getKey(), ResponseCode._0000.getValue()));
            }
            log.info("业务处理开始");

            // 根据servCode查询所对应的service业务处理类
            BaseServiceWrapper baseServiceWrapper = serviceWrapperContainer.getService(servCode);
            // 或有业务存在servCode相同的情况,需加以判断是否存在servCode相同的情况
            if (baseServiceWrapper.hasSameService())
            {
                // 根据servCode查询出所有servCode相同的service
                List<BaseServiceWrapper> baseServiceWrappers = serviceWrapperContainer.findSameService(servCode);
                for (BaseServiceWrapper bs : baseServiceWrappers)
                {
                    if (bs.sameIsMyDo(entryModel))
                    {
                        resultModel.setMap(bs.handle(entryModel));
                    }
                }
            }
            else
            {
                resultModel.setMap(baseServiceWrapper.handle(entryModel));
            }
            log.info("业务处理完毕");
        }
        catch (Exception e)
        {
            log.error("业务处理发生异常:\n{}", e.getMessage());
            e.printStackTrace();
            resultModel.setMap(Communication.getInstance().getFailedMap(ResponseCode._0009.getKey(), ResponseCode._0009.getValue()));
        }
        return resultModel;
    }

    /**
     * 若无起始标记,则返回无标识信息
     *
     * @param bos
     */
    public void noSignFlag(BufferedOutputStream bos)
    {
        Map<String, Object> resultMap = Communication.getInstance().getFailedMap(ResponseCode._0000.getKey(), ResponseCode._0000.getValue());
        try
        {
            bos.write(ComposeUtil.doCanProcess(resultMap, "", ""));
            bos.flush();
            bos.close();
        }
        catch (Exception e)
        {
            e.printStackTrace();
        }
    }
}

怎么实现根据业务编码找到对应的业务包装类呢?

每个业务包装类都需要继承一个包装类基类BaseServiceWrapper,重写doCode(即当前业务类的服务编码)与handle(详细业务处理),like this:

package com.lance.api.business.service.wrap.business;

import com.lance.api.business.service.wrap.core.BaseServiceWrapper;
import com.lance.api.common.base.model.Communication;
import com.lance.api.common.constant.ServerCode;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;

import java.util.Map;

/**
 * 校验包装类
 *
 * @author lance
 */
@Slf4j
@Component
public class CheckWrapper extends BaseServiceWrapper
{

    /**
     * 此业务包装类的servCode
     */
    private final String servCode = ServerCode.SERVER_CODE_CHECK;

    /**
     * 获取当前servCode
     *
     * @return
     */
    @Override
    public String doCode()
    {
        return servCode;
    }

    /**
     * 处理业务--校验
     *
     * @param params
     * @return
     * @throws Exception
     */
    @Override
    public Map handle(Object... params) throws Exception
    {
        // 返回结果map
        return Communication.getInstance().getSuccessMap();
    }
}

BaseServiceWrapper如下:

package com.lance.api.business.service.wrap.core;

import java.util.Map;

/**
 * <p>包装类基类
 *
 * @author lance
 * @since 2018-10-15
 */
public abstract class BaseServiceWrapper extends SameServiceWrapper
{
    /**
     * 获取当前servCode
     *
     * @return
     */
    public abstract String doCode();

    /**
     * 传入的servCode是否为当前包装类的servCode
     *
     * @param doCode
     * @return
     */
    public Boolean isMyDo(String doCode)
    {
        return doCode.equals(doCode());
    }

    /**
     * 处理业务
     *
     * @param params
     * @return
     * @throws Exception
     */
    public abstract Map handle(Object... params) throws Exception;
}

ps:若服务编码一致,则需要继承SameServiceWrapper进行进一步处理。

通过ServiceWrapperContainer(包装类容器类)对比客户端传入的业务编码与包装类配置的业务编码进行比较,从容器中获取到对应的包装类

package com.lance.api.business.service.wrap.core;

import java.util.ArrayList;
import java.util.List;

/**
 * <p>service包裝类容器类
 *
 * @author lance
 * @since 2018-10-15
 */
public class ServiceWrapperContainer
{

    List<BaseServiceWrapper> services = new ArrayList<>();

    /**
     * 添加包装类
     *
     * @param baseServiceWrapper
     */
    public void addService(BaseServiceWrapper baseServiceWrapper)
    {
        services.add(baseServiceWrapper);
    }

    /**
     * 根据servCode获取service包装类
     *
     * @param servCode
     * @return
     */
    public BaseServiceWrapper getService(String servCode)
    {
        for (BaseServiceWrapper baseServiceWrapper : services)
        {
            if (baseServiceWrapper.isMyDo(servCode))
            {
                return baseServiceWrapper;
            }
        }
        return null;
    }

    /**
     * 根据servCode获取所有service包装类
     *
     * @param servCode
     * @return
     */
    public List<BaseServiceWrapper> findSameService(String servCode)
    {

        List<BaseServiceWrapper> baseServiceWrapperList = new ArrayList<>();
        for (BaseServiceWrapper baseServiceWrapper : services)
        {
            if (baseServiceWrapper.hasSameService() && baseServiceWrapper.isMyDo(servCode))
            {
                baseServiceWrapperList.add(baseServiceWrapper);
            }
        }
        return baseServiceWrapperList;
    }
}

通过Config将各个业务包装类存储到上述容器中

package com.lance.api.business.service.wrap.config;

import com.lance.api.business.service.wrap.business.InquiryWrapper;
import com.lance.api.business.service.wrap.business.SaveWrapper;
import com.lance.api.business.service.wrap.business.CheckWrapper;
import com.lance.api.business.service.wrap.core.ServiceWrapperContainer;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

/**
 * <p>加载wrappers
 *
 * @author lance
 * @since 2018-10-15
 */
@Configuration
public class InitServiceWrapper
{
    @Bean("serviceWrapperContainer")
    public ServiceWrapperContainer initWrapper(InquiryWrapper inquiryWrapper, SaveWrapper saveWrapper, CheckWrapper checkWrapper)
    {
        ServiceWrapperContainer serviceWrapper = new ServiceWrapperContainer();
        serviceWrapper.addService(inquiryWrapper);
        serviceWrapper.addService(saveWrapper);
        serviceWrapper.addService(checkWrapper);
        return serviceWrapper;
    }
}

 这样就可以在先前的DealSocket中通过业务编码处理对应的业务了.

测试客户端代码见下面Git地址项目的test.sslsocketclient

GItHub地址:

https://github.com/lance2038/springbootNettySslSocketDemo

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值