Netty的简单封装-JSON

基于netty通讯的简单封装

只是一个简单的demo版本测试使用

本案例只包含服务端 

netty版本:4.1

<dependency>
            <groupId>io.netty</groupId>
            <artifactId>netty-all</artifactId>
            <version>4.1.20.Final</version>
</dependency>

<dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>fastjson</artifactId>
            <version>1.2.47</version>
</dependency>

数据格式 JSON UTF-8

#############################关键代码##########################

1.启动入口:

/**
     * 启动server
     */
    public static Boolean startServer() {
        try {
            if (b == null) {
                Thread thread = new Thread(new Runnable() {
                    @Override
                    public void run() {
                        try {
                            b = new Server().startServer(port);
                        } catch (Exception e) {
                            e.printStackTrace();
                        }
                    }
                });
                //设置为守护线程
                thread.setDaemon(true);
                thread.start();
                return true;
            }

        } catch (Exception e) {

            e.printStackTrace();
        }
        return false;
    }

2.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.codec.DelimiterBasedFrameDecoder;
import io.netty.handler.codec.Delimiters;
import io.netty.handler.codec.string.StringDecoder;
import io.netty.handler.codec.string.StringEncoder;
import java.nio.charset.Charset;

public class Server {


    private static EventLoopGroup boss;
    private static EventLoopGroup worker;
    private static ServerBootstrap bootstrap;

    /**
     * netty服务端ServerBootstrap();
     * */
    public  ServerBootstrap startServer(Integer port) throws InterruptedException {
        try {
            //多线程模式处理
            bootstrap = new ServerBootstrap();
            boss = new NioEventLoopGroup(4); //可以根据机器核心*2设置
            worker = new NioEventLoopGroup(8);
            bootstrap.group(boss,worker);
            bootstrap.channel(NioServerSocketChannel.class);
            bootstrap.childHandler(new NettyServerFilter()); //设置过滤器
            // 服务器绑定端口监听
            ChannelFuture f = bootstrap.bind(port).sync();
            System.out.println("服务端启动成功...");
            // 监听服务器关闭监听
            f.channel().closeFuture().sync();
        } finally {
            boss.shutdownGracefully(); 关闭EventLoopGroup,释放掉所有资源包括创建的线程
        }
        return bootstrap;
    }
}

class NettyServerHandler extends SimpleChannelInboundHandler<String> {
    /*
     * 收到消息时,返回信息
     */
    @Override
    protected void channelRead0(ChannelHandlerContext ctx, String msg)
            throws Exception {
        // 收到消息直接打印输出
        System.out.println("服务端接受的消息 : " + msg + System.currentTimeMillis());
        ServerControll.handData(ctx,msg);
    }

    /*
     * 建立连接时,返回消息
     */
    @Override
    public void channelActive(ChannelHandlerContext ctx) throws Exception {
        System.out.println("连接的客户端地址:" + ctx.channel().remoteAddress());
        ServerDataPool.setConnectionList(ctx);
        ctx.writeAndFlush("{\"data\": {\"inked\": \"ok\"},\"code\": \"000\",\"msg\": \"登录成功\",\"tsp\": 152695656465}\n");
        super.channelActive(ctx);
    }
}

class NettyServerFilter extends ChannelInitializer<SocketChannel> {

    @Override
    protected void initChannel(SocketChannel ch) throws Exception {
        ChannelPipeline ph = ch.pipeline();
        // 以("\n")为结尾分割的 解码器
        ph.addLast("framer", new DelimiterBasedFrameDecoder(8192, Delimiters.lineDelimiter()));
        // 解码和编码,应和客户端一致
        ph.addLast("decoder", new StringDecoder(Charset.forName("UTF-8")));
        ph.addLast("encoder", new StringEncoder(Charset.forName("UTF-8")));
        ph.addLast("handler", new NettyServerHandler());// 服务端业务逻辑
    }
}

3.服务总控  + 链接池


