前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >基于t-io官网源码旗舰版App socket对接的一点经验

基于t-io官网源码旗舰版App socket对接的一点经验

作者头像
talent-tan
修改2019-07-29 10:26:46
1.2K0
修改2019-07-29 10:26:46
举报
文章被收录于专栏:t-io小笔记t-io小笔记

原文转载自:https://www.t-io.org/1154654880242212864

tio-APP对接

对接基于TIO旗舰版(其实大同小异),基本是按照新手对接的样子写的文档,老司机可能会觉得啰嗦,望见谅。

1、必备知识

java ByteBuffer 操作

object-c byte 操作

socket 基础操作

以上三个是对接的前提

2、对接基础流程

官网文档中未详细描述App对接的流程,所以在对接的时候出现了一些小问题,对此先对对接流程进行补充整理如下:

代码语言:txt
复制
1、连接(必须SSL)
2、握手
3、心跳

注:握手验签的时候,sgin MD5后必须为大写,后台代码强制为大写验证;

3、物料准备

android:OkSocket

ios: CocoaAsyncSocket

4、Android对接

4.1 IM链接管理

socket 基础配置及联系

代码语言:txt
复制
package com.biuo.sdk;


import java.security.NoSuchAlgorithmException;

import javax.net.ssl.SSLContext;

import android.content.Context;
import android.support.annotation.NonNull;

import com.biuo.sdk.common.sendable.BaseSend;
import com.biuo.sdk.common.sendable.HeartBeatSend;
import com.biuo.sdk.constans.Constants;
import com.biuo.sdk.exception.SendException;
import com.biuo.sdk.handler.ImActionHandler;
import com.biuo.sdk.handler.ImHeadHandler;
import com.xuhao.didi.socket.client.sdk.OkSocket;
import com.xuhao.didi.socket.client.sdk.client.ConnectionInfo;
import com.xuhao.didi.socket.client.sdk.client.OkSocketOptions;
import com.xuhao.didi.socket.client.sdk.client.OkSocketSSLConfig;
import com.xuhao.didi.socket.client.sdk.client.bean.IPulse;
import com.xuhao.didi.socket.client.sdk.client.connection.IConnectionManager;
/**
  * IM连接管理
  *
  * @author zhangjunhu
  */
public class ImClient {

  private static ImClient mImClient;
  public static Context context;
  private ConnectionInfo mConnectionInfo;
  private String ip;
  private String cId;
  private int port;
  private int connectTimeout = 1;//连接超时时间,单位:秒
  private int heartbeatTimeout = 30 * 1000;//心跳超时时间,单位:毫秒
  private int reconnectMaximum = 60; //最大重连次数
  private boolean isSslConnect = true;//是否开启SSL链接

  private IConnectionManager connectionManager;

  public static ImClient getInstance() {
    if (null == mImClient) {
      mImClient = new ImClient();
    }
    return mImClient;
  }

  private ImClient() {
  }

  /**
   * 初始化调用,在Application中初始化,为后续网络状态判断等提供
   *
   * @param context
   */
  public void init(Context context) {
    ImClient.context = context;
  }


  /**
   *
   * @param ip
   * @param port
   * @return
   */
  public ImClient setConnectInfo(@NonNull String ip, @NonNull int port) {
    this.ip = ip;
    this.port = port;
    return this;
  }

  /**
   * 连接超时时间,单位:秒
   *
   * @param connectTimeout
   * @return
   */
  public ImClient setConnectTimeout(@NonNull int connectTimeout) {
    this.connectTimeout = connectTimeout;
    return this;
  }

  /**
   * 心跳超时时间,单位:毫秒
   *
   * @param heartbeatTimeout
   * @return
   */
  public ImClient setHeartbeatTimeout(@NonNull int heartbeatTimeout) {
    this.heartbeatTimeout = heartbeatTimeout;
    return this;
  }

  /**
   * 最大重连次数
   *
   * @param reconnectMaximum
   * @return
   */
  public ImClient setReconnectMaximum(@NonNull int reconnectMaximum) {
    this.reconnectMaximum = reconnectMaximum;
    return this;
  }

  public ImClient isSslConnect(@NonNull boolean isSslConnect) {
    this.isSslConnect = isSslConnect;
    return this;
  }


  public String getcId() {
    return cId;
  }

  public void setcId(String cId) {
    this.cId = cId;
  }

