栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > Java

t-io实战训练

Java 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

t-io实战训练

  • 上一讲我们了解了请求分发的一点小思路,这一讲进行实战训练,项目搭建过程忽略,如果需要源码,去公众号回复"tio实战源码",后续会分享一些个人在项目方面的思考,在不同的业务下,项目的构建思路,闲言少叙

  • 这个项目是因为有个朋友需要搞个socket项目,然后让我帮忙搞一下,本着偷懒的原则然后试着引入了t-io,相关资料自行百度(https://www.tiocloud.com/2/product/tio.html, https://www.oschina.net/p/t-io),t-io作者说如果网络编程很痛苦,那一定是没用 t-io,经过使用,确实比netty要感觉简单很多

  • 首先看下项目结构


tio-demo-common 模块放一些公共的工具类以及bean
tio-demo-socket socket放socket相关的核心类
tio-demo-socket-biz biz放业务类

  • 结构比较简单

-

  • 消息包:
public class TioDemoPacket extends Packet {
   
   public static final int HEADER_LENGTH = 4;
   private byte[] body;
   public byte[] getBody() {
       return body;
   }
   public void setBody(byte[] body) {
       this.body = body;
   }
}
  • 接收参数解码:
    

   @Override
   public TioDemoPacket decode(ByteBuffer buffer, int limit, int position, int readableLength, ChannelContext channelContext) throws AioDecodeException {
       if (buffer.remaining() < TioDemoPacket.HEADER_LENGTH) {
           return null;
       } else {
           TioDemoPacket imPacket = new TioDemoPacket();
           if (buffer.remaining() < TioDemoPacket.HEADER_LENGTH) {
               return null;
           } else {
               byte[] bytes = new byte[limit];
               buffer.get(bytes);
               imPacket.setBody(bytes);
               return imPacket;
           }
       }
   }
  • 接收参数之后的解析
  TioDemoPacket packingPacket = (TioDemoPacket) packet;
      byte[] body = packingPacket.getBody();
      if (body != null) {
          String reqJson = new String(body);
          if (logPrint) {
              log.info("接收到服务器的请求信息:{},{},收到消息:{}", cc.getId(), cc.getToken(), reqJson);
          }
          if (!JSONUtil.isTypeJSON(reqJson)) {
              log.error("参数不是json");
              return;
          }
          JSONObject jb = JSONUtil.parseObj(reqJson);
          String service = jb.getStr("service");
          if (StrUtil.isEmpty(service)) {
              log.error("接口参数缺失");
              return;
          }
          if (!InterfaceNameEnum.interfaceNameIsExist(service)) {
              log.error("接口未接入");
              return;
          }
          RequestMessageHandler requestMessageHandler = requestMessageHandlerContainer.getMessageHandler(service);
          // 获得  MessageHandler 处理器 的消息类
          Class messageClass = RequestMessageHandlerContainer.getMessageClass(requestMessageHandler);
          // 解析消息
          RequestMessage requestMessage = JacksonUtils.toBean(reqJson, messageClass);
          //线程池执行
          tioDemoTaskExecutor.submit(() -> {
              //先判断是否是认证接口
              if (InterfaceNameEnum.CHECK_KEY.getName().equals(service)) {
                  requestMessageHandler.execute(cc, requestMessage);
                  return;
              }
              String token = cc.getToken();
              if (StrUtil.isEmpty(token)) {
                  log.error("调用的接口未经认证,不能调用:{}", service);
                  Tio.close(cc, token);
                  return;
              }
              requestMessageHandler.execute(cc, requestMessage);

          });
          return;
      }
  • 响应参数编码:
    
    @Override
    public ByteBuffer encode(Packet packet, TioConfig tioConfig, ChannelContext channelContext) {
        TioDemoPacket serverPacket = (TioDemoPacket) packet;
        byte[] body = serverPacket.getBody();
        int bodyLen = 0;
        if (body != null) {
            bodyLen = body.length;
        }
        //bytebuffer的总长度是 = 消息头的长度 + 消息体的长度
        int allLen = bodyLen;
        //创建一个新的bytebuffer
        ByteBuffer buffer = ByteBuffer.allocate(allLen);
        //设置字节序
        buffer.order(tioConfig.getByteOrder());
        //写入消息头----消息头的内容就是消息体的长度
        //写入消息体
        if (body != null) {
            buffer.put(body);
        }
        return buffer;
    }

  • listener 在监听到关闭以后清理token
 
    @Override
    public void onBeforeClose(ChannelContext cc, Throwable throwable, String remark, boolean isRemove) throws Exception {
        log.info("server onBeforeClose");
        log.info("关闭后清除认证信息");
        Tio.unbindToken(cc);
    }

    
    @Override
    public boolean onHeartbeatTimeout(ChannelContext cc,
                                      Long interval,
                                      int heartbeatTimeoutCount) {
        Tio.unbindToken(cc);
        return false;
    }

  • 进行认证:
package cn.tio.demo.biz.auth;

import cn.tio.demo.socket.RequestMessageHandler;
import cn.tio.demo.socket.SocketUtil;
import com.tio.common.enums.InterfaceNameEnum;
import com.tio.common.enums.ResultCodeEnum;
import com.tio.common.socket.request.AuthRequestMessage;
import com.tio.common.socket.response.AuthResponseMessage;
import com.tio.common.util.JacksonUtils;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
import org.tio.core.ChannelContext;
import org.tio.core.Tio;

import javax.validation.ConstraintViolation;
import java.util.Objects;
import java.util.Set;


@Slf4j
@Component
public class AuthRequestMessageHandler implements RequestMessageHandler {

    @Value("${parking.parkId}")
    private String parkId;

    @Value("${parking.parkKey}")
    private String parkKey;

    @Value(("${parking.logPrint:false}"))
    private Boolean logPrint;

    @Override
    public String getService() {
        return InterfaceNameEnum.CHECK_KEY.getName();
    }

    @Override
    public void execute(ChannelContext cc, AuthRequestMessage message) {
        if (logPrint) {
            log.info("客户端认证信息:{}", message);
        }
        AuthResponseMessage authResponseMessage = new AuthResponseMessage();
        authResponseMessage.setService(InterfaceNameEnum.CHECK_KEY.getName());
        Set> constraintViolationSet = validParam(message);
        if (Objects.isNull(constraintViolationSet) || !constraintViolationSet.isEmpty()) {
            authResponseMessage.setResultCode(ResultCodeEnum.FAIL_2.getCode());
            authResponseMessage.setMessage("参数不能为空");
            SocketUtil.sendMsg(cc, JacksonUtils.toJson(authResponseMessage));
            return;
        }

        String reParkId = message.getParkId();
        String reParkKey = message.getParkKey();
        if (!reParkId.equals(parkId) || !reParkKey.equals(parkKey)) {
            authResponseMessage.setResultCode(ResultCodeEnum.FAIL_1.getCode());
            authResponseMessage.setMessage("认证失败,无此车场");
            SocketUtil.sendMsg(cc, JacksonUtils.toJson(authResponseMessage));
            return;
        }
        //绑定用户
        Tio.bindToken(cc, parkId);
        authResponseMessage.setResultCode(ResultCodeEnum.SUCCESS.getCode());
        authResponseMessage.setMessage("认证成功");
        SocketUtil.sendMsg(cc, JacksonUtils.toJson(authResponseMessage));

    }


}

  • 心跳
package cn.tio.demo.biz.heartbeat;


import cn.tio.demo.socket.RequestMessageHandler;
import cn.tio.demo.socket.SocketUtil;
import com.tio.common.enums.InterfaceNameEnum;
import com.tio.common.enums.ResultCodeEnum;
import com.tio.common.socket.request.HeartBeatRequestMessage;
import com.tio.common.socket.response.HeartbeatResponseMessage;
import com.tio.common.util.JacksonUtils;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
import org.tio.core.ChannelContext;

import javax.validation.ConstraintViolation;
import java.util.Objects;
import java.util.Set;


@Slf4j
@Component
public class HeartBeatRequestMessageHandler implements RequestMessageHandler {

    @Value(("${parking.logPrint:false}"))
    private Boolean logPrint;

    @Override
    public String getService() {
        return InterfaceNameEnum.HEART_BEAT.getName();
    }


    @Override
    public void execute(ChannelContext cc, HeartBeatRequestMessage message) {
        if (logPrint) {
            log.info("接收到的客户端心跳结果:{}", message);
        }
        HeartbeatResponseMessage responseMessage = new HeartbeatResponseMessage();
        responseMessage.setService(InterfaceNameEnum.HEART_BEAT.getName());
        Set> constraintViolationSet = validParam(message);
        if (Objects.isNull(constraintViolationSet) || !constraintViolationSet.isEmpty()) {
            responseMessage.setResultCode(ResultCodeEnum.FAIL_2.getCode());
            responseMessage.setMessage("参数不能为空");
            SocketUtil.sendMsg(cc, JacksonUtils.toJson(responseMessage));
            return;
        }
        SocketUtil.connect = cc;
        responseMessage.setResultCode(ResultCodeEnum.SUCCESS.getCode());
        responseMessage.setMessage("在线");
        SocketUtil.sendMsg(cc, JacksonUtils.toJson(responseMessage));
    }


}

  • 一个简单的骨架就搭建好了,试着跑一下:
  • 关键代码已经贴出来了,还是不懂得可以留言,看到就会回复
  • 这是我的微信公众号二维码,你的关注是我持续更新的动力,谢谢
转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/832099.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号