import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.jfinal.weixin.sdk.utils.JsonUtils;
import io.netty.channel.ChannelHandlerContext;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class ServerControll {

    private static final Logger LOGGER = LoggerFactory.getLogger(ServerControll.class);


    /**
     * 接收到的消息处理
     */
    public static void handData(ChannelHandlerContext ctx, String msg) throws Exception {
        try {
            jsonObject.toJavaObject(BizCodeEnum.getClassByCode(jsonObject.get("code").toString())).analysis(ctx);
        } catch (Exception e) {
            e.printStackTrace();
            //返回服务端错误信息
            ctx.writeAndFlush("{\"data\": {\"eqpNo\": \"\"},\"code\": \"999\",\"msg\": \"数据解析错误\",\"tsp\": 152695656465}\n");
        }
    }

    /***
     * 发送处理之后需要返回的信息(登录时调用)
     * */
    public static void sendData(BaseTransfer data, ChannelHandlerContext ctx) throws Exception {
        String returnStr = JSON.toJSONString(data);
        if (data.getEqpNo() != null) {
            ServerDataPool.getCtxByEqpNo(data.getEqpNo()).writeAndFlush(returnStr + "\n");
        } else {
            ctx.writeAndFlush(returnStr + "\n");
        }
        LOGGER.info(Thread.currentThread().getName() + "返回信息:【" + returnStr + "】");
    }

    /***
     * 发送处理之后需要返回的信息
     * */
    public static void sendData(BaseTransfer data) throws Exception {
        String returnStr = JsonUtils.toJson(data);
        ServerDataPool.getCtxByEqpNo(data.getEqpNo()).writeAndFlush(returnStr + "\n");
        LOGGER.info(Thread.currentThread().getName() + "返回信息:【" + returnStr + "】");
    }

    /**
     * 清理无用链接
     **/
    private static void cleanUselessConnection() {}

    /**
     * 断开链接
     */
    private static Integer breakConnection() {return null;}

    /**
     * 断开所有链接
     */
    public static Boolean breakAllConnection() {return false;}


}


import io.netty.channel.ChannelHandlerContext;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

public class ServerDataPool {

    private static final Logger LOGGER = LoggerFactory.getLogger(ServerDataPool.class);

    /**
     * 存放已经登录的connection
     */
    private static final Map<String, CtxPackage> ACTIVE_POOL = new HashMap<>();

    /**
     * 存放所有链接到服务器的connection
     */
    private static final List<CtxPackage> CONNAETION_LIST = new ArrayList<>();

    /**
     * 根据eqpNo获取cx
     */
    public static ChannelHandlerContext getCtxByEqpNo(String eqpNo) {
        CtxPackage ctxPackage = ACTIVE_POOL.get(eqpNo);
        return ctxPackage.updataActiveTimeStamp().getCtx();
    }

    /**
     * 根据eqpNo获取cxpackage
     */
    public static CtxPackage getCtxPackageByEqpNo(String eqpNo) {
        CtxPackage ctxPackage = ACTIVE_POOL.get(eqpNo);
        return ctxPackage;
    }

    /**
     * 清除无用的链接
     */
    public static int removeUnuseCtx(Long intervalTimes) {
        Long now = System.currentTimeMillis();
        int count = 0;
        for (CtxPackage ctxPackage : CONNAETION_LIST) {
            if (!ctxPackage.getLogIn() && (ctxPackage.getLastActiveTimeStamp() + now) < now) {//未登录并且链接维持时间过长的的关闭
                try {
                    ctxPackage.getCtx().close();
                    CONNAETION_LIST.remove(ctxPackage);
                    LOGGER.info("【删除过期链接】:"+ CONNAETION_LIST.add(ctxPackage));
                    count++;
                } catch (Exception e) {
                    LOGGER.error("【关闭连接:%s , 异常!】", ctxPackage.getCtx().channel().id().asLongText());
                }
            }
        }
        return count;
    }

    /**
     * 向CONNAETION_LIST里面添加
     */
    public static Boolean setConnectionList(ChannelHandlerContext ctx) {
        CtxPackage ctxPackage = new CtxPackage();
        ctxPackage.setCtx(ctx);
        ctxPackage.setLastActiveTimeStamp(System.currentTimeMillis());
        ctxPackage.setLogIn(false);
        LOGGER.info("【创建新链接】:"+ CONNAETION_LIST.add(ctxPackage));
        removeUnuseCtx(60*15L);
        return true;
    }