  private ConnectionInfo getConnectionInfo() {
    if (mConnectionInfo == null) {
      mConnectionInfo = new ConnectionInfo(this.ip, this.port);
    }
    return mConnectionInfo;
  }

  private OkSocketSSLConfig getSSlConfig() {
    OkSocketSSLConfig.Builder mSSLConfig = new OkSocketSSLConfig.Builder();
    try {
      mSSLConfig.setProtocol(SSLContext.getInstance("SSL").getProtocol());
    } catch (NoSuchAlgorithmException e) {
      e.printStackTrace();
    }
    return mSSLConfig.build();
  }


  private OkSocketOptions.Builder getDefaultSocketOptions(@NonNull OkSocketOptions okSocketOptions) {
    return new OkSocketOptions.Builder(okSocketOptions)
        .setWritePackageBytes(Constants.MAX_LENGTH_OF_BODY)
        .setReadPackageBytes(Constants.MAX_LENGTH_OF_BODY)
        .setConnectionHolden(true)
        .setConnectTimeoutSecond(connectTimeout)//链接超时时间设置
        .setPulseFrequency(heartbeatTimeout)//心跳设置
        .setPulseFeedLoseTimes(reconnectMaximum)//最大重连次数
        .setReaderProtocol(ImHeadHandler.getInstance());//设置报文头解析
  }

  /**
   * 连接
   */
  public synchronized void connect() {
    connectionManager = OkSocket.open(getConnectionInfo());
    connectionManager.setIsConnectionHolder(true);
    OkSocketOptions.Builder builder = getDefaultSocketOptions(connectionManager.getOption());
    if (isSslConnect) {//开启SSL链接
      builder.setSSLConfig(getSSlConfig());
    }
    connectionManager.option(builder.build());
    connectionManager.registerReceiver(ImActionHandler.getInstance(this));//监听消息发送接口及状态
    connectionManager.connect();
  }


  /**
   * 获取链接管理器
   *
   * @return
   */
  public IConnectionManager getConnectionManager() {
    return connectionManager;
  }

  /**
   * 关闭连接
   */
  public synchronized void disconnect() {
    if (null == connectionManager || !connectionManager.isConnect() || connectionManager.isDisconnecting()) {
      return;
    }
    connectionManager.disconnect();
  }


  /**
   * 获取心跳检测配置
   *
   * @return
   */
  public IPulse getHeartBeatPulse() {
    return connectionManager.getPulseManager()
        .setPulseSendable(new HeartBeatSend());//只需要设置一次,下一次可以直接调用pulse()
  }

  /**
   * 信息发送
   *
   * @param baseSend
   * @throws SendException
   */
  public void sendData(BaseSend baseSend) throws SendException {
    if (null == connectionManager || !connectionManager.isConnect() || connectionManager.isDisconnecting())
      throw new SendException("服务未连接");
    connectionManager.send(baseSend);
  }

}

4.2 信息发送基类

代码语言:txt
复制
package com.biuo.sdk.common.sendable;



import android.annotation.SuppressLint;

import com.biuo.sdk.common.Command;
import com.biuo.sdk.common.ImPacket;
import com.biuo.sdk.common.bs.Base;
import com.biuo.utils.SystemTools;
import com.xuhao.didi.core.iocore.interfaces.ISendable;

/**
 * 信息发送基类
 * @author zhangjunhu
 */
public class BaseSend implements ISendable {

  private Command command;
  private Base base;

  public BaseSend(Command command) {
    this.command = command;
  }

  public BaseSend(Command command, Base base) {
    this.command = command;
    this.base = base;
  }

  public void setCommand(Command command) {
    this.command = command;
  }


  public void setBody(Base base) {
      this.base = base;
  }


  @SuppressLint("NewApi")
  @Override
  public byte[] parse() {
    return SystemTools.encode(new ImPacket(command, base)).array();
  }
}

4.3 数据加解密

代码语言:txt
复制
package com.biuo.utils;

import java.io.UnsupportedEncodingException;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;

import android.annotation.SuppressLint;
import cn.hutool.core.util.StrUtil;
import cn.hutool.core.util.ZipUtil;

import com.alibaba.fastjson.JSON;
import com.biuo.sdk.entity.ImPacket;
import com.biuo.sdk.common.bs.Base;
import com.biuo.sdk.constans.Constants;
import com.biuo.sdk.exception.DecodeException;
import com.goboosoft.commons.utils.LogUtils;

/**
 * 数据加解密
 * @author zhangjunhu
 */