    /**
     * 向ACTIVE_POOL里面添加对应的ctxPackage
     */
    public static Boolean setActivePool(String eqpNo, ChannelHandlerContext ctx) {
        CtxPackage ctxPackage = new CtxPackage();
        ctxPackage.setCtx(ctx);
        ctxPackage.setLastActiveTimeStamp(System.currentTimeMillis());
        ctxPackage.setLogIn(true);
        ctxPackage.setEqpNo(eqpNo);
        ACTIVE_POOL.put(eqpNo, ctxPackage);
        return true;
    }

}

4.数据包



import io.netty.channel.ChannelHandlerContext;

public class CtxPackage {
    /**
     * 机识别码
     * */
    private String eqpNo;
    /**
     * 游戏过程用户识别码
     * */
    private String token;
    /**
     * 最近一次收到客户端消息时间
     * */
    private Long lastActiveTimeStamp;
    /**
     * 是否登录
     * */
    private Boolean isLogIn;

    /**
     * netty通道
     * */
    private ChannelHandlerContext ctx;


    public String getEqpNo() {
        return eqpNo;
    }

    public void setEqpNo(String eqpNo) {
        this.eqpNo = eqpNo;
    }

    public String getToken() {
        return token;
    }

    public void setToken(String token) {
        this.token = token;
    }

    public Long getLastActiveTimeStamp() {
        return lastActiveTimeStamp;
    }

    public void setLastActiveTimeStamp(Long lastActiveTimeStamp) {
        this.lastActiveTimeStamp = lastActiveTimeStamp;
    }

    public Boolean getLogIn() {
        return isLogIn;
    }

    public void setLogIn(Boolean logIn) {
        isLogIn = logIn;
    }

    public ChannelHandlerContext getCtx() {
        return ctx;
    }

    public void setCtx(ChannelHandlerContext ctx) {
        this.ctx = ctx;
    }
    
    public CtxPackage updataActiveTimeStamp(){
        this.setLastActiveTimeStamp(System.currentTimeMillis());
        return this;
    }
}

5.具体识别码

public enum  BizCodeEnum {

    /**
     *正常业务代码
     * */
    BIZ_SUCCESS("000","成功",null),
    BIZ_TO_LOGIN("101","登录", LoginDataTs.class),
    BIZ_SEND_TIP("102","心跳包", TipDataTs.class),
    BIZ_SEND_TIP_BACK("103","在线",null),
//
    BIZ_START_GAME("201","启动游戏",null),
//    BIZ_START_OVER("202","游戏已启动"),
    BIZ_GAME_DATA("203","游戏数据上报",GameDataTs.class),

    ERROR_USERNAME("901","机器用户名不存在",null),
    ERROR_PASSWORD("902","机器登录密码错误",null),
    ERROR_CONNECTION("903","机器用户名不存在",null),
    ERROR_START("904","游戏启动失败",null),
    ERROR_DATA("905","游戏数据异常",null),
    ERROR_DATA_FORMAT("906","游戏数据不完整",null),
    ERROR_STATISTIC("907","游戏统计数据失败",null),
    ERROR_EQP("908","客户端暂不可用",null),
    ERROR_SERVER("909","服务器异常",null),
    ERROR_CODE("910","CODE无效",null);

    private String code;
    private String msg;
    private Class<BaseTransfer> clazz;

    BizCodeEnum(String code,String msg,Class clazz){
        this.code = code;
        this.msg = msg;
        this.clazz =clazz;
    }


    public String getCode() {
        return code;
    }

    public void setCode(String code) {
        this.code = code;
    }

    public String getMsg() {
        return msg;
    }

    public void setMsg(String msg) {
        this.msg = msg;
    }

    public Class getClazz() {
        return clazz;
    }

    public void setClazz(Class clazz) {
        this.clazz = clazz;
    }

    
}

6.数据包具体处理父类 

package com.skyfly.up.server.dataModle;


import com.skyfly.up.server.BizCodeEnum;
import com.skyfly.up.server.ServerControll;
import io.netty.channel.ChannelHandlerContext;

import java.io.Serializable;

public abstract class BaseTransfer implements Serializable{

    /**
     * 业务代码
     * */
    private String code;

    /**
     * 时间
     * */
    private Long tsp = System.currentTimeMillis();

    /**
     * 返回的消息
     * */
    private String msg;

    /**
     *token
     * 用户登录之后才会有
     * */
    private String token;

    /**
     * 识别码
     * */
    private String eqpNo;

    public BaseTransfer(String code,String msg){
        this.code = code;
        this.msg = msg;
    }

    public BaseTransfer(){}

    public abstract void analysis(ChannelHandlerContext ctx) throws Exception;

    /**
     * @param baseTransfer
     * @param eqpNo
     * @param ctx
     * @throws Exception
     */
    public void finalHandle(BaseTransfer baseTransfer,String eqpNo,ChannelHandlerContext ctx) throws Exception{
        if(baseTransfer == null){
            baseTransfer.setCode(BizCodeEnum.BIZ_SUCCESS.getCode());
            baseTransfer.setMsg(BizCodeEnum.BIZ_SUCCESS.getMsg());
        }
        baseTransfer.setEqpNo(eqpNo);
        ServerControll.sendData(baseTransfer,ctx);
    }


    public void finalHandle(BaseTransfer baseTransfer,String eqpNo) throws Exception{
        if(baseTransfer == null){
            baseTransfer.setCode(BizCodeEnum.BIZ_SUCCESS.getCode());
            baseTransfer.setMsg(BizCodeEnum.BIZ_SUCCESS.getMsg());
        }
        baseTransfer.setEqpNo(eqpNo);
        ServerControll.sendData(baseTransfer);
    }


    public String getCode() {
        return code;
    }

    public void setCode(String code) {
        this.code = code;
    }

    public Long getTsp() {
        return tsp;
    }

    public void setTsp(Long tsp) {
        this.tsp = tsp;
    }

    public String getMsg() {
        return msg;
    }

    public void setMsg(String msg) {
        this.msg = msg;
    }

    public String getToken() {
        return token;
    }

    public void setToken(String token) {
        this.token = token;
    }

    public String getEqpNo() {
        return eqpNo;
    }

    public void setEqpNo(String eqpNo) {
        this.eqpNo = eqpNo;
    }


}

7.具体实现

import io.netty.channel.ChannelHandlerContext;

public class LoginDataTs extends BaseTransfer {

    private LoginDataRe data;

    public LoginDataRe getData() {
        return data;
    }

    public void setData(LoginDataRe data) {
        this.data = data;
    }

    /**
     * 具体的处理方法
     * 最后调用finalHandle(BaseTransfer baseTransfer)返回对应的数据
     */
    @Override
    public void analysis(ChannelHandlerContext ctx) throws Exception{
        /**
         *1.验证用户名密码
         *2.不正确直接返回
         *3.登录后保存 返回信息 带上eqpNo
         * */
        BizEqp bizEqp = BizEqp.dao.getEqpByName(data.getEqpName());
        if (bizEqp == null) {
            //返回账号错误
            BaseTransfer baseTransfer = new NormalResult(BizCodeEnum.ERROR_USERNAME.getCode(),BizCodeEnum.ERROR_USERNAME.getMsg());
            finalHandle(baseTransfer,getEqpNo(),ctx);
            return;
        }
        String passMD5 = MD5Util.getMD5(data.getPassword());
        if (!passMD5.equals(bizEqp.getPassword())) {
            BaseTransfer baseTransfer = new NormalResult(BizCodeEnum.ERROR_PASSWORD.getCode(),BizCodeEnum.ERROR_PASSWORD.getMsg());
            finalHandle(baseTransfer,getEqpNo(),ctx);
            return;
        }
        /**
         * 1.保存登录信息
         * 2.返回数据
         * */
        this.setEqpNo(bizEqp.getEqpNo());
        ServerDataPool.setActivePool(getEqpNo(),ctx);
        BaseTransfer baseTransfer = new LoginResultData(BizCodeEnum.BIZ_SUCCESS.getCode(),BizCodeEnum.BIZ_SUCCESS.getMsg(),getEqpNo());
        finalHandle(baseTransfer, getEqpNo());

    }

}


public class LoginDataRe{

    private String eqpName;

    private String password;

    public String getEqpName() {
        return eqpName;
    }

    public void setEqpName(String eqpName) {
        this.eqpName = eqpName;
    }

    public String getPassword() {
        return password;
    }

    public void setPassword(String password) {
        this.password = password;
    }
}

以上只是个人写的demo测试

  • 2
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值