@SuppressLint("SimpleDateFormat")
public final class SystemTools {

  /**
   *
   * @param imPacket
   * @return
   */
  public static ByteBuffer encode(ImPacket imPacket) {
    byte[] body = imPacket.getBody();
    if (body == null) {
      String bodyStr = imPacket.getBodyStr();
      if (StrUtil.isBlank(bodyStr)) {
        Base bodyObj = imPacket.getBodyObj();
        if (bodyObj != null) {
          bodyStr = JSON.toJSONString(bodyObj);
        }
      }

      if (StrUtil.isNotBlank(bodyStr)) {
        try {
          body = bodyStr.getBytes(Constants.CHARSET);
        } catch (UnsupportedEncodingException e) {
          LogUtils.e(e.getMessage());
        }
      }
    }

    int bodyLen = 0;
    boolean isCompress = false;
    if (body != null) {
      bodyLen = body.length;
      if (bodyLen > Constants.SIZE_FOR_COMPRESS) {
        try {
          byte[] gzipedbody = ZipUtil.gzip(body);
          if (gzipedbody.length < body.length) {
            LogUtils.i("压缩前:" + body.length + ", 压缩后: " + gzipedbody.length);
            body = gzipedbody;
            bodyLen = gzipedbody.length;
            isCompress = true;
          }
        } catch (Throwable e) {
          LogUtils.e(e.getMessage());
        }
      }
    }

    int allLen = Constants.LEAST_HEADER_LENGTH + bodyLen;
    LogUtils.i("allLen: " + allLen + ", bodyLen: " + bodyLen + ", getCommand: " + imPacket.getCommand().getValue());
    ByteBuffer buffer = ByteBuffer.allocate(allLen);
    buffer.order(ByteOrder.BIG_ENDIAN);
    buffer.putShort((short) bodyLen);
    buffer.putShort(imPacket.getCommand().getValue());
    buffer.put((byte) (isCompress ? 1 : 0)) ;
    if (body != null) {
      buffer.put(body);
    }
    return buffer;
  }


  public static String decode(ByteBuffer buffer, short bodyLength, boolean isGzipped) throws DecodeException, UnsupportedEncodingException {
    String content = "";
    if (bodyLength > Constants.MAX_LENGTH_OF_BODY || bodyLength < 0) {
      throw new DecodeException("bodyLength [" + bodyLength + "] is not right");
    }
    if (buffer.array().length == 0) {
      return content;
    }
    byte[] contentBytes = buffer.array();

    if (isGzipped) {
      byte[] dst = new byte[bodyLength];
      buffer.get(dst);
      contentBytes = ZipUtil.unGzip(dst);
    }
    content = new String(contentBytes, Constants.CHARSET);
    return content;
  }
}

4.4 消息体头解析

代码语言:txt
复制
package com.biuo.sdk.handler;

import java.nio.ByteBuffer;
import java.nio.ByteOrder;

import com.biuo.sdk.constans.Constants;
import com.xuhao.didi.core.protocol.IReaderProtocol;

/**
 * IM消息头解析
 * @author zhangjunhu
 */
public class ImHeadHandler implements IReaderProtocol {

  public static ImHeadHandler getInstance() {return new ImHeadHandler();}

  private ImHeadHandler() {}

  @Override
  public int getHeaderLength() {
    return Constants.LEAST_HEADER_LENGTH;
  }

  @Override
  public int getBodyLength(byte[] header, ByteOrder byteOrder) {
    ByteBuffer byteBuffer = ByteBuffer.wrap(header);
    return byteBuffer.getShort();
  }
}

4.5 IM状态处理

主要对IM的连接状及消息收发状态进行监听及处理

代码语言:txt
复制
package com.biuo.sdk.handler;

import java.io.UnsupportedEncodingException;
import java.nio.ByteBuffer;

import org.greenrobot.eventbus.EventBus;

import android.annotation.SuppressLint;
import android.util.Log;


import com.alibaba.fastjson.JSON;
import com.biuo.sdk.ImClient;
import com.biuo.sdk.common.Command;
import com.biuo.sdk.common.bs.P2PChatNtf;
import com.biuo.sdk.common.enums.ConnectStatus;
import com.biuo.sdk.event.ImConnectEvent;
import com.biuo.sdk.event.MsgAcceptEnent;
import com.biuo.sdk.exception.DecodeException;
import com.biuo.utils.SystemTools;
import com.goboosoft.commons.utils.AppConfig;
import com.goboosoft.commons.utils.LogUtils;
import com.xuhao.didi.core.iocore.interfaces.IPulseSendable;
import com.xuhao.didi.core.iocore.interfaces.ISendable;
import com.xuhao.didi.core.pojo.OriginalData;
import com.xuhao.didi.socket.client.sdk.client.ConnectionInfo;
import com.xuhao.didi.socket.client.sdk.client.action.SocketActionAdapter;

/**
 * IM状态处理
 * @author zhangjunhu
 */
public class ImActionHandler extends SocketActionAdapter {

  private ImClient mClient;

  public static ImActionHandler getInstance(ImClient mClient) {return new ImActionHandler(mClient);}

  private ImActionHandler(ImClient mClient) {
    this.mClient = mClient;
  }

  @SuppressLint("CheckResult")
  @Override
  public void onSocketConnectionSuccess(ConnectionInfo info, String action) {
    EventBus.getDefault().postSticky(ImConnectEvent.getInstance(ConnectStatus.CONNECT_SUCCESS));
  }


  @Override
  public void onSocketConnectionFailed(ConnectionInfo info, String action, Exception e) {
    EventBus.getDefault().postSticky(ImConnectEvent.getInstance(ConnectStatus.CONNECT_FAIL));
  }

  @Override
  public void onSocketDisconnection(ConnectionInfo info, String action, Exception e) {
    EventBus.getDefault().postSticky(ImConnectEvent.getInstance(ConnectStatus.CONNECT_CLOSE));
  }

  public void onSocketReadResponse(ConnectionInfo info, String action, OriginalData data) {
    //每次收到消息进行oksocket 的喂狗操作

    //对协议头进行解析
    ByteBuffer headBuffer = ByteBuffer.wrap(data.getHeadBytes());
    short bodyLength = headBuffer.getShort();
    short commandValue = headBuffer.getShort();
    boolean isGzipped = headBuffer.get() == (byte) 1;

    String content = null;
    try {
    //对内容进行解码
      content = SystemTools.decode(ByteBuffer.wrap(data.getBodyBytes()), bodyLength, isGzipped);
    } catch (DecodeException e) {
      e.printStackTrace();
    } catch (UnsupportedEncodingException e) {
      e.printStackTrace();
    }


    Command command = Command.from(commandValue);
    if (command == null) {
      LogUtils.e("消息命令码【" + commandValue + "】不正确");
      return;
    }



    switch (Command.from(commandValue)) {
      case HANDSHAKE_RESP://握手响应 握手成功后,开始心跳

        break;

      case JOIN_GROUP_RESP://进入群组响应

        break;
      case JOIN_GROUP_NTY://进入群组通知

        break;
      case LEAVE_GROUP_NTY://离开群组通知

        break;
      case P2P_CHAT_NTY://(私聊)通知

        break;
      case GROUP_CHAT_NTY://群聊通知

        break;
      case MSG_TIP://消息提示

        break;
      case PAGE_ONLINE_RESP://分页获取在线观众响应

        break;
      case UPDATE_TOKEN_RESP://更新token响应

        break;
      case UNSEND_MSG_NTY://撤回消息通知

        break;
      case P2P_ALREADY_READ_NTY://已读通知: 服务器告诉张三,张三发给李四的私聊,李四已经阅读

        break;
      case P2P_RECENT_CHAT_LIST_RESP://查询最近私聊列表响应

        break;
      default:
        break;
    }

  }

  public void onSocketWriteResponse(ConnectionInfo info, String action, ISendable data) {
  }

  public void onPulseSend(ConnectionInfo info, IPulseSendable data) {
  }


}

4.6 android 总结

上面放了大量的源码,结合官网的文档及oksocket,消息的接口及发送不成任何问题;

1、关于心跳,稍微看下文档,就能很快解决。

2、上述代码完整性,不是不愿意贴出所有代码,是留点学习空间。

3、对于消息的处理、网络处理等等需要自己根据实际业务去处理。

5 IOS对接【木有写完】

本文系转载,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文系转载前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • tio-APP对接
  • 1、必备知识
  • 2、对接基础流程
  • 3、物料准备
  • 4、Android对接
    • 4.1 IM链接管理
      • 4.2 信息发送基类
        • 4.3 数据加解密
          • 4.4 消息体头解析
            • 4.5 IM状态处理
              • 4.6 android 总结
              • 5 IOS对接【木有写完】
              领券
              问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档