| | |
| | | implementation 'com.wang.avi:library:2.1.3' |
| | | //地址选择 |
| | | implementation project(':pickerviewlibrary') |
| | | |
| | | implementation project(':easysocket') |
| | | implementation 'com.tencent.bugly:crashreport:latest.release' |
| | | // 处理图片 |
| | | // implementation (name: 'ocr-library', ext: 'aar') |
| | |
| | | <activity android:name=".activity.IdentifyingActivity" /> |
| | | <activity android:name=".activity.ReplacementActivity" /> |
| | | <activity android:name=".activity.RechargeDetail" /> |
| | | <activity android:name=".activity.PasswordCardActivity" /> |
| | | |
| | | |
| | | <meta-data |
| | |
| | | startActivity(new Intent(HomeActivity.this, ReadCardAcitivy.class)); |
| | | } |
| | | }); |
| | | //管理系统界面 |
| | | homeBinding.homeAdmin.setOnClickListener(new View.OnClickListener() { |
| | | @Override |
| | | public void onClick(View v) { |
| | |
| | | startActivity(intent); |
| | | } |
| | | }); |
| | | //密码卡设置密码 |
| | | binding.parameterPassWordCard.setOnClickListener(new View.OnClickListener() { |
| | | @Override |
| | | public void onClick(View v) { |
| | | Intent intent = new Intent(ParameterActivity.this, PasswordCardActivity.class); |
| | | startActivity(intent); |
| | | } |
| | | }); |
| | | } |
| | | } |
| | |
| | | import com.dayu.recharge.card.ConfigureDeviceRegistrationCrad; |
| | | import com.dayu.recharge.card.DomainCard; |
| | | import com.dayu.recharge.card.ManageCard; |
| | | import com.dayu.recharge.card.PassWordCard; |
| | | import com.dayu.recharge.card.RegisteredCard; |
| | | import com.dayu.recharge.card.TestCard; |
| | | import com.dayu.recharge.databinding.ActivityAdminBinding; |
| | | import com.dayu.recharge.dbBean.DomainBean; |
| | | import com.dayu.recharge.dbBean.PassWordCardBean; |
| | | import com.dayu.recharge.dbBean.PowerBean; |
| | | import com.dayu.recharge.utils.TipUtil; |
| | | |
| | |
| | | startActivity(intent); |
| | | } |
| | | }); |
| | | |
| | | //制作测试卡 |
| | | adminBinding.adminTest.setOnClickListener(new View.OnClickListener() { |
| | | @Override |
| | | public void onClick(View v) { |
| | |
| | | startActivity(intent); |
| | | } |
| | | }); |
| | | |
| | | //制作配置设备信息卡 |
| | | adminBinding.adminConfigDeviceRegistration.setOnClickListener(new View.OnClickListener() { |
| | | @Override |
| | | public void onClick(View v) { |
| | |
| | | } |
| | | }); |
| | | |
| | | //制作配置水泵功率卡 |
| | | adminBinding.adminConfigPower.setOnClickListener(new View.OnClickListener() { |
| | | @Override |
| | | public void onClick(View v) { |
| | |
| | | } |
| | | }); |
| | | |
| | | |
| | | adminBinding.adminPassWordCard.setOnClickListener(new View.OnClickListener() { |
| | | @Override |
| | | public void onClick(View v) { |
| | | PassWordCardBean powerBean = baseDao.passWordCardDao().findFirst(); |
| | | if (powerBean != null) { |
| | | Intent intent = new Intent(SysActivity.this, NFCWreatActivity.class); |
| | | PassWordCard passWordCard = new PassWordCard(); |
| | | passWordCard.setPassWord(powerBean.getPassWord()); |
| | | intent.putExtra("passWordCard", passWordCard); |
| | | startActivity(intent); |
| | | } else { |
| | | TipUtil.show("请先设置卡密码"); |
| | | } |
| | | } |
| | | }); |
| | | |
| | | //制作区域表号卡 |
| | | adminBinding.adminRegionCard.setOnClickListener(new View.OnClickListener() { |
| | | @Override |
| | | public void onClick(View v) { |
| | | |
| | | } |
| | | }); |
| | | } |
| | | |
| | | |
| | |
| | | import com.dayu.recharge.tools.HexUtil; |
| | | import com.dayu.recharge.utils.MyCommon; |
| | | |
| | | import java.io.Serializable; |
| | | import java.util.List; |
| | | |
| | | /** |
| | | * Copyright (C), 2023, |
| | | * Copyright (C), 2024, |
| | | * Author: zuo |
| | | * Date: 2023-11-08 11:22 |
| | | * Date: 2024-3-04 11:22 |
| | | * Description:密码卡 |
| | | */ |
| | | public class PassWordCard { |
| | | public class PassWordCard implements Serializable { |
| | | public String cardType = MyCommon.PASS_WORD_CRAD_TYPE;//卡类型 |
| | | public String cardData = "A0B1C289";//标识码 |
| | | public String passWord;//六位的密码 |
| | | |
| | | public String getPassWord() { |
| | | return passWord; |
| | | } |
| | | |
| | | public void setPassWord(String passWord) { |
| | | this.passWord = passWord; |
| | | } |
| | | |
| | | public static PassWordCard getBean(List<byte[]> data){ |
| | | |
| | |
| | | * author: zuo |
| | | * Date: 2024-02-29 |
| | | * Time: 16:29 |
| | | * 备注:区域标号卡 |
| | | * 备注:区域表号卡 |
| | | */ |
| | | public class RegionCard implements Serializable { |
| | | |
| | |
| | | android:layout_marginLeft="20dp" |
| | | android:layout_marginTop="20dp" |
| | | android:padding="5dp" |
| | | android:visibility="gone" |
| | | android:text="制作重新注册设备卡" |
| | | android:textColor="@color/text_selecter" |
| | | android:textSize="@dimen/text_size" /> |
| | |
| | | android:layout_marginLeft="20dp" |
| | | android:layout_marginTop="20dp" |
| | | android:padding="5dp" |
| | | android:visibility="gone" |
| | | android:text="制作删除全部用户卡" |
| | | android:textColor="@color/text_selecter" |
| | | android:textSize="@dimen/text_size" /> |
| | |
| | | android:layout_marginTop="20dp" |
| | | android:padding="5dp" |
| | | android:text="制作设置域名卡" |
| | | android:visibility="gone" |
| | | android:textColor="@color/text_selecter" |
| | | android:textSize="@dimen/text_size" /> |
| | | |
| | |
| | | android:layout_marginTop="20dp" |
| | | android:padding="5dp" |
| | | android:text="制作测试卡" |
| | | android:visibility="gone" |
| | | android:textColor="@color/text_selecter" |
| | | android:textSize="@dimen/text_size" /> |
| | | |
| | |
| | | android:layout_marginTop="20dp" |
| | | android:padding="5dp" |
| | | android:text="制作配置设备信息卡" |
| | | android:visibility="gone" |
| | | android:textColor="@color/text_selecter" |
| | | android:textSize="@dimen/text_size" /> |
| | | |
| | | <TextView |
| | | android:id="@+id/admin_passWordCard" |
| | | android:layout_width="match_parent" |
| | | android:layout_height="wrap_content" |
| | | android:layout_marginLeft="20dp" |
| | | android:layout_marginTop="20dp" |
| | | android:padding="5dp" |
| | | android:text="制作密码卡" |
| | | android:textColor="@color/text_selecter" |
| | | android:textSize="@dimen/text_size" /> |
| | | |
| | |
| | | android:text="制作配置水泵功率卡" |
| | | android:textColor="@color/text_selecter" |
| | | android:textSize="@dimen/text_size" /> |
| | | |
| | | <TextView |
| | | android:id="@+id/admin_blackCard" |
| | | android:layout_width="match_parent" |
| | | android:layout_height="wrap_content" |
| | | android:layout_marginLeft="20dp" |
| | | android:layout_marginTop="20dp" |
| | | android:padding="5dp" |
| | | android:text="制作黑卡" |
| | | android:textColor="@color/text_selecter" |
| | | android:textSize="@dimen/text_size" /> |
| | | |
| | | <TextView |
| | | android:id="@+id/admin_regionCard" |
| | | android:layout_width="match_parent" |
| | | android:layout_height="wrap_content" |
| | | android:layout_marginLeft="20dp" |
| | | android:layout_marginTop="20dp" |
| | | android:padding="5dp" |
| | | android:text="制作区域表号卡" |
| | | android:textColor="@color/text_selecter" |
| | | android:textSize="@dimen/text_size" /> |
| | | </LinearLayout> |
| | | |
| | | |
| | |
| | | android:layout_marginLeft="20dp" |
| | | android:layout_marginTop="20dp" |
| | | android:padding="5dp" |
| | | android:visibility="gone" |
| | | android:text="域名卡设置" |
| | | android:textColor="@color/text_selecter" |
| | | android:textSize="@dimen/text_size" /> |
| | |
| | | android:layout_marginLeft="20dp" |
| | | android:layout_marginTop="20dp" |
| | | android:padding="5dp" |
| | | android:visibility="gone" |
| | | android:text="水泵功率卡设置" |
| | | android:textColor="@color/text_selecter" |
| | | android:textSize="@dimen/text_size" /> |
| | |
| | | android:text="电量单价设置" |
| | | android:textColor="@color/text_selecter" |
| | | android:textSize="@dimen/text_size" /> |
| | | |
| | | <TextView |
| | | android:id="@+id/parameter_PassWordCard" |
| | | android:layout_width="match_parent" |
| | | android:layout_height="wrap_content" |
| | | android:layout_marginLeft="20dp" |
| | | android:layout_marginTop="20dp" |
| | | android:padding="5dp" |
| | | android:text="电量单价设置" |
| | | android:textColor="@color/text_selecter" |
| | | android:textSize="@dimen/text_size" /> |
| | | <TextView |
| | | android:id="@+id/parameter_cardIdentifying" |
| | | android:layout_width="match_parent" |
| | |
| | | android:layout_marginTop="20dp" |
| | | android:padding="5dp" |
| | | android:text="卡标识码设置" |
| | | android:visibility="gone" |
| | | android:textColor="@color/text_selecter" |
| | | android:textSize="@dimen/text_size" /> |
| | | |
| New file |
| | |
| | | apply plugin: 'com.android.library' |
| | | android { |
| | | compileSdkVersion 27 |
| | | //buildToolsVersion rootProject.ext.android.buildToolsVersion |
| | | |
| | | defaultConfig { |
| | | minSdkVersion 14 |
| | | targetSdkVersion 27 |
| | | versionCode 1 |
| | | versionName "1.0" |
| | | // javaCompileOptions { annotationProcessorOptions { includeCompileClasspath = true } } |
| | | } |
| | | |
| | | buildTypes { |
| | | release { |
| | | minifyEnabled false |
| | | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' |
| | | } |
| | | } |
| | | |
| | | } |
| | | |
| | | dependencies { |
| | | implementation fileTree(dir: 'libs', include: ['*.jar']) |
| | | compileOnly 'com.google.code.gson:gson:2.2.4' |
| | | } |
| | | |
| | | sourceCompatibility = "7" |
| | | targetCompatibility = "7" |
| New file |
| | |
| | | <manifest xmlns:android="http://schemas.android.com/apk/res/android" |
| | | package="com.socket" > |
| | | <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" /> |
| | | </manifest> |
| New file |
| | |
| | | package com.easysocket; |
| | | |
| | | import com.easysocket.config.EasySocketOptions; |
| | | import com.easysocket.connection.connect.SuperConnection; |
| | | import com.easysocket.connection.connect.TcpConnection; |
| | | import com.easysocket.entity.SocketAddress; |
| | | import com.easysocket.interfaces.config.IConnectionSwitchListener; |
| | | import com.easysocket.interfaces.conn.IConnectionManager; |
| | | import com.easysocket.utils.LogUtil; |
| | | |
| | | import java.util.HashMap; |
| | | import java.util.Map; |
| | | |
| | | /** |
| | | * Author:Alex |
| | | * Date:2019/6/4 |
| | | * Note:socket连接管理器 |
| | | */ |
| | | public class ConnectionHolder { |
| | | |
| | | private volatile Map<String, IConnectionManager> mConnectionManagerMap = new HashMap<>(); |
| | | |
| | | |
| | | private static class InstanceHolder { |
| | | private static final ConnectionHolder INSTANCE = new ConnectionHolder(); |
| | | } |
| | | |
| | | public static ConnectionHolder getInstance() { |
| | | return InstanceHolder.INSTANCE; |
| | | } |
| | | |
| | | private ConnectionHolder() { |
| | | mConnectionManagerMap.clear(); |
| | | } |
| | | |
| | | /** |
| | | * 移除某个连接 |
| | | * |
| | | * @param socketAddress |
| | | */ |
| | | public void removeConnection(SocketAddress socketAddress) { |
| | | removeConnection(createKey(socketAddress)); |
| | | } |
| | | |
| | | public void removeConnection(String socketAddress) { |
| | | mConnectionManagerMap.remove(socketAddress); |
| | | } |
| | | |
| | | /** |
| | | * 获取指定SocketAddress的连接,参数配置使用默认的 |
| | | * |
| | | * @param address |
| | | * @return |
| | | */ |
| | | public IConnectionManager getConnection(SocketAddress address) { |
| | | return getConnection(createKey(address)); |
| | | } |
| | | |
| | | public IConnectionManager getConnection(String address) { |
| | | IConnectionManager manager = mConnectionManagerMap.get(address); |
| | | if (manager == null) { |
| | | return getConnection(address, EasySocketOptions.getDefaultOptions()); |
| | | } else { |
| | | return getConnection(address, manager.getOptions()); |
| | | } |
| | | } |
| | | |
| | | /** |
| | | * 获取指定SocketAddress的连接 |
| | | * |
| | | * @param address |
| | | * @param socketOptions |
| | | * @return |
| | | */ |
| | | public IConnectionManager getConnection(SocketAddress address, EasySocketOptions socketOptions) { |
| | | return getConnection(createKey(address),socketOptions); |
| | | } |
| | | |
| | | public IConnectionManager getConnection(String address, EasySocketOptions socketOptions) { |
| | | IConnectionManager manager = mConnectionManagerMap.get(address); |
| | | if (manager != null) { // 有缓存 |
| | | manager.setOptions(socketOptions); |
| | | return manager; |
| | | } else { |
| | | return createNewManagerAndCache(address, socketOptions); |
| | | } |
| | | } |
| | | |
| | | /** |
| | | * 创建新的连接并缓存 |
| | | * |
| | | * @param address |
| | | * @param socketOptions |
| | | * @return |
| | | */ |
| | | private IConnectionManager createNewManagerAndCache(SocketAddress address, EasySocketOptions socketOptions) { |
| | | SuperConnection manager = new TcpConnection(address); // 创建连接管理器 |
| | | manager.setOptions(socketOptions); // 设置参数 |
| | | // 连接主机的切换监听 |
| | | manager.setOnConnectionSwitchListener(new IConnectionSwitchListener() { |
| | | @Override |
| | | public void onSwitchConnectionInfo(IConnectionManager manager, SocketAddress oldAddress, |
| | | SocketAddress newAddress) { |
| | | // 切换了另外一个主机的连接,删除旧的连接和添加新的连接 |
| | | synchronized (mConnectionManagerMap) { |
| | | // 首先断开连接,销毁相关线程和资源 |
| | | LogUtil.d("---> 首先断开连接,销毁相关线程和资源"); |
| | | mConnectionManagerMap.get(createKey(oldAddress)).disconnect(false); |
| | | mConnectionManagerMap.remove(createKey(oldAddress)); |
| | | mConnectionManagerMap.put(createKey(newAddress), manager); |
| | | } |
| | | } |
| | | }); |
| | | |
| | | synchronized (mConnectionManagerMap) { |
| | | mConnectionManagerMap.put(createKey(address), manager); |
| | | } |
| | | return manager; |
| | | } |
| | | |
| | | private IConnectionManager createNewManagerAndCache(String address, EasySocketOptions socketOptions) { |
| | | return createNewManagerAndCache(createSocketAddress(address), socketOptions); |
| | | } |
| | | |
| | | /** |
| | | * @param socketAddress |
| | | * @return |
| | | */ |
| | | private String createKey(SocketAddress socketAddress) { |
| | | return socketAddress.getIp() + ":" + socketAddress.getPort(); |
| | | } |
| | | |
| | | private SocketAddress createSocketAddress(String address) { |
| | | String[] s = address.split(":"); |
| | | return new SocketAddress(s[0], Integer.parseInt(s[1])); |
| | | } |
| | | |
| | | } |
| New file |
| | |
| | | package com.easysocket; |
| | | |
| | | import android.content.Context; |
| | | |
| | | import com.easysocket.config.EasySocketOptions; |
| | | import com.easysocket.connection.heartbeat.HeartManager; |
| | | import com.easysocket.entity.SocketAddress; |
| | | import com.easysocket.entity.basemsg.SuperCallbackSender; |
| | | import com.easysocket.exception.InitialExeption; |
| | | import com.easysocket.exception.NotNullException; |
| | | import com.easysocket.interfaces.conn.IConnectionManager; |
| | | import com.easysocket.interfaces.conn.ISocketActionListener; |
| | | import com.easysocket.utils.LogUtil; |
| | | |
| | | import java.nio.charset.Charset; |
| | | |
| | | /** |
| | | * Author:Alex |
| | | * Date:2019/6/4 |
| | | * Note:EasySocket API |
| | | */ |
| | | public class EasySocket { |
| | | |
| | | /** |
| | | * 连接的缓存 |
| | | */ |
| | | private static ConnectionHolder connectionHolder = ConnectionHolder.getInstance(); |
| | | // 单例 |
| | | private volatile static EasySocket singleton = null; |
| | | /** |
| | | * 默认的连接参数 |
| | | */ |
| | | private EasySocketOptions defOptions; |
| | | /** |
| | | * 默认的连接 |
| | | */ |
| | | private IConnectionManager defConnection; |
| | | /** |
| | | * 上下文 |
| | | */ |
| | | private Context context; |
| | | |
| | | /** |
| | | * 单例 |
| | | * |
| | | * @return |
| | | */ |
| | | public static EasySocket getInstance() { |
| | | if (singleton == null) { |
| | | synchronized (EasySocket.class) { |
| | | if (singleton == null) { |
| | | singleton = new EasySocket(); |
| | | } |
| | | } |
| | | } |
| | | return singleton; |
| | | } |
| | | |
| | | /** |
| | | * 获取上下文 |
| | | * |
| | | * @return |
| | | */ |
| | | public Context getContext() { |
| | | return context; |
| | | } |
| | | |
| | | /** |
| | | * 获取默认的配置参数 |
| | | * |
| | | * @return |
| | | */ |
| | | public EasySocketOptions getDefOptions() { |
| | | return defOptions == null ? EasySocketOptions.getDefaultOptions() : defOptions; |
| | | } |
| | | |
| | | /** |
| | | * 创建socket连接,此连接为默认的连接,如果你的项目只有一个Socket连接,可以用这个方法, |
| | | * 在方法不指定连接地址的情况下,默认使用都是这个连接, |
| | | * 比如: upMessage(byte[] message)、 connect()等 |
| | | * |
| | | * @return |
| | | */ |
| | | public EasySocket createConnection(EasySocketOptions options, Context context) { |
| | | this.defOptions = options; |
| | | this.context = context; |
| | | SocketAddress socketAddress = options.getSocketAddress(); |
| | | if (options.getSocketAddress() == null) { |
| | | throw new InitialExeption("请在初始化的时候设置SocketAddress"); |
| | | } |
| | | // 如果有备用主机则设置 |
| | | if (options.getBackupAddress() != null) { |
| | | socketAddress.setBackupAddress(options.getBackupAddress()); |
| | | } |
| | | if (defConnection == null) { |
| | | defConnection = connectionHolder.getConnection(socketAddress, |
| | | options == null ? EasySocketOptions.getDefaultOptions() : options); |
| | | // 执行连接 |
| | | defConnection.connect(); |
| | | } |
| | | return this; |
| | | } |
| | | |
| | | /** |
| | | * 连接socket,作用于默认连接 |
| | | * |
| | | * @return |
| | | */ |
| | | public EasySocket connect() { |
| | | getDefconnection().connect(); |
| | | return this; |
| | | } |
| | | |
| | | /** |
| | | * @param address socket地址,包括ip和端口 |
| | | * @return |
| | | */ |
| | | public EasySocket connect(String address) { |
| | | getConnection(address).connect(); |
| | | return this; |
| | | } |
| | | |
| | | |
| | | /** |
| | | * 关闭连接,作用于默认连接 |
| | | * |
| | | * @param isNeedReconnect 是否需要重连 |
| | | * @return |
| | | */ |
| | | public EasySocket disconnect(boolean isNeedReconnect) { |
| | | LogUtil.d("EasySocket--》disconnect"); |
| | | getDefconnection().disconnect(isNeedReconnect); |
| | | return this; |
| | | } |
| | | |
| | | |
| | | /** |
| | | * 关闭连接 |
| | | * |
| | | * @param isNeedReconnect 是否需要重连 |
| | | * @return |
| | | */ |
| | | public EasySocket disconnect(String address, boolean isNeedReconnect) { |
| | | getConnection(address).disconnect(isNeedReconnect); |
| | | return this; |
| | | } |
| | | |
| | | /** |
| | | * 销毁连接对象,作用于默认连接 |
| | | * |
| | | * @return |
| | | */ |
| | | public EasySocket destroyConnection() { |
| | | LogUtil.d("easysocket--》destroyConnection"); |
| | | // 断开连接 |
| | | getDefconnection().disconnect(false); |
| | | // 移除连接 |
| | | connectionHolder.removeConnection(defOptions.getSocketAddress()); |
| | | defConnection = null; |
| | | return this; |
| | | } |
| | | |
| | | |
| | | /** |
| | | * 销毁连接对象 |
| | | * |
| | | * @return |
| | | */ |
| | | public EasySocket destroyConnection(String address) { |
| | | // 断开连接 |
| | | getConnection(address).disconnect(false); |
| | | // 移除连接 |
| | | connectionHolder.removeConnection(address); |
| | | return this; |
| | | } |
| | | |
| | | /** |
| | | * 发送有回调的消息,作用于默认连接 |
| | | * |
| | | * @param sender |
| | | * @return |
| | | */ |
| | | public IConnectionManager upCallbackMessage(SuperCallbackSender sender) { |
| | | getDefconnection().upCallbackMessage(sender); |
| | | return defConnection; |
| | | } |
| | | |
| | | /** |
| | | * 发送有回调的消息 |
| | | * |
| | | * @param sender |
| | | * @return |
| | | */ |
| | | public IConnectionManager upCallbackMessage(SuperCallbackSender sender, String address) { |
| | | return getConnection(address).upCallbackMessage(sender); |
| | | } |
| | | |
| | | |
| | | /** |
| | | * 发送byte[] |
| | | * |
| | | * @param message |
| | | * @return |
| | | */ |
| | | public IConnectionManager upMessage(byte[] message, String address) { |
| | | return getConnection(address).upBytes(message); |
| | | } |
| | | |
| | | /** |
| | | * 发送byte[],作用于默认连接 |
| | | * |
| | | * @param message |
| | | * @return |
| | | */ |
| | | public IConnectionManager upMessage(byte[] message) { |
| | | return getDefconnection().upBytes(message); |
| | | } |
| | | |
| | | |
| | | /** |
| | | * 注册监听socket行为,作用于默认连接 |
| | | * |
| | | * @param socketActionListener |
| | | */ |
| | | public EasySocket subscribeSocketAction(ISocketActionListener socketActionListener) { |
| | | getDefconnection().subscribeSocketAction(socketActionListener); |
| | | return this; |
| | | } |
| | | |
| | | |
| | | /** |
| | | * 注册监听socket行为 |
| | | * |
| | | * @param socketActionListener |
| | | */ |
| | | public EasySocket subscribeSocketAction(ISocketActionListener socketActionListener, String address) { |
| | | getConnection(address).subscribeSocketAction(socketActionListener); |
| | | return this; |
| | | } |
| | | |
| | | /** |
| | | * 开启心跳检测,作用于默认连接 |
| | | * |
| | | * @param clientHeart |
| | | * @return |
| | | */ |
| | | public EasySocket startHeartBeat(byte[] clientHeart, HeartManager.HeartbeatListener listener) { |
| | | getDefconnection().getHeartManager().startHeartbeat(clientHeart, listener); |
| | | return this; |
| | | } |
| | | |
| | | /** |
| | | * 开启心跳检测 |
| | | * |
| | | * @param clientHeart |
| | | * @return |
| | | */ |
| | | public EasySocket startHeartBeat(byte[] clientHeart, String address, HeartManager.HeartbeatListener listener) { |
| | | getConnection(address).getHeartManager().startHeartbeat(clientHeart, listener); |
| | | return this; |
| | | } |
| | | |
| | | |
| | | /** |
| | | * 获取连接 |
| | | * |
| | | * @return |
| | | */ |
| | | public IConnectionManager getDefconnection() { |
| | | if (defConnection == null) { |
| | | // throw new NotNullException("你还没有创建:" + defOptions.getSocketAddress().getIp() + ":" + defOptions.getSocketAddress().getPort() |
| | | // + "的Socket的连接,请使用com.easysocket.EasySocket.connect()方法创建一个默认的连接"); |
| | | } |
| | | return defConnection; |
| | | } |
| | | |
| | | /** |
| | | * 获取连接 |
| | | * |
| | | * @return |
| | | */ |
| | | public IConnectionManager getConnection(String address) { |
| | | IConnectionManager connectionManager = connectionHolder.getConnection(address); |
| | | if (connectionManager == null) { |
| | | throw new NotNullException("请先创建:" + address + "的Socket连接"); |
| | | } |
| | | return connectionManager; |
| | | } |
| | | |
| | | /** |
| | | * 创建指定的socket连接,如果你的项目有多个socket连接,可以用这个方法创建更多的连接, |
| | | * 当你使用带有socket地址为参数的方法的时候,作用的就是对应的连接 |
| | | * 比如:connect(String address)、 upMessage(byte[] message, String address)等 |
| | | * |
| | | * @param socketOptions |
| | | * @return |
| | | */ |
| | | public IConnectionManager createSpecifyConnection(EasySocketOptions socketOptions, Context context) { |
| | | this.context = context; |
| | | IConnectionManager connectionManager = connectionHolder.getConnection(socketOptions.getSocketAddress(), socketOptions == null |
| | | ? EasySocketOptions.getDefaultOptions() : socketOptions); |
| | | |
| | | connectionManager.connect(); |
| | | return connectionManager; |
| | | } |
| | | |
| | | /** |
| | | * 获取指定的连接 |
| | | * |
| | | * @param socketAddress |
| | | * @return |
| | | */ |
| | | public IConnectionManager getSpecifyConnection(String socketAddress) { |
| | | return connectionHolder.getConnection(socketAddress); |
| | | } |
| | | |
| | | /** |
| | | * 发送消息至指定的连接 |
| | | * |
| | | * @param sender |
| | | * @param socketAddress |
| | | */ |
| | | public IConnectionManager upToSpecifyConnection(byte[] sender, String socketAddress) { |
| | | IConnectionManager connect = getSpecifyConnection(socketAddress); |
| | | if (connect != null) { |
| | | connect.upBytes(sender); |
| | | } |
| | | return connect; |
| | | } |
| | | |
| | | /** |
| | | * 是否为debug |
| | | * |
| | | * @param debug |
| | | */ |
| | | public void setDebug(boolean debug) { |
| | | EasySocketOptions.setIsDebug(debug); |
| | | } |
| | | |
| | | } |
| New file |
| | |
| | | |
| | | package com.easysocket.callback; |
| | | |
| | | import android.app.Dialog; |
| | | import android.content.DialogInterface; |
| | | |
| | | import com.easysocket.entity.OriginReadData; |
| | | import com.easysocket.exception.RequestCancelException; |
| | | import com.easysocket.interfaces.callback.IProgressDialog; |
| | | import com.easysocket.interfaces.callback.ProgressCancelListener; |
| | | |
| | | |
| | | /** |
| | | * 自定义带有加载进度框的回调 |
| | | */ |
| | | public abstract class ProgressDialogCallBack extends SuperCallBack implements ProgressCancelListener { |
| | | |
| | | private IProgressDialog progressDialog; |
| | | private Dialog mDialog; |
| | | private boolean isShowProgress = true; |
| | | |
| | | /** |
| | | * @param |
| | | */ |
| | | public ProgressDialogCallBack(IProgressDialog progressDialog, String callbackId) { |
| | | super(callbackId); |
| | | this.progressDialog = progressDialog; |
| | | init(false); |
| | | onStart(); |
| | | } |
| | | |
| | | /** |
| | | * 自定义加载进度框,可以设置是否显示弹出框,是否可以取消 |
| | | * |
| | | * @param progressDialog dialog |
| | | * @param isShowProgress 是否显示进度 |
| | | * @param isCancel 对话框是否可以取消 |
| | | * @param |
| | | */ |
| | | public ProgressDialogCallBack(IProgressDialog progressDialog, boolean isShowProgress, |
| | | boolean isCancel, String callbackId) { |
| | | super(callbackId); |
| | | this.progressDialog = progressDialog; |
| | | this.isShowProgress = isShowProgress; |
| | | init(isCancel); |
| | | onStart(); |
| | | } |
| | | |
| | | /** |
| | | * 初始化 |
| | | * |
| | | * @param isCancel |
| | | */ |
| | | private void init(boolean isCancel) { |
| | | if (progressDialog == null) return; |
| | | mDialog = progressDialog.getDialog(); |
| | | if (mDialog == null) return; |
| | | mDialog.setCancelable(isCancel); |
| | | if (isCancel) { |
| | | mDialog.setOnCancelListener(new DialogInterface.OnCancelListener() { |
| | | @Override |
| | | public void onCancel(DialogInterface dialogInterface) { |
| | | ProgressDialogCallBack.this.onCancelProgress(); |
| | | } |
| | | }); |
| | | } |
| | | } |
| | | |
| | | /** |
| | | * 展示进度框 |
| | | */ |
| | | private void showProgress() { |
| | | if (!isShowProgress) { |
| | | return; |
| | | } |
| | | if (mDialog != null && !mDialog.isShowing()) { |
| | | mDialog.show(); |
| | | } |
| | | } |
| | | |
| | | /** |
| | | * 取消进度框 |
| | | */ |
| | | private void dismissProgress() { |
| | | if (!isShowProgress) { |
| | | return; |
| | | } |
| | | if (mDialog != null && mDialog.isShowing()) { |
| | | mDialog.dismiss(); |
| | | } |
| | | } |
| | | |
| | | @Override |
| | | public void onStart() { |
| | | showProgress(); |
| | | } |
| | | |
| | | @Override |
| | | public void onCompleted() { |
| | | dismissProgress(); |
| | | } |
| | | |
| | | public abstract void onResponse(OriginReadData data); |
| | | |
| | | @Override |
| | | public void onError(Exception e) { |
| | | onCompleted(); |
| | | } |
| | | |
| | | @Override |
| | | public void onCancelProgress() { |
| | | onCompleted(); |
| | | onError(new RequestCancelException("网络请求被取消")); |
| | | } |
| | | |
| | | } |
| New file |
| | |
| | | package com.easysocket.callback; |
| | | |
| | | |
| | | /** |
| | | * Created by LXR ON 2018/8/29. |
| | | */ |
| | | public abstract class SimpleCallBack extends SuperCallBack{ |
| | | |
| | | |
| | | public SimpleCallBack(String callbackId) { |
| | | super(callbackId); |
| | | } |
| | | |
| | | @Override |
| | | public void onStart() { |
| | | } |
| | | |
| | | @Override |
| | | public void onCompleted() { |
| | | |
| | | } |
| | | |
| | | @Override |
| | | public void onError(Exception e) { |
| | | |
| | | } |
| | | |
| | | |
| | | } |
| New file |
| | |
| | | package com.easysocket.callback; |
| | | |
| | | |
| | | import com.easysocket.entity.OriginReadData; |
| | | |
| | | /** |
| | | * Created by LXR ON 2018/8/29. |
| | | */ |
| | | public abstract class SuperCallBack { |
| | | /** |
| | | * 随机字符串,识别服务端应答消息的唯一标识 |
| | | */ |
| | | private String callbackId; |
| | | |
| | | /** |
| | | * @param callbackId 识别服务端应答消息的唯一标识 |
| | | */ |
| | | public SuperCallBack(String callbackId) { |
| | | this.callbackId = callbackId; |
| | | } |
| | | |
| | | /** |
| | | * 获取回调ID |
| | | * |
| | | * @return |
| | | */ |
| | | public String getCallbackId() { |
| | | return callbackId; |
| | | } |
| | | |
| | | public abstract void onStart(); |
| | | |
| | | public abstract void onCompleted(); |
| | | |
| | | public abstract void onError(Exception e); |
| | | |
| | | public void onSuccess(OriginReadData data) { |
| | | onCompleted(); |
| | | onResponse(data); |
| | | } |
| | | |
| | | public abstract void onResponse(OriginReadData data); |
| | | |
| | | } |
| New file |
| | |
| | | package com.easysocket.config; |
| | | |
| | | import com.easysocket.entity.OriginReadData; |
| | | |
| | | /** |
| | | * Author:Alex |
| | | * Date:2019/6/4 |
| | | * Note:要想实现EasySocket的回调功能,必须实现此工厂类,callbackID作为回调消息的唯一标识 |
| | | */ |
| | | public abstract class CallbackIDFactory { |
| | | /** |
| | | * 返回callbackID |
| | | * |
| | | * @param |
| | | * @return 如果没有callbackID请返回null |
| | | */ |
| | | public abstract String getCallbackID(OriginReadData data); |
| | | } |
| New file |
| | |
| | | package com.easysocket.config; |
| | | |
| | | import com.easysocket.interfaces.config.IMessageProtocol; |
| | | |
| | | import java.nio.ByteBuffer; |
| | | import java.nio.ByteOrder; |
| | | |
| | | /** |
| | | * Author:Alex |
| | | * Date:2019/5/31 |
| | | * Note:默认的消息协议,header为4个字节,保存消息体 body的长度 |
| | | */ |
| | | public class DefaultMessageProtocol implements IMessageProtocol { |
| | | @Override |
| | | public int getHeaderLength() { |
| | | return 4; // 包头长度,用来保存body的长度值 |
| | | } |
| | | |
| | | @Override |
| | | public int getBodyLength(byte[] header, ByteOrder byteOrder) { |
| | | if (header == null || header.length < getHeaderLength()) { |
| | | return 0; |
| | | } |
| | | ByteBuffer bb = ByteBuffer.wrap(header); |
| | | bb.order(byteOrder); |
| | | return bb.getInt(); // body的长度以int的形式保存在 header |
| | | } |
| | | } |
| New file |
| | |
| | | package com.easysocket.config; |
| | | |
| | | import java.security.cert.CertificateException; |
| | | import java.security.cert.X509Certificate; |
| | | |
| | | import javax.net.ssl.X509TrustManager; |
| | | |
| | | /** |
| | | * Author:Alex |
| | | * Date:2019/6/3 |
| | | * Note: |
| | | */ |
| | | public class DefaultX509ProtocolTrustManager implements X509TrustManager { |
| | | @Override |
| | | public void checkClientTrusted(X509Certificate[] chain, String authType) throws CertificateException { |
| | | |
| | | } |
| | | |
| | | @Override |
| | | public void checkServerTrusted(X509Certificate[] chain, String authType) throws CertificateException { |
| | | |
| | | } |
| | | |
| | | @Override |
| | | public X509Certificate[] getAcceptedIssuers() { |
| | | return new X509Certificate[0]; |
| | | } |
| | | } |
| New file |
| | |
| | | package com.easysocket.config; |
| | | |
| | | import com.easysocket.connection.reconnect.AbsReconnection; |
| | | import com.easysocket.connection.reconnect.DefaultReConnection; |
| | | import com.easysocket.entity.SocketAddress; |
| | | import com.easysocket.interfaces.config.IMessageProtocol; |
| | | |
| | | import java.nio.ByteOrder; |
| | | |
| | | /** |
| | | * Author:Alex。 |
| | | * Date:2019/5/31。 |
| | | * Note:socket相关配置。 |
| | | */ |
| | | public class EasySocketOptions { |
| | | |
| | | /** |
| | | * 是否调试模式 |
| | | */ |
| | | private static boolean isDebug = true; |
| | | /** |
| | | * 主机地址 |
| | | */ |
| | | private SocketAddress socketAddress; |
| | | /** |
| | | * 备用主机地址 |
| | | */ |
| | | private SocketAddress backupAddress; |
| | | /** |
| | | * 写入Socket管道的字节序 |
| | | */ |
| | | private ByteOrder writeOrder; |
| | | /** |
| | | * 从Socket读取字节时的字节序 |
| | | */ |
| | | private ByteOrder readOrder; |
| | | /** |
| | | * 从socket读取数据时遵从的数据包结构协议,在业务层进行定义 |
| | | */ |
| | | private IMessageProtocol messageProtocol; |
| | | /** |
| | | * 写数据时单个数据包的最大值 |
| | | */ |
| | | private int maxWriteBytes; |
| | | /** |
| | | * 读数据时单次读取最大缓存值,数值越大效率越高,但是系统消耗也越大 |
| | | */ |
| | | private int maxReadBytes; |
| | | /** |
| | | * 心跳频率/毫秒 |
| | | */ |
| | | private long heartbeatFreq; |
| | | /** |
| | | * 心跳最大的丢失次数,大于这个数据,将断开socket连接 |
| | | */ |
| | | private int maxHeartbeatLoseTimes; |
| | | /** |
| | | * 连接超时时间(毫秒) |
| | | */ |
| | | private int connectTimeout; |
| | | /** |
| | | * 服务器返回数据的最大值(单位Mb),防止客户端内存溢出 |
| | | */ |
| | | private int maxResponseDataMb; |
| | | /** |
| | | * socket重连管理器 |
| | | */ |
| | | private AbsReconnection reconnectionManager; |
| | | /** |
| | | * 安全套接字相关配置 |
| | | */ |
| | | private SocketSSLConfig easySSLConfig; |
| | | /** |
| | | * socket工厂 |
| | | */ |
| | | private SocketFactory socketFactory; |
| | | /** |
| | | * 实现回调功能需要callbackID,而callbackID是保存在发送消息和应答消息中的,此工厂用来获取socket消息中 |
| | | * 保存callbackID值的key,比如json格式中的key-value中的key |
| | | */ |
| | | private CallbackIDFactory callbackIDFactory; |
| | | /** |
| | | * 请求超时时间,单位毫秒 |
| | | */ |
| | | private long requestTimeout; |
| | | /** |
| | | * 是否开启请求超时检测 |
| | | */ |
| | | private boolean isOpenRequestTimeout; |
| | | |
| | | /** |
| | | * IO字符流的编码方式,默认utf-8 |
| | | */ |
| | | private String charsetName; |
| | | |
| | | public boolean isDebug() { |
| | | return isDebug; |
| | | } |
| | | |
| | | |
| | | /** |
| | | * 静态内部类 |
| | | */ |
| | | public static class Builder { |
| | | EasySocketOptions socketOptions; |
| | | |
| | | // 首先获得一个默认的配置 |
| | | public Builder() { |
| | | this(getDefaultOptions()); |
| | | } |
| | | |
| | | public Builder(EasySocketOptions defaultOptions) { |
| | | socketOptions = defaultOptions; |
| | | } |
| | | |
| | | /** |
| | | * 设置socket 主机地址 |
| | | * |
| | | * @param socketAddress |
| | | * @return |
| | | */ |
| | | public Builder setSocketAddress(SocketAddress socketAddress) { |
| | | socketOptions.socketAddress = socketAddress; |
| | | return this; |
| | | } |
| | | |
| | | /** |
| | | * 设置备用的主机地址 |
| | | * |
| | | * @param backupAddress |
| | | * @return |
| | | */ |
| | | public Builder setBackupAddress(SocketAddress backupAddress) { |
| | | socketOptions.backupAddress = backupAddress; |
| | | return this; |
| | | } |
| | | |
| | | /** |
| | | * 设置是否开启请求超时的检测 |
| | | * |
| | | * @param openRequestTimeout |
| | | * @return |
| | | */ |
| | | public Builder setOpenRequestTimeout(boolean openRequestTimeout) { |
| | | socketOptions.isOpenRequestTimeout = openRequestTimeout; |
| | | return this; |
| | | } |
| | | |
| | | /** |
| | | * 设置请求超时时间 |
| | | * |
| | | * @param requestTimeout 毫秒 |
| | | * @return |
| | | */ |
| | | public Builder setRequestTimeout(long requestTimeout) { |
| | | socketOptions.requestTimeout = requestTimeout; |
| | | return this; |
| | | } |
| | | |
| | | /** |
| | | * 设置请求ack的工厂 |
| | | * |
| | | * @param callbackIDFactory |
| | | */ |
| | | public Builder setCallbackIDFactory(CallbackIDFactory callbackIDFactory) { |
| | | socketOptions.callbackIDFactory = callbackIDFactory; |
| | | return this; |
| | | } |
| | | |
| | | |
| | | /** |
| | | * 设置写数据的字节顺序 |
| | | * |
| | | * @param writeOrder |
| | | * @return |
| | | */ |
| | | public Builder setWriteOrder(ByteOrder writeOrder) { |
| | | socketOptions.writeOrder = writeOrder; |
| | | return this; |
| | | } |
| | | |
| | | /** |
| | | * 设置读数据的字节顺序 |
| | | * |
| | | * @param readOrder |
| | | * @return |
| | | */ |
| | | public Builder setReadOrder(ByteOrder readOrder) { |
| | | socketOptions.readOrder = readOrder; |
| | | return this; |
| | | } |
| | | |
| | | /** |
| | | * 设置读取数据的数据结构协议 |
| | | * |
| | | * @param readerProtocol |
| | | * @return |
| | | */ |
| | | public Builder setReaderProtocol(IMessageProtocol readerProtocol) { |
| | | socketOptions.messageProtocol = readerProtocol; |
| | | return this; |
| | | } |
| | | |
| | | /** |
| | | * 设置写数据时单个数据包的最大值 |
| | | * |
| | | * @param maxWriteBytes |
| | | * @return |
| | | */ |
| | | public Builder setMaxWriteBytes(int maxWriteBytes) { |
| | | socketOptions.maxWriteBytes = maxWriteBytes; |
| | | return this; |
| | | } |
| | | |
| | | /** |
| | | * 设置读数据时单次读取的最大缓存值 |
| | | * |
| | | * @param maxReadBytes |
| | | * @return |
| | | */ |
| | | public Builder setMaxReadBytes(int maxReadBytes) { |
| | | socketOptions.maxReadBytes = maxReadBytes; |
| | | return this; |
| | | } |
| | | |
| | | /** |
| | | * 设置心跳发送频率,单位毫秒 |
| | | * |
| | | * @param heartbeatFreq |
| | | * @return |
| | | */ |
| | | public Builder setHeartbeatFreq(long heartbeatFreq) { |
| | | socketOptions.heartbeatFreq = heartbeatFreq; |
| | | return this; |
| | | } |
| | | |
| | | /** |
| | | * 设置心跳丢失的最大允许数,如果超过这个最大数就断开socket连接 |
| | | * |
| | | * @param maxHeartbeatLoseTimes |
| | | * @return |
| | | */ |
| | | public Builder setMaxHeartbeatLoseTimes(int maxHeartbeatLoseTimes) { |
| | | socketOptions.maxHeartbeatLoseTimes = maxHeartbeatLoseTimes; |
| | | return this; |
| | | } |
| | | |
| | | /** |
| | | * 设置连接超时时间 |
| | | * |
| | | * @param connectTimeout |
| | | * @return |
| | | */ |
| | | public Builder setConnectTimeout(int connectTimeout) { |
| | | socketOptions.connectTimeout = connectTimeout; |
| | | return this; |
| | | } |
| | | |
| | | /** |
| | | * 设置服务器返回数据的允许的最大值,单位兆 |
| | | * |
| | | * @param maxResponseDataMb |
| | | * @return |
| | | */ |
| | | public Builder setMaxResponseDataMb(int maxResponseDataMb) { |
| | | socketOptions.maxResponseDataMb = maxResponseDataMb; |
| | | return this; |
| | | } |
| | | |
| | | /** |
| | | * 设置重连管理器 |
| | | * |
| | | * @param reconnectionManager |
| | | * @return |
| | | */ |
| | | public Builder setReconnectionManager(AbsReconnection reconnectionManager) { |
| | | socketOptions.reconnectionManager = reconnectionManager; |
| | | return this; |
| | | } |
| | | |
| | | /** |
| | | * 安全套接字的配置 |
| | | * |
| | | * @param easySSLConfig |
| | | * @return |
| | | */ |
| | | public Builder setEasySSLConfig(SocketSSLConfig easySSLConfig) { |
| | | socketOptions.easySSLConfig = easySSLConfig; |
| | | return this; |
| | | } |
| | | |
| | | /** |
| | | * 自定义创建socket工厂 |
| | | * |
| | | * @param socketFactory |
| | | * @return |
| | | */ |
| | | public Builder setSocketFactory(SocketFactory socketFactory) { |
| | | socketOptions.socketFactory = socketFactory; |
| | | return this; |
| | | } |
| | | |
| | | public Builder setCharsetName(String charsetName) { |
| | | socketOptions.charsetName = charsetName; |
| | | return this; |
| | | } |
| | | |
| | | public EasySocketOptions build() { |
| | | return socketOptions; |
| | | } |
| | | } |
| | | |
| | | |
| | | /** |
| | | * 获取默认的配置 |
| | | * |
| | | * @return |
| | | */ |
| | | public static EasySocketOptions getDefaultOptions() { |
| | | EasySocketOptions options = new EasySocketOptions(); |
| | | options.socketAddress = null; |
| | | options.backupAddress = null; |
| | | options.heartbeatFreq = 5 * 1000; |
| | | options.messageProtocol = null; |
| | | options.maxResponseDataMb = 5; |
| | | options.connectTimeout = 5 * 1000; // 连接超时默认5秒 |
| | | options.maxWriteBytes = 100; |
| | | options.maxReadBytes = 50; |
| | | options.readOrder = ByteOrder.BIG_ENDIAN; |
| | | options.writeOrder = ByteOrder.BIG_ENDIAN; |
| | | options.maxHeartbeatLoseTimes = 5; |
| | | options.reconnectionManager = new DefaultReConnection(); |
| | | options.easySSLConfig = null; |
| | | options.socketFactory = null; |
| | | options.callbackIDFactory = null; |
| | | options.requestTimeout = 10 * 1000; // 默认十秒 |
| | | options.isOpenRequestTimeout = true; // 默认开启 |
| | | options.charsetName = "UTF-8"; |
| | | return options; |
| | | } |
| | | |
| | | public String getCharsetName() { |
| | | return charsetName; |
| | | } |
| | | |
| | | public ByteOrder getWriteOrder() { |
| | | return writeOrder; |
| | | } |
| | | |
| | | public ByteOrder getReadOrder() { |
| | | return readOrder; |
| | | } |
| | | |
| | | public IMessageProtocol getMessageProtocol() { |
| | | return messageProtocol; |
| | | } |
| | | |
| | | public int getMaxWriteBytes() { |
| | | return maxWriteBytes; |
| | | } |
| | | |
| | | public int getMaxReadBytes() { |
| | | return maxReadBytes; |
| | | } |
| | | |
| | | public long getHeartbeatFreq() { |
| | | return heartbeatFreq; |
| | | } |
| | | |
| | | public int getMaxHeartbeatLoseTimes() { |
| | | return maxHeartbeatLoseTimes; |
| | | } |
| | | |
| | | public int getConnectTimeout() { |
| | | return connectTimeout; |
| | | } |
| | | |
| | | public int getMaxResponseDataMb() { |
| | | return maxResponseDataMb; |
| | | } |
| | | |
| | | public AbsReconnection getReconnectionManager() { |
| | | return reconnectionManager; |
| | | } |
| | | |
| | | public SocketSSLConfig getEasySSLConfig() { |
| | | return easySSLConfig; |
| | | } |
| | | |
| | | public SocketFactory getSocketFactory() { |
| | | return socketFactory; |
| | | } |
| | | |
| | | public long getRequestTimeout() { |
| | | return requestTimeout; |
| | | } |
| | | |
| | | public boolean isOpenRequestTimeout() { |
| | | return isOpenRequestTimeout; |
| | | } |
| | | |
| | | public CallbackIDFactory getCallbackIDFactory() { |
| | | return callbackIDFactory; |
| | | } |
| | | |
| | | public static void setIsDebug(boolean isDebug) { |
| | | EasySocketOptions.isDebug = isDebug; |
| | | } |
| | | |
| | | public void setWriteOrder(ByteOrder writeOrder) { |
| | | this.writeOrder = writeOrder; |
| | | } |
| | | |
| | | public void setReadOrder(ByteOrder readOrder) { |
| | | this.readOrder = readOrder; |
| | | } |
| | | |
| | | public void setMessageProtocol(IMessageProtocol messageProtocol) { |
| | | this.messageProtocol = messageProtocol; |
| | | } |
| | | |
| | | public void setMaxWriteBytes(int maxWriteBytes) { |
| | | this.maxWriteBytes = maxWriteBytes; |
| | | } |
| | | |
| | | public void setMaxReadBytes(int maxReadBytes) { |
| | | this.maxReadBytes = maxReadBytes; |
| | | } |
| | | |
| | | public void setHeartbeatFreq(long heartbeatFreq) { |
| | | this.heartbeatFreq = heartbeatFreq; |
| | | } |
| | | |
| | | public void setMaxHeartbeatLoseTimes(int maxHeartbeatLoseTimes) { |
| | | this.maxHeartbeatLoseTimes = maxHeartbeatLoseTimes; |
| | | } |
| | | |
| | | public void setConnectTimeout(int connectTimeout) { |
| | | this.connectTimeout = connectTimeout; |
| | | } |
| | | |
| | | public void setMaxResponseDataMb(int maxResponseDataMb) { |
| | | this.maxResponseDataMb = maxResponseDataMb; |
| | | } |
| | | |
| | | public void setReconnectionManager(AbsReconnection reconnectionManager) { |
| | | this.reconnectionManager = reconnectionManager; |
| | | } |
| | | |
| | | public void setEasySSLConfig(SocketSSLConfig easySSLConfig) { |
| | | this.easySSLConfig = easySSLConfig; |
| | | } |
| | | |
| | | public void setSocketFactory(SocketFactory socketFactory) { |
| | | this.socketFactory = socketFactory; |
| | | } |
| | | |
| | | public void setCallbackIDFactory(CallbackIDFactory callbackIDFactory) { |
| | | this.callbackIDFactory = callbackIDFactory; |
| | | } |
| | | |
| | | public void setRequestTimeout(long requestTimeout) { |
| | | this.requestTimeout = requestTimeout; |
| | | } |
| | | |
| | | public void setOpenRequestTimeout(boolean openRequestTimeout) { |
| | | isOpenRequestTimeout = openRequestTimeout; |
| | | } |
| | | |
| | | public SocketAddress getSocketAddress() { |
| | | return socketAddress; |
| | | } |
| | | |
| | | public SocketAddress getBackupAddress() { |
| | | return backupAddress; |
| | | } |
| | | |
| | | } |
| New file |
| | |
| | | package com.easysocket.config; |
| | | |
| | | import com.easysocket.entity.SocketAddress; |
| | | |
| | | import java.net.Socket; |
| | | |
| | | /** |
| | | * Author:Alex |
| | | * Date:2019/5/31 |
| | | * Note:socket工厂 |
| | | */ |
| | | public abstract class SocketFactory { |
| | | public abstract Socket createSocket(SocketAddress info, EasySocketOptions options) throws Exception; |
| | | } |
| New file |
| | |
| | | package com.easysocket.config; |
| | | |
| | | import javax.net.ssl.KeyManager; |
| | | import javax.net.ssl.SSLSocketFactory; |
| | | import javax.net.ssl.TrustManager; |
| | | |
| | | /** |
| | | * socket的ssl配置 |
| | | */ |
| | | |
| | | public class SocketSSLConfig { |
| | | /** |
| | | * 安全协议名称(缺省为SSL) |
| | | */ |
| | | private String mProtocol; |
| | | /** |
| | | * 信任证书管理器(缺省为X509) |
| | | */ |
| | | private TrustManager[] mTrustManagers; |
| | | /** |
| | | * 证书秘钥管理器(缺省为null) |
| | | */ |
| | | private KeyManager[] mKeyManagers; |
| | | /** |
| | | * 自定义SSLFactory(缺省为null) |
| | | */ |
| | | private SSLSocketFactory mCustomSSLFactory; |
| | | |
| | | private SocketSSLConfig() { |
| | | |
| | | } |
| | | |
| | | public static class Builder { |
| | | |
| | | private SocketSSLConfig mConfig; |
| | | |
| | | public Builder() { |
| | | mConfig = new SocketSSLConfig(); |
| | | } |
| | | |
| | | public Builder setProtocol(String protocol) { |
| | | mConfig.mProtocol = protocol; |
| | | return this; |
| | | } |
| | | |
| | | public Builder setTrustManagers(TrustManager[] trustManagers) { |
| | | mConfig.mTrustManagers = trustManagers; |
| | | return this; |
| | | } |
| | | |
| | | public Builder setKeyManagers(KeyManager[] keyManagers) { |
| | | mConfig.mKeyManagers = keyManagers; |
| | | return this; |
| | | } |
| | | |
| | | public Builder setCustomSSLFactory(SSLSocketFactory customSSLFactory) { |
| | | mConfig.mCustomSSLFactory = customSSLFactory; |
| | | return this; |
| | | } |
| | | |
| | | public SocketSSLConfig build() { |
| | | return mConfig; |
| | | } |
| | | } |
| | | |
| | | public KeyManager[] getKeyManagers() { |
| | | return mKeyManagers; |
| | | } |
| | | |
| | | public String getProtocol() { |
| | | return mProtocol; |
| | | } |
| | | |
| | | public TrustManager[] getTrustManagers() { |
| | | return mTrustManagers; |
| | | } |
| | | |
| | | public SSLSocketFactory getCustomSSLFactory() { |
| | | return mCustomSSLFactory; |
| | | } |
| | | } |
| New file |
| | |
| | | package com.easysocket.connection.action; |
| | | |
| | | /** |
| | | * Author:Alex |
| | | * Date:2019/6/3 |
| | | * Note: |
| | | */ |
| | | public interface IOAction { |
| | | // 收到消息响应 |
| | | String ACTION_READ_COMPLETE = "action_read_complete"; |
| | | } |
| New file |
| | |
| | | package com.easysocket.connection.action; |
| | | |
| | | /** |
| | | * Author:Alex |
| | | * Date:2019/6/1 |
| | | * Note: |
| | | */ |
| | | public interface SocketAction { |
| | | // 连接成功 |
| | | String ACTION_CONN_SUCCESS="action_conn_success"; |
| | | // 连接失败 |
| | | String ACTION_CONN_FAIL="action_conn_fail"; |
| | | // 断开连接 |
| | | String ACTION_DISCONNECTION="action_disconnection"; |
| | | } |
| New file |
| | |
| | | package com.easysocket.connection.action; |
| | | |
| | | /** |
| | | * Author:Alex |
| | | * Date:2019/6/1 |
| | | * Note:连接状态 |
| | | */ |
| | | public interface SocketStatus { |
| | | // 已断开连接 |
| | | int SOCKET_DISCONNECTED = 0; |
| | | // 正在连接 |
| | | int SOCKET_CONNECTING = 1; |
| | | // 已连接 |
| | | int SOCKET_CONNECTED = 2; |
| | | // 正在断开连接 |
| | | int SOCKET_DISCONNECTING =3; |
| | | } |
| New file |
| | |
| | | package com.easysocket.connection.connect; |
| | | |
| | | import android.os.Handler; |
| | | |
| | | import com.easysocket.EasySocket; |
| | | import com.easysocket.callback.SuperCallBack; |
| | | import com.easysocket.config.EasySocketOptions; |
| | | import com.easysocket.connection.action.SocketAction; |
| | | import com.easysocket.connection.action.SocketStatus; |
| | | import com.easysocket.connection.dispatcher.CallbackResponseDispatcher; |
| | | import com.easysocket.connection.dispatcher.SocketActionDispatcher; |
| | | import com.easysocket.connection.heartbeat.HeartManager; |
| | | import com.easysocket.connection.iowork.IOManager; |
| | | import com.easysocket.connection.reconnect.AbsReconnection; |
| | | import com.easysocket.entity.SocketAddress; |
| | | import com.easysocket.entity.basemsg.SuperCallbackSender; |
| | | import com.easysocket.exception.NotNullException; |
| | | import com.easysocket.interfaces.config.IConnectionSwitchListener; |
| | | import com.easysocket.interfaces.conn.IConnectionManager; |
| | | import com.easysocket.interfaces.conn.ISocketActionListener; |
| | | import com.easysocket.utils.LogUtil; |
| | | import com.easysocket.utils.Utils; |
| | | |
| | | import java.io.IOException; |
| | | import java.util.concurrent.ExecutorService; |
| | | import java.util.concurrent.Executors; |
| | | import java.util.concurrent.atomic.AtomicInteger; |
| | | |
| | | /** |
| | | * Author:Alex |
| | | * Date:2019/5/29 |
| | | * Note:socket连接的超类 |
| | | */ |
| | | public abstract class SuperConnection implements IConnectionManager { |
| | | |
| | | /** |
| | | * 连接状态,初始值为断开连接 |
| | | */ |
| | | protected final AtomicInteger connectionStatus = new AtomicInteger(SocketStatus.SOCKET_DISCONNECTED); |
| | | /** |
| | | * 连接线程 |
| | | */ |
| | | private ExecutorService connExecutor; |
| | | /** |
| | | * socket地址信息 |
| | | */ |
| | | protected SocketAddress socketAddress; |
| | | /** |
| | | * socket行为分发器 |
| | | */ |
| | | private SocketActionDispatcher actionDispatcher; |
| | | /** |
| | | * 重连管理器 |
| | | */ |
| | | private AbsReconnection reconnection; |
| | | /** |
| | | * io管理器 |
| | | */ |
| | | private IOManager ioManager; |
| | | /** |
| | | * 心跳管理器 |
| | | */ |
| | | private HeartManager heartManager; |
| | | /** |
| | | * 配置信息 |
| | | */ |
| | | protected EasySocketOptions socketOptions; |
| | | /** |
| | | * socket回调消息的分发器 |
| | | */ |
| | | private CallbackResponseDispatcher callbackResponseDispatcher; |
| | | /** |
| | | * 连接切换的监听 |
| | | */ |
| | | private IConnectionSwitchListener connectionSwitchListener; |
| | | |
| | | public SuperConnection(SocketAddress socketAddress) { |
| | | this.socketAddress = socketAddress; |
| | | actionDispatcher = new SocketActionDispatcher(this, socketAddress); |
| | | } |
| | | |
| | | @Override |
| | | public void subscribeSocketAction(ISocketActionListener iSocketActionListener) { |
| | | actionDispatcher.subscribe(iSocketActionListener); |
| | | } |
| | | |
| | | @Override |
| | | public void unSubscribeSocketAction(ISocketActionListener iSocketActionListener) { |
| | | actionDispatcher.unsubscribe(iSocketActionListener); |
| | | } |
| | | |
| | | @Override |
| | | public synchronized IConnectionManager setOptions(EasySocketOptions socketOptions) { |
| | | if (socketOptions == null) return this; |
| | | |
| | | this.socketOptions = socketOptions; |
| | | |
| | | if (ioManager != null) |
| | | ioManager.setOptions(socketOptions); |
| | | |
| | | if (heartManager != null) |
| | | heartManager.setOptions(socketOptions); |
| | | |
| | | if (callbackResponseDispatcher != null) |
| | | callbackResponseDispatcher.setSocketOptions(socketOptions); |
| | | |
| | | // 更改了重连器 |
| | | if (reconnection != null && !reconnection.equals(socketOptions.getReconnectionManager())) { |
| | | reconnection.detach(); |
| | | reconnection = socketOptions.getReconnectionManager(); |
| | | reconnection.attach(this); |
| | | } |
| | | return this; |
| | | } |
| | | |
| | | @Override |
| | | public EasySocketOptions getOptions() { |
| | | return socketOptions; |
| | | } |
| | | |
| | | @Override |
| | | public synchronized void connect() { |
| | | |
| | | if (connectionStatus.get() == SocketStatus.SOCKET_DISCONNECTING) { |
| | | new Handler().postDelayed(new Runnable() { |
| | | @Override |
| | | public void run() { |
| | | LogUtil.d("---> socket>>connect>>正在断开连接,延时一秒執行重连"); |
| | | connect(); |
| | | } |
| | | }, 1000); // 延时1秒 |
| | | return; |
| | | } |
| | | LogUtil.d("---> socket开始连接"); |
| | | if (socketAddress.getIp() == null) { |
| | | throw new NotNullException("请检查是否设置了IP地址"); |
| | | } |
| | | // 正在连接 |
| | | connectionStatus.set(SocketStatus.SOCKET_CONNECTING); |
| | | |
| | | // 心跳管理器 |
| | | if (heartManager == null) { |
| | | heartManager = new HeartManager(this, actionDispatcher); |
| | | } |
| | | |
| | | // 重连管理器 |
| | | if (reconnection != null) { |
| | | reconnection.detach(); |
| | | } |
| | | reconnection = socketOptions.getReconnectionManager(); |
| | | if (reconnection != null) { |
| | | reconnection.attach(this); |
| | | } |
| | | |
| | | // 开启分发消息线程 |
| | | if (actionDispatcher != null) { |
| | | actionDispatcher.startDispatchThread(); |
| | | } |
| | | // 开启连接线程 |
| | | if (connExecutor == null || connExecutor.isShutdown()) { |
| | | // 核心线程数为0,非核心线程数可以有Integer.MAX_VALUE个,存活时间为60秒,适合于在不断进行连接的情况下,避免重复创建和销毁线程 |
| | | connExecutor = Executors.newCachedThreadPool(); |
| | | // 执行连接任务 |
| | | } |
| | | connExecutor.execute(connTask); |
| | | } |
| | | |
| | | @Override |
| | | public synchronized void disconnect(boolean isNeedReconnect) { |
| | | // 判断当前socket的连接状态 |
| | | if (connectionStatus.get() == SocketStatus.SOCKET_DISCONNECTED) { |
| | | return; |
| | | } |
| | | // 正在重连中 |
| | | if (isNeedReconnect && reconnection.isReconning()) { |
| | | return; |
| | | } |
| | | // 正在断开连接 |
| | | connectionStatus.set(SocketStatus.SOCKET_DISCONNECTING); |
| | | |
| | | // 开启断开连接线程 |
| | | String info = socketAddress.getIp() + " : " + socketAddress.getPort(); |
| | | Thread disconnThread = new DisconnectThread(isNeedReconnect, "disconn thread:" + info); |
| | | disconnThread.setDaemon(true); |
| | | disconnThread.start(); |
| | | } |
| | | |
| | | /** |
| | | * 断开连接线程 |
| | | */ |
| | | private class DisconnectThread extends Thread { |
| | | boolean isNeedReconnect; // 当前连接的断开是否需要自动重连 |
| | | |
| | | public DisconnectThread(boolean isNeedReconnect, String name) { |
| | | super(name); |
| | | this.isNeedReconnect = isNeedReconnect; |
| | | } |
| | | |
| | | @Override |
| | | public void run() { |
| | | try { |
| | | // 关闭io线程 |
| | | if (ioManager != null) |
| | | ioManager.closeIO(); |
| | | // 关闭回调分发器线程 |
| | | if (callbackResponseDispatcher != null) |
| | | callbackResponseDispatcher.shutdownThread(); |
| | | // 关闭连接线程 |
| | | if (connExecutor != null && !connExecutor.isShutdown()) { |
| | | connExecutor.shutdown(); |
| | | connExecutor = null; |
| | | } |
| | | // 关闭连接 |
| | | closeConnection(); |
| | | actionDispatcher.dispatchAction(SocketAction.ACTION_DISCONNECTION, new Boolean(isNeedReconnect)); |
| | | if (!isNeedReconnect){ |
| | | heartManager.stopHeartbeat(); |
| | | } |
| | | LogUtil.d("---> 关闭socket连接"); |
| | | connectionStatus.set(SocketStatus.SOCKET_DISCONNECTED); |
| | | } catch (IOException e) { |
| | | // 断开连接发生异常 |
| | | e.printStackTrace(); |
| | | } |
| | | } |
| | | } |
| | | |
| | | // 连接任务 |
| | | private Runnable connTask = new Runnable() { |
| | | @Override |
| | | public void run() { |
| | | try { |
| | | openConnection(); |
| | | } catch (Exception e) { |
| | | // 连接异常 |
| | | e.printStackTrace(); |
| | | LogUtil.d("---> socket连接失败"); |
| | | connectionStatus.set(SocketStatus.SOCKET_DISCONNECTED); |
| | | // 第二个参数指需要重连 |
| | | actionDispatcher.dispatchAction(SocketAction.ACTION_CONN_FAIL, new Boolean(true)); |
| | | |
| | | } |
| | | } |
| | | }; |
| | | |
| | | /** |
| | | * 连接成功 |
| | | */ |
| | | protected void onConnectionOpened() { |
| | | LogUtil.d("---> socket连接成功"); |
| | | openSocketManager(); |
| | | connectionStatus.set(SocketStatus.SOCKET_CONNECTED); |
| | | actionDispatcher.dispatchAction(SocketAction.ACTION_CONN_SUCCESS); |
| | | } |
| | | |
| | | // 开启socket相关管理器 |
| | | private void openSocketManager() { |
| | | if (callbackResponseDispatcher == null) |
| | | callbackResponseDispatcher = new CallbackResponseDispatcher(this); |
| | | if (ioManager == null) { |
| | | ioManager = new IOManager(this, actionDispatcher); |
| | | } |
| | | ioManager.startIO(); |
| | | |
| | | // 启动相关线程 |
| | | callbackResponseDispatcher.engineThread(); |
| | | ioManager.startIO(); |
| | | } |
| | | |
| | | // 切换了主机IP和端口 |
| | | @Override |
| | | public synchronized void switchHost(SocketAddress socketAddress) { |
| | | if (socketAddress != null) { |
| | | SocketAddress oldAddress = this.socketAddress; |
| | | this.socketAddress = socketAddress; |
| | | |
| | | if (actionDispatcher != null) |
| | | actionDispatcher.setSocketAddress(socketAddress); |
| | | // 切换主机 |
| | | if (connectionSwitchListener != null) { |
| | | connectionSwitchListener.onSwitchConnectionInfo(this, oldAddress, socketAddress); |
| | | } |
| | | } |
| | | |
| | | } |
| | | |
| | | public void setOnConnectionSwitchListener(IConnectionSwitchListener listener) { |
| | | connectionSwitchListener = listener; |
| | | } |
| | | |
| | | @Override |
| | | public boolean isConnectViable() { |
| | | // 当前socket是否处于可连接状态 |
| | | return Utils.isNetConnected(EasySocket.getInstance().getContext()) && connectionStatus.get() == SocketStatus.SOCKET_DISCONNECTED; |
| | | } |
| | | |
| | | @Override |
| | | public int getConnectionStatus() { |
| | | return connectionStatus.get(); |
| | | } |
| | | |
| | | /** |
| | | * 打开连接 |
| | | * |
| | | * @throws IOException |
| | | */ |
| | | protected abstract void openConnection() throws Exception; |
| | | |
| | | /** |
| | | * 关闭连接 |
| | | * |
| | | * @throws IOException |
| | | */ |
| | | protected abstract void closeConnection() throws IOException; |
| | | |
| | | /** |
| | | * 发送bytes数据 |
| | | * |
| | | * @param bytes |
| | | * @return |
| | | */ |
| | | private IConnectionManager sendBytes(byte[] bytes) { |
| | | if (ioManager == null || connectionStatus.get() != SocketStatus.SOCKET_CONNECTED) { |
| | | LogUtil.w("sendBytes错误-----ioManager为null或者connectionStatus状态不为已连接"); |
| | | return this; |
| | | } |
| | | ioManager.sendBytes(bytes); |
| | | return this; |
| | | } |
| | | |
| | | @Override |
| | | public void onCallBack(SuperCallBack callBack) { |
| | | callbackResponseDispatcher.addSocketCallback(callBack); |
| | | } |
| | | |
| | | |
| | | @Override |
| | | public synchronized IConnectionManager upBytes(byte[] bytes) { |
| | | sendBytes(bytes); |
| | | return this; |
| | | } |
| | | |
| | | @Override |
| | | public synchronized IConnectionManager upCallbackMessage(SuperCallbackSender sender) { |
| | | callbackResponseDispatcher.checkCallbackSender(sender); |
| | | // 发送 |
| | | sendBytes(sender.pack()); |
| | | return this; |
| | | } |
| | | |
| | | |
| | | @Override |
| | | public HeartManager getHeartManager() { |
| | | return heartManager; |
| | | } |
| | | } |
| New file |
| | |
| | | package com.easysocket.connection.connect; |
| | | |
| | | import com.easysocket.config.DefaultX509ProtocolTrustManager; |
| | | import com.easysocket.config.SocketSSLConfig; |
| | | import com.easysocket.connection.action.SocketStatus; |
| | | import com.easysocket.entity.SocketAddress; |
| | | import com.easysocket.utils.LogUtil; |
| | | import com.easysocket.utils.Utils; |
| | | |
| | | import java.io.IOException; |
| | | import java.io.InputStream; |
| | | import java.io.OutputStream; |
| | | import java.net.InetSocketAddress; |
| | | import java.net.Socket; |
| | | import java.security.SecureRandom; |
| | | |
| | | import javax.net.ssl.SSLContext; |
| | | import javax.net.ssl.SSLSocketFactory; |
| | | import javax.net.ssl.TrustManager; |
| | | |
| | | /** |
| | | * Author:Alex |
| | | * Date:2019/5/29 |
| | | * Note:tcp连接 |
| | | */ |
| | | public class TcpConnection extends SuperConnection { |
| | | /** |
| | | * socket对象 |
| | | */ |
| | | private Socket socket; |
| | | |
| | | public TcpConnection(SocketAddress socketAddress) { |
| | | super(socketAddress); |
| | | } |
| | | |
| | | @Override |
| | | protected void openConnection() throws Exception { |
| | | try { |
| | | socket = getSocket(); |
| | | } catch (Exception e) { |
| | | e.printStackTrace(); |
| | | connectionStatus.set(SocketStatus.SOCKET_DISCONNECTED); // 设置为未连接 |
| | | throw new RuntimeException("创建socket失败"); |
| | | } |
| | | |
| | | // 进行socket连接 |
| | | socket.connect(new InetSocketAddress(socketAddress.getIp(), socketAddress.getPort()), socketOptions.getConnectTimeout()); |
| | | |
| | | // 关闭Nagle算法,无论TCP数据报大小,立即发送 |
| | | socket.setTcpNoDelay(true); |
| | | // 连接已经打开 |
| | | if (socket.isConnected() && !socket.isClosed()) { |
| | | onConnectionOpened(); |
| | | } |
| | | } |
| | | |
| | | @Override |
| | | protected void closeConnection() throws IOException { |
| | | if (socket != null) |
| | | socket.close(); |
| | | } |
| | | |
| | | /** |
| | | * 根据配置信息获取对应的socket |
| | | * |
| | | * @return |
| | | */ |
| | | private synchronized Socket getSocket() throws Exception { |
| | | // 自定义的socket生成工厂 |
| | | if (socketOptions.getSocketFactory() != null) { |
| | | return socketOptions.getSocketFactory().createSocket(socketAddress, socketOptions); |
| | | } |
| | | // 默认操作 |
| | | SocketSSLConfig config = socketOptions.getEasySSLConfig(); |
| | | if (config == null) { |
| | | return new Socket(); |
| | | } |
| | | // 获取SSL配置工厂 |
| | | SSLSocketFactory factory = config.getCustomSSLFactory(); |
| | | if (factory == null) { |
| | | String protocol = "SSL"; |
| | | if (!Utils.isStringEmpty(config.getProtocol())) { |
| | | protocol = config.getProtocol(); |
| | | } |
| | | |
| | | TrustManager[] trustManagers = config.getTrustManagers(); |
| | | if (trustManagers == null || trustManagers.length == 0) { |
| | | // 缺省信任所有证书 |
| | | trustManagers = new TrustManager[]{new DefaultX509ProtocolTrustManager()}; |
| | | } |
| | | |
| | | try { |
| | | SSLContext sslContext = SSLContext.getInstance(protocol); |
| | | sslContext.init(config.getKeyManagers(), trustManagers, new SecureRandom()); |
| | | return sslContext.getSocketFactory().createSocket(); |
| | | } catch (Exception e) { |
| | | if (socketOptions.isDebug()) { |
| | | e.printStackTrace(); |
| | | } |
| | | LogUtil.e(e.getMessage()); |
| | | return new Socket(); |
| | | } |
| | | |
| | | } else { |
| | | try { |
| | | return factory.createSocket(); |
| | | } catch (IOException e) { |
| | | if (socketOptions.isDebug()) { |
| | | e.printStackTrace(); |
| | | } |
| | | LogUtil.e(e.getMessage()); |
| | | return new Socket(); |
| | | } |
| | | } |
| | | } |
| | | |
| | | |
| | | @Override |
| | | public InputStream getInputStream() { |
| | | if (socket != null && socket.isConnected() && !socket.isClosed()) { |
| | | try { |
| | | return socket.getInputStream(); |
| | | } catch (IOException e) { |
| | | e.printStackTrace(); |
| | | } |
| | | } |
| | | return null; |
| | | } |
| | | |
| | | @Override |
| | | public OutputStream getOutStream() { |
| | | if (socket != null && socket.isConnected() && !socket.isClosed()) { |
| | | try { |
| | | return socket.getOutputStream(); |
| | | } catch (IOException e) { |
| | | e.printStackTrace(); |
| | | } |
| | | } |
| | | return null; |
| | | } |
| | | } |
| New file |
| | |
| | | package com.easysocket.connection.dispatcher; |
| | | |
| | | import com.easysocket.callback.SuperCallBack; |
| | | import com.easysocket.config.EasySocketOptions; |
| | | import com.easysocket.entity.OriginReadData; |
| | | import com.easysocket.entity.SocketAddress; |
| | | import com.easysocket.entity.basemsg.SuperCallbackSender; |
| | | import com.easysocket.exception.RequestTimeOutException; |
| | | import com.easysocket.interfaces.conn.IConnectionManager; |
| | | import com.easysocket.interfaces.conn.SocketActionListener; |
| | | import com.easysocket.utils.LogUtil; |
| | | import com.easysocket.utils.Utils; |
| | | |
| | | import java.util.HashMap; |
| | | import java.util.Map; |
| | | import java.util.concurrent.DelayQueue; |
| | | import java.util.concurrent.Delayed; |
| | | import java.util.concurrent.ExecutorService; |
| | | import java.util.concurrent.Executors; |
| | | import java.util.concurrent.TimeUnit; |
| | | |
| | | /** |
| | | * Author:Alex |
| | | * Date:2019/6/4 |
| | | * Note:回调消息分发器 |
| | | */ |
| | | public class CallbackResponseDispatcher { |
| | | /** |
| | | * 保存发送的每个回调消息的监听实例,key为回调标识callbackId,这样回调消息有反馈的时候,就可以找到并调用 |
| | | * 对应的监听对象 |
| | | */ |
| | | private Map<String, SuperCallBack> callbacks = new HashMap<>(); |
| | | /** |
| | | * 保存需要进行超时检测的请求,这是一个延时队列,元素超时的时候会被取出来 |
| | | */ |
| | | private DelayQueue<timeoutItem> timeoutQueue = new DelayQueue<>(); |
| | | /** |
| | | * 超时检测的线程管理器 |
| | | */ |
| | | private ExecutorService timeoutExecutor; |
| | | |
| | | /** |
| | | * 连接管理 |
| | | */ |
| | | IConnectionManager connectionManager; |
| | | |
| | | private EasySocketOptions socketOptions; |
| | | |
| | | |
| | | public CallbackResponseDispatcher(IConnectionManager connectionManager) { |
| | | this.connectionManager = connectionManager; |
| | | socketOptions = connectionManager.getOptions(); |
| | | // 注册监听 |
| | | connectionManager.subscribeSocketAction(socketActionListener); |
| | | // 开始超时检测线程 |
| | | engineThread(); |
| | | } |
| | | |
| | | /** |
| | | * 设置socketoptions |
| | | * |
| | | * @param socketOptions |
| | | */ |
| | | public void setSocketOptions(EasySocketOptions socketOptions) { |
| | | this.socketOptions = socketOptions; |
| | | } |
| | | |
| | | /** |
| | | * 超时检测线程 |
| | | */ |
| | | public void engineThread() { |
| | | try { |
| | | if (timeoutExecutor == null || timeoutExecutor.isShutdown()) { |
| | | timeoutExecutor = Executors.newSingleThreadExecutor(); |
| | | timeoutExecutor.execute(new Runnable() { |
| | | @Override |
| | | public void run() { |
| | | try { |
| | | // 只有超时的元素才会被取出,没有的话会被等待 |
| | | timeoutItem item = timeoutQueue.take(); |
| | | if (item != null) { |
| | | SuperCallBack callBack = callbacks.remove(item.callbackId); |
| | | if (callBack != null) |
| | | callBack.onError(new RequestTimeOutException("request timeout")); |
| | | } |
| | | } catch (InterruptedException e) { |
| | | e.printStackTrace(); |
| | | } |
| | | // 继续循环 |
| | | if (timeoutExecutor != null && !timeoutExecutor.isShutdown()) { |
| | | run(); |
| | | } |
| | | } |
| | | }); |
| | | } |
| | | } catch (Exception e) { |
| | | e.printStackTrace(); |
| | | } |
| | | } |
| | | |
| | | /** |
| | | * 关闭线程 |
| | | */ |
| | | public void shutdownThread() { |
| | | if (timeoutExecutor != null && !timeoutExecutor.isShutdown()) { |
| | | // shutdown和shutdownNow的主要区别是前者中断未执行的线程,后者中断所有线程 |
| | | timeoutExecutor.shutdownNow(); |
| | | timeoutExecutor = null; |
| | | } |
| | | |
| | | } |
| | | |
| | | /** |
| | | * socket行为监听,重写反馈消息的回调方法 |
| | | */ |
| | | private SocketActionListener socketActionListener = new SocketActionListener() { |
| | | @Override |
| | | public void onSocketResponse(SocketAddress socketAddress, OriginReadData originReadData) { |
| | | if (callbacks.size() == 0) return; |
| | | if (socketOptions.getCallbackIDFactory() == null) return; |
| | | // 获取回调ID |
| | | String callbackID = socketOptions.getCallbackIDFactory().getCallbackID(originReadData); |
| | | if (callbackID != null) { |
| | | // 获取callbackID对应的callback |
| | | SuperCallBack callBack = callbacks.get(callbackID); |
| | | if (callBack != null) { |
| | | // 回调 |
| | | callBack.onSuccess(originReadData); |
| | | callbacks.remove(callbackID); // 移除完成任务的callback |
| | | LogUtil.d("移除的callbackId-->" + callbackID); |
| | | } |
| | | } |
| | | } |
| | | }; |
| | | |
| | | |
| | | /** |
| | | * 每发一条回调消息都要在这里添加监听对象 |
| | | * |
| | | * @param superCallBack |
| | | */ |
| | | public void addSocketCallback(SuperCallBack superCallBack) { |
| | | callbacks.put(superCallBack.getCallbackId(), superCallBack); |
| | | // 放入延时队列 |
| | | long delayTime = socketOptions == null ? |
| | | EasySocketOptions.getDefaultOptions().getRequestTimeout() : socketOptions.getRequestTimeout(); |
| | | timeoutQueue.add(new timeoutItem(superCallBack.getCallbackId(), delayTime, TimeUnit.MILLISECONDS)); |
| | | } |
| | | |
| | | /** |
| | | * 延时队列的item |
| | | */ |
| | | class timeoutItem implements Delayed { |
| | | |
| | | String callbackId; // 当前callback的callbackId |
| | | long executeTime; // 触发时间 |
| | | |
| | | public timeoutItem(String callbackId, long delayTime, TimeUnit timeUnit) { |
| | | this.callbackId = callbackId; |
| | | this.executeTime = System.currentTimeMillis() + (delayTime > 0 ? timeUnit.toMillis(delayTime) : 0); |
| | | } |
| | | |
| | | @Override |
| | | public long getDelay(TimeUnit unit) { |
| | | return executeTime - System.currentTimeMillis(); |
| | | } |
| | | |
| | | @Override |
| | | public int compareTo(Delayed o) { |
| | | return (int) (this.getDelay(TimeUnit.MILLISECONDS) - o.getDelay(TimeUnit.MILLISECONDS)); |
| | | } |
| | | } |
| | | |
| | | /** |
| | | * 同一个消息发送多次,callbackId是不能一样的,所以这里要先check一下,否则服务端反馈的时候,客户端接收就会乱套 |
| | | * |
| | | * @param callbackSender |
| | | * @return |
| | | */ |
| | | public void checkCallbackSender(SuperCallbackSender callbackSender) { |
| | | |
| | | Utils.checkNotNull(socketOptions.getCallbackIDFactory(), "要想实现EasySocket的回调功能,CallbackIdFactory不能为null," + |
| | | "请实现一个CallbackIdFactory并在初始化的时候通过EasySocketOptions的setCallbackIdFactory进行配置"); |
| | | String callbackId = callbackSender.getCallbackId(); |
| | | // 同一个消息发送两次以上,callbackId是不能一样的,否则服务端反馈的时候,客户端接收就会乱套 |
| | | if (callbacks.containsKey(callbackId)) { |
| | | callbackSender.generateCallbackId(); |
| | | } |
| | | } |
| | | |
| | | } |
| New file |
| | |
| | | package com.easysocket.connection.dispatcher; |
| | | |
| | | import android.os.Handler; |
| | | import android.os.Looper; |
| | | |
| | | import java.util.concurrent.Executor; |
| | | |
| | | /** |
| | | * Author:Mapogo |
| | | * Date:2020/4/8 |
| | | * Note:切到主线程 |
| | | */ |
| | | public class MainThreadExecutor implements Executor { |
| | | |
| | | private final Handler handler = new Handler(Looper.getMainLooper()); |
| | | |
| | | @Override |
| | | public void execute(Runnable r) { |
| | | handler.post(r); |
| | | } |
| | | } |
| New file |
| | |
| | | package com.easysocket.connection.dispatcher; |
| | | |
| | | import com.easysocket.EasySocket; |
| | | import com.easysocket.entity.OriginReadData; |
| | | import com.easysocket.entity.SocketAddress; |
| | | import com.easysocket.interfaces.conn.IConnectionManager; |
| | | import com.easysocket.interfaces.conn.ISocketActionDispatch; |
| | | import com.easysocket.interfaces.conn.ISocketActionListener; |
| | | import com.easysocket.utils.Utils; |
| | | |
| | | import java.io.Serializable; |
| | | import java.nio.charset.Charset; |
| | | import java.util.ArrayList; |
| | | import java.util.Iterator; |
| | | import java.util.List; |
| | | import java.util.concurrent.LinkedBlockingQueue; |
| | | |
| | | import static com.easysocket.connection.action.IOAction.ACTION_READ_COMPLETE; |
| | | import static com.easysocket.connection.action.SocketAction.ACTION_CONN_FAIL; |
| | | import static com.easysocket.connection.action.SocketAction.ACTION_CONN_SUCCESS; |
| | | import static com.easysocket.connection.action.SocketAction.ACTION_DISCONNECTION; |
| | | |
| | | /** |
| | | * Author:Alex |
| | | * Date:2019/6/1 |
| | | * Note:socket行为分发器 |
| | | */ |
| | | public class SocketActionDispatcher implements ISocketActionDispatch { |
| | | /** |
| | | * 连接地址 |
| | | */ |
| | | private SocketAddress socketAddress; |
| | | /** |
| | | * 连接器 |
| | | */ |
| | | private IConnectionManager connectionManager; |
| | | /** |
| | | * 回调监听集合 |
| | | */ |
| | | private List<ISocketActionListener> actionListeners = new ArrayList<>(); |
| | | /** |
| | | * 处理socket行为的线程 |
| | | */ |
| | | private Thread actionThread; |
| | | /** |
| | | * 是否停止分发 |
| | | */ |
| | | private boolean isStop; |
| | | |
| | | /** |
| | | * 事件消费队列 |
| | | */ |
| | | private final LinkedBlockingQueue<ActionBean> socketActions = new LinkedBlockingQueue(); |
| | | /** |
| | | * 切换到UI线程 |
| | | */ |
| | | private MainThreadExecutor mainThreadExecutor = new MainThreadExecutor(); |
| | | |
| | | |
| | | public SocketActionDispatcher(IConnectionManager connectionManager, SocketAddress socketAddress) { |
| | | this.socketAddress = socketAddress; |
| | | this.connectionManager = connectionManager; |
| | | } |
| | | |
| | | public void setSocketAddress(SocketAddress info) { |
| | | socketAddress = info; |
| | | } |
| | | |
| | | |
| | | @Override |
| | | public void dispatchAction(String action) { |
| | | dispatchAction(action, null); |
| | | } |
| | | |
| | | @Override |
| | | public void dispatchAction(String action, Serializable serializable) { |
| | | // 将接收到的socket行为封装入列 |
| | | ActionBean actionBean = new ActionBean(action, serializable, this); |
| | | socketActions.offer(actionBean); |
| | | } |
| | | |
| | | @Override |
| | | public void subscribe(ISocketActionListener iSocketActionListener) { |
| | | if (iSocketActionListener != null && !actionListeners.contains(iSocketActionListener)) { |
| | | actionListeners.add(iSocketActionListener); |
| | | } |
| | | } |
| | | |
| | | @Override |
| | | public void unsubscribe(ISocketActionListener iSocketActionListener) { |
| | | actionListeners.remove(iSocketActionListener); |
| | | } |
| | | |
| | | /** |
| | | * 分发线程 |
| | | */ |
| | | private class DispatchThread extends Thread { |
| | | |
| | | public DispatchThread() { |
| | | super("dispatch thread"); |
| | | } |
| | | |
| | | @Override |
| | | public void run() { |
| | | // 循环处理socket的行为信息 |
| | | while (!isStop) { |
| | | try { |
| | | ActionBean actionBean = socketActions.take(); |
| | | if (actionBean != null && actionBean.mDispatcher != null) { |
| | | SocketActionDispatcher actionDispatcher = actionBean.mDispatcher; |
| | | List<ISocketActionListener> copyListeners = new ArrayList<>(actionDispatcher.actionListeners); |
| | | Iterator<ISocketActionListener> listeners = copyListeners.iterator(); |
| | | // 通知所有监听者 |
| | | while (listeners.hasNext()) { |
| | | ISocketActionListener listener = listeners.next(); |
| | | actionDispatcher.dispatchActionToListener(actionBean.mAction, actionBean.arg, listener); |
| | | } |
| | | } |
| | | } catch (InterruptedException e) { |
| | | e.printStackTrace(); |
| | | } |
| | | } |
| | | } |
| | | } |
| | | |
| | | /** |
| | | * socket行为的封装 |
| | | */ |
| | | protected static class ActionBean { |
| | | |
| | | public ActionBean(String action, Serializable arg, SocketActionDispatcher dispatcher) { |
| | | mAction = action; |
| | | this.arg = arg; |
| | | mDispatcher = dispatcher; |
| | | } |
| | | |
| | | String mAction = ""; |
| | | Serializable arg; |
| | | SocketActionDispatcher mDispatcher; |
| | | } |
| | | |
| | | /** |
| | | * 分发行为给监听者 |
| | | * |
| | | * @param action |
| | | * @param content |
| | | * @param actionListener |
| | | */ |
| | | private void dispatchActionToListener(String action, final Serializable content, final ISocketActionListener actionListener) { |
| | | switch (action) { |
| | | |
| | | case ACTION_CONN_SUCCESS: // 连接成功 |
| | | mainThreadExecutor.execute(new Runnable() { |
| | | @Override |
| | | public void run() { |
| | | actionListener.onSocketConnSuccess(socketAddress); |
| | | } |
| | | }); |
| | | |
| | | break; |
| | | |
| | | case ACTION_CONN_FAIL: // 连接失败 |
| | | mainThreadExecutor.execute(new Runnable() { |
| | | @Override |
| | | public void run() { |
| | | actionListener.onSocketConnFail(socketAddress, ((Boolean) content).booleanValue()); |
| | | } |
| | | }); |
| | | |
| | | break; |
| | | |
| | | case ACTION_DISCONNECTION: // 连接断开 |
| | | mainThreadExecutor.execute(new Runnable() { |
| | | @Override |
| | | public void run() { |
| | | actionListener.onSocketDisconnect(socketAddress, ((Boolean) content).booleanValue()); |
| | | // 不需要重连,则释放资源 |
| | | if (!(Boolean) content) { |
| | | stopDispatchThread(); |
| | | } |
| | | } |
| | | }); |
| | | break; |
| | | |
| | | case ACTION_READ_COMPLETE: // 读取数据完成 |
| | | mainThreadExecutor.execute(new Runnable() { |
| | | @Override |
| | | public void run() { |
| | | // response有三种形式 |
| | | actionListener.onSocketResponse(socketAddress, (OriginReadData) content); |
| | | byte[] data = Utils.concatBytes(((OriginReadData) content).getHeaderData(), ((OriginReadData) content).getBodyBytes()); |
| | | actionListener.onSocketResponse(socketAddress, new String(data, Charset.forName(EasySocket.getInstance().getDefOptions().getCharsetName()))); |
| | | actionListener.onSocketResponse(socketAddress, data); |
| | | } |
| | | }); |
| | | break; |
| | | } |
| | | } |
| | | |
| | | // 开始分发线程 |
| | | @Override |
| | | public void startDispatchThread() { |
| | | isStop = false; |
| | | if (actionThread == null) { |
| | | actionThread = new DispatchThread(); |
| | | actionThread.start(); |
| | | } |
| | | } |
| | | |
| | | @Override |
| | | public void stopDispatchThread() { |
| | | if (actionThread != null && actionThread.isAlive() && !actionThread.isInterrupted()) { |
| | | socketActions.clear(); |
| | | //actionListeners.clear(); |
| | | isStop = true; |
| | | actionThread.interrupt(); |
| | | actionThread = null; |
| | | } |
| | | } |
| | | |
| | | } |
| New file |
| | |
| | | package com.easysocket.connection.heartbeat; |
| | | |
| | | import com.easysocket.config.EasySocketOptions; |
| | | import com.easysocket.entity.OriginReadData; |
| | | import com.easysocket.entity.SocketAddress; |
| | | import com.easysocket.interfaces.config.IOptions; |
| | | import com.easysocket.interfaces.conn.IConnectionManager; |
| | | import com.easysocket.interfaces.conn.IHeartManager; |
| | | import com.easysocket.interfaces.conn.ISocketActionDispatch; |
| | | import com.easysocket.interfaces.conn.SocketActionListener; |
| | | |
| | | import java.util.concurrent.Executors; |
| | | import java.util.concurrent.ScheduledExecutorService; |
| | | import java.util.concurrent.TimeUnit; |
| | | import java.util.concurrent.atomic.AtomicInteger; |
| | | |
| | | /** |
| | | * Author:Alex |
| | | * Date:2019/12/8 |
| | | * Note:心跳包检测管理器 |
| | | */ |
| | | public class HeartManager extends SocketActionListener implements IOptions, IHeartManager { |
| | | |
| | | /** |
| | | * 连接器 |
| | | */ |
| | | private IConnectionManager connectionManager; |
| | | /** |
| | | * 连接参数 |
| | | */ |
| | | private EasySocketOptions socketOptions; |
| | | /** |
| | | * 客户端心跳包 |
| | | */ |
| | | private byte[] clientHeart; |
| | | /** |
| | | * 心跳包发送线程 |
| | | */ |
| | | private ScheduledExecutorService heartExecutor; |
| | | /** |
| | | * 记录心跳的失联次数 |
| | | */ |
| | | private AtomicInteger loseTimes = new AtomicInteger(-1); |
| | | /** |
| | | * 心跳频率 |
| | | */ |
| | | private long freq; |
| | | /** |
| | | * 是否激活了心跳 |
| | | */ |
| | | private boolean isActivate; |
| | | |
| | | |
| | | /** |
| | | * 心跳包接收监听 |
| | | */ |
| | | private HeartbeatListener heartbeatListener; |
| | | |
| | | |
| | | public HeartManager(IConnectionManager iConnectionManager, ISocketActionDispatch actionDispatch) { |
| | | this.connectionManager = iConnectionManager; |
| | | socketOptions = iConnectionManager.getOptions(); |
| | | actionDispatch.subscribe(this); // 注册监听 |
| | | } |
| | | |
| | | /** |
| | | * 心跳发送任务 |
| | | */ |
| | | private final Runnable beatTask = new Runnable() { |
| | | @Override |
| | | public void run() { |
| | | // 心跳丢失次数判断,心跳包丢失了一定的次数则会进行socket的断开重连 |
| | | if (socketOptions.getMaxHeartbeatLoseTimes() != -1 |
| | | && loseTimes.incrementAndGet() >= socketOptions.getMaxHeartbeatLoseTimes()) { |
| | | // 断开重连 |
| | | connectionManager.disconnect(true); |
| | | resetLoseTimes(); |
| | | } else { // 发送心跳包 |
| | | connectionManager.upBytes(clientHeart); |
| | | } |
| | | } |
| | | }; |
| | | |
| | | |
| | | @Override |
| | | public void startHeartbeat(byte[] clientHeart, HeartbeatListener listener) { |
| | | this.clientHeart = clientHeart; |
| | | this.heartbeatListener = listener; |
| | | isActivate = true; |
| | | openThread(); |
| | | } |
| | | |
| | | |
| | | // 启动心跳线程 |
| | | private void openThread() { |
| | | freq = socketOptions.getHeartbeatFreq(); // 心跳频率 |
| | | // 启动线程发送心跳 |
| | | if (heartExecutor == null || heartExecutor.isShutdown()) { |
| | | heartExecutor = Executors.newSingleThreadScheduledExecutor(); |
| | | heartExecutor.scheduleWithFixedDelay(beatTask, 0, freq, TimeUnit.MILLISECONDS); |
| | | } |
| | | } |
| | | |
| | | /** |
| | | * 停止心跳发送 |
| | | */ |
| | | @Override |
| | | public void stopHeartbeat() { |
| | | isActivate = false; |
| | | closeThread(); |
| | | } |
| | | |
| | | // 停止心跳线程 |
| | | private void closeThread() { |
| | | if (heartExecutor != null && !heartExecutor.isShutdown()) { |
| | | heartExecutor.shutdownNow(); |
| | | heartExecutor = null; |
| | | resetLoseTimes(); // 重置 |
| | | } |
| | | } |
| | | |
| | | @Override |
| | | public void onReceiveHeartBeat() { |
| | | resetLoseTimes(); |
| | | } |
| | | |
| | | |
| | | private void resetLoseTimes() { |
| | | loseTimes.set(-1); |
| | | } |
| | | |
| | | @Override |
| | | public void onSocketConnSuccess(SocketAddress socketAddress) { |
| | | if (isActivate) { |
| | | openThread(); |
| | | } |
| | | } |
| | | |
| | | @Override |
| | | public void onSocketConnFail(SocketAddress socketAddress, boolean isNeedReconnect) { |
| | | // 如果不需要重连,则停止心跳频率线程 |
| | | if (!isNeedReconnect) { |
| | | closeThread(); |
| | | } |
| | | } |
| | | |
| | | @Override |
| | | public void onSocketDisconnect(SocketAddress socketAddress, boolean isNeedReconnect) { |
| | | // 如果不需要重连,则停止心跳检测 |
| | | if (!isNeedReconnect) { |
| | | closeThread(); |
| | | } |
| | | } |
| | | |
| | | @Override |
| | | public void onSocketResponse(SocketAddress socketAddress, OriginReadData originReadData) { |
| | | if (heartbeatListener != null && heartbeatListener.isServerHeartbeat(originReadData)) { |
| | | // 收到服务器心跳 |
| | | onReceiveHeartBeat(); |
| | | } |
| | | } |
| | | |
| | | @Override |
| | | public Object setOptions(EasySocketOptions socketOptions) { |
| | | this.socketOptions = socketOptions; |
| | | freq = socketOptions.getHeartbeatFreq(); |
| | | freq = freq < 1000 ? 1000 : freq; // 不能小于一秒 |
| | | return this; |
| | | } |
| | | |
| | | @Override |
| | | public EasySocketOptions getOptions() { |
| | | return socketOptions; |
| | | } |
| | | |
| | | public interface HeartbeatListener { |
| | | // 是否为服务器心跳 |
| | | boolean isServerHeartbeat(OriginReadData orginReadData); |
| | | } |
| | | |
| | | } |
| New file |
| | |
| | | package com.easysocket.connection.iowork; |
| | | |
| | | import com.easysocket.config.EasySocketOptions; |
| | | import com.easysocket.connection.action.IOAction; |
| | | import com.easysocket.connection.action.SocketStatus; |
| | | import com.easysocket.entity.OriginReadData; |
| | | import com.easysocket.exception.ReadRecoverableExeption; |
| | | import com.easysocket.exception.ReadUnrecoverableException; |
| | | import com.easysocket.interfaces.config.IMessageProtocol; |
| | | import com.easysocket.interfaces.conn.IConnectionManager; |
| | | import com.easysocket.interfaces.conn.ISocketActionDispatch; |
| | | import com.easysocket.interfaces.io.IReader; |
| | | import com.easysocket.utils.HexUtil; |
| | | import com.easysocket.utils.LogUtil; |
| | | |
| | | import java.io.IOException; |
| | | import java.io.InputStream; |
| | | import java.nio.ByteBuffer; |
| | | |
| | | /** |
| | | * Author:Alex |
| | | * Date:2019/6/1 |
| | | * Note: |
| | | */ |
| | | public class EasyReader implements IReader<EasySocketOptions> { |
| | | /** |
| | | * 输入流 |
| | | */ |
| | | private InputStream inputStream; |
| | | /** |
| | | * 读取原始数据的缓存空间 |
| | | */ |
| | | private ByteBuffer originBuf; |
| | | /** |
| | | * socket行为分发器 |
| | | */ |
| | | private ISocketActionDispatch actionDispatch; |
| | | /** |
| | | * 连接器 |
| | | */ |
| | | private IConnectionManager connectionManager; |
| | | /** |
| | | * 连接参数 |
| | | */ |
| | | private EasySocketOptions socketOptions; |
| | | |
| | | /** |
| | | * 读数据时,余留数据的缓存 |
| | | */ |
| | | private ByteBuffer remainingBuf; |
| | | /** |
| | | * 读数据线程 |
| | | */ |
| | | private Thread readerThread; |
| | | /** |
| | | * 是否停止线程 |
| | | */ |
| | | private boolean stopThread; |
| | | |
| | | public EasyReader(IConnectionManager connectionManager, ISocketActionDispatch actionDispatch) { |
| | | this.actionDispatch = actionDispatch; |
| | | this.connectionManager = connectionManager; |
| | | socketOptions = connectionManager.getOptions(); |
| | | } |
| | | |
| | | @Override |
| | | public void read() throws IOException, ReadRecoverableExeption, ReadUnrecoverableException { |
| | | OriginReadData originalData = new OriginReadData(); |
| | | IMessageProtocol messageProtocol = socketOptions.getMessageProtocol(); |
| | | // 消息协议为null,则直接读原始消息,不建议这样使用,因为会发生黏包、分包的问题 |
| | | if (messageProtocol == null) { |
| | | readOriginDataFromSteam(originalData); |
| | | return; |
| | | } |
| | | |
| | | // 定义了消息协议 |
| | | int headerLength = messageProtocol.getHeaderLength(); // 包头长度 |
| | | ByteBuffer headBuf = ByteBuffer.allocate(headerLength); // 包头数据的buffer |
| | | headBuf.order(socketOptions.getReadOrder()); |
| | | |
| | | /*1、读 header=====>>>*/ |
| | | if (remainingBuf != null) { // 有余留 |
| | | // flip方法:一般从Buffer读数据前调用,将limit设置为当前position,将position设置为0,在读数据时,limit代表可读数据的有效长度 |
| | | remainingBuf.flip(); |
| | | // 读余留数据的长度 |
| | | int length = Math.min(remainingBuf.remaining(), headerLength); |
| | | // 读入余留数据 |
| | | headBuf.put(remainingBuf.array(), 0, length); |
| | | |
| | | if (length < headerLength) { // 余留数据小于一个header |
| | | // there are no data left |
| | | remainingBuf = null; |
| | | // 从stream中读剩下的header数据 |
| | | readHeaderFromSteam(headBuf, headerLength - length); |
| | | } else { |
| | | // 移动开始读数据的指针 |
| | | remainingBuf.position(headerLength); |
| | | } |
| | | } else { // 无余留 |
| | | // 从stream读取一个完整的 header |
| | | readHeaderFromSteam(headBuf, headBuf.capacity()); |
| | | } |
| | | |
| | | // 保存header |
| | | originalData.setHeaderData(headBuf.array()); |
| | | |
| | | /*2、读 body=====>>>*/ |
| | | int bodyLength = messageProtocol.getBodyLength(originalData.getHeaderData(), socketOptions.getReadOrder()); |
| | | if (bodyLength > 0) { |
| | | if (bodyLength > socketOptions.getMaxResponseDataMb() * 1024 * 1024) { |
| | | throw new ReadUnrecoverableException("服务器返回的单次数据超过了规定的最大值,可能你的Socket消息协议不对,一般消息格式" + |
| | | "为:Header+Body,其中Header保存消息长度和类型等,Body保存消息内容,请规范好你的协议"); |
| | | } |
| | | // 分配空间 |
| | | ByteBuffer bodyBuf = ByteBuffer.allocate(bodyLength); |
| | | bodyBuf.order(socketOptions.getReadOrder()); |
| | | |
| | | // 有余留 |
| | | if (remainingBuf != null) { |
| | | int bodyStartPosition = remainingBuf.position(); |
| | | |
| | | int length = Math.min(remainingBuf.remaining(), bodyLength); |
| | | // 读length大小的余留数据 |
| | | bodyBuf.put(remainingBuf.array(), bodyStartPosition, length); |
| | | // 移动position位置 |
| | | remainingBuf.position(bodyStartPosition + length); |
| | | |
| | | // 读的余留数据刚好等于一个body |
| | | if (length == bodyLength) { |
| | | if (remainingBuf.remaining() > 0) { // 余留数据未读完 |
| | | ByteBuffer temp = ByteBuffer.allocate(remainingBuf.remaining()); |
| | | temp.order(socketOptions.getReadOrder()); |
| | | temp.put(remainingBuf.array(), remainingBuf.position(), remainingBuf.remaining()); |
| | | remainingBuf = temp; |
| | | } else { // there are no data left |
| | | remainingBuf = null; |
| | | } |
| | | |
| | | // 保存body |
| | | originalData.setBodyData(bodyBuf.array()); |
| | | |
| | | LogUtil.d("Socket收到数据-->" +HexUtil.bytesToHex(originalData.getBodyBytes()) ); |
| | | // 分发数据 |
| | | actionDispatch.dispatchAction(IOAction.ACTION_READ_COMPLETE, originalData); |
| | | |
| | | /*return读取结束*/ |
| | | return; |
| | | |
| | | } else { // there are no data left in buffer and some data pieces in channel |
| | | remainingBuf = null; |
| | | } |
| | | } |
| | | // 无余留,则从stream中读 |
| | | readBodyFromStream(bodyBuf); |
| | | // 保存body到originalData |
| | | originalData.setBodyData(bodyBuf.array()); |
| | | |
| | | } else if (bodyLength == 0) { // 没有body数据 |
| | | originalData.setBodyData(new byte[0]); |
| | | if (remainingBuf != null) { |
| | | // the body is empty so header remaining buf need set null |
| | | if (remainingBuf.hasRemaining()) { |
| | | ByteBuffer temp = ByteBuffer.allocate(remainingBuf.remaining()); |
| | | temp.order(socketOptions.getReadOrder()); |
| | | temp.put(remainingBuf.array(), remainingBuf.position(), remainingBuf.remaining()); |
| | | remainingBuf = temp; |
| | | } else { |
| | | remainingBuf = null; |
| | | } |
| | | } |
| | | } else if (bodyLength < 0) { |
| | | throw new ReadUnrecoverableException("数据body的长度不能小于0"); |
| | | } |
| | | |
| | | LogUtil.d("Socket收到数据-->" + HexUtil.bytesToHex(originalData.getBodyBytes())); |
| | | // 分发 |
| | | actionDispatch.dispatchAction(IOAction.ACTION_READ_COMPLETE, originalData); |
| | | |
| | | } |
| | | |
| | | |
| | | /** |
| | | * 读数据任务 |
| | | */ |
| | | private Runnable readerTask = new Runnable() { |
| | | @Override |
| | | public void run() { |
| | | try { |
| | | while (!stopThread) { |
| | | read(); |
| | | } |
| | | } catch (ReadUnrecoverableException unrecoverableException) { |
| | | // 读异常 |
| | | unrecoverableException.printStackTrace(); |
| | | // 停止线程 |
| | | stopThread = true; |
| | | release(); |
| | | } catch (ReadRecoverableExeption readRecoverableExeption) { |
| | | readRecoverableExeption.printStackTrace(); |
| | | // 重连 |
| | | LogUtil.d("--->重连 ReadRecoverableExeption"); |
| | | connectionManager.disconnect(true); |
| | | |
| | | } catch (IOException e) { |
| | | e.printStackTrace(); |
| | | // 重连 |
| | | LogUtil.d("--->重连 IOException"); |
| | | if (connectionManager.getConnectionStatus() != SocketStatus.SOCKET_DISCONNECTING) { |
| | | connectionManager.disconnect(true); |
| | | } |
| | | } |
| | | } |
| | | }; |
| | | |
| | | |
| | | private void readHeaderFromSteam(ByteBuffer headBuf, int readLength) throws ReadRecoverableExeption, IOException { |
| | | for (int i = 0; i < readLength; i++) { |
| | | byte[] bytes = new byte[1]; |
| | | // 从输入流中读数据,无数据时会阻塞 |
| | | int value = inputStream.read(bytes); |
| | | // -1代表读到了文件的末尾,一般是因为服务器断开了连接 |
| | | if (value == -1) { |
| | | throw new ReadRecoverableExeption("读数据失败,可能是因为socket跟服务器断开了连接"); |
| | | } |
| | | headBuf.put(bytes); |
| | | } |
| | | } |
| | | |
| | | private void readOriginDataFromSteam(OriginReadData readData) throws ReadRecoverableExeption, IOException { |
| | | // 用 全局originBuf避免重复创建字节数组 |
| | | int len = inputStream.read(originBuf.array()); |
| | | // no more data |
| | | if (len == -1) { |
| | | throw new ReadRecoverableExeption("读数据失败,可能因为socket跟服务器断开了连接"); |
| | | } |
| | | // bytes复制 |
| | | byte[] data = new byte[len]; |
| | | originBuf.get(data, 0, len); |
| | | readData.setBodyData(data); |
| | | LogUtil.d("Socket收到数据-->" + HexUtil.bytesToHex(readData.getBodyBytes())); |
| | | // 分发数据 |
| | | actionDispatch.dispatchAction(IOAction.ACTION_READ_COMPLETE, readData); |
| | | // 相当于把指针重新指向positon=0 |
| | | originBuf.clear(); |
| | | } |
| | | |
| | | private void readBodyFromStream(ByteBuffer byteBuffer) throws ReadRecoverableExeption, IOException { |
| | | // while循环直到byteBuffer装满数据 |
| | | while (byteBuffer.hasRemaining()) { |
| | | byte[] bufArray = new byte[socketOptions.getMaxReadBytes()]; // 从服务器单次读取的最大值 |
| | | int len = inputStream.read(bufArray); |
| | | if (len == -1) { // no more data |
| | | throw new ReadRecoverableExeption("读数据失败,可能是因为socket跟服务器断开了连接"); |
| | | } |
| | | int remaining = byteBuffer.remaining(); |
| | | if (len > remaining) { // 从stream读的数据超过byteBuffer的剩余空间 |
| | | byteBuffer.put(bufArray, 0, remaining); |
| | | // 将多余的数据保存到remainingBuf中缓存,等下一次读取 |
| | | remainingBuf = ByteBuffer.allocate(len - remaining); |
| | | remainingBuf.order(socketOptions.getReadOrder()); |
| | | remainingBuf.put(bufArray, remaining, len - remaining); |
| | | } else { // 从stream读的数据小于或等于byteBuffer的剩余空间 |
| | | byteBuffer.put(bufArray, 0, len); |
| | | } |
| | | } |
| | | } |
| | | |
| | | @Override |
| | | public void openReader() { |
| | | init(); |
| | | if (readerThread == null || !readerThread.isAlive()) { |
| | | readerThread = new Thread(readerTask, "reader thread"); |
| | | stopThread = false; |
| | | readerThread.start(); |
| | | } |
| | | } |
| | | |
| | | @Override |
| | | public void closeReader() { |
| | | try { |
| | | // 关闭线程释放资源 |
| | | shutDownThread(); |
| | | release(); |
| | | } catch (InterruptedException e) { |
| | | e.printStackTrace(); |
| | | } |
| | | } |
| | | |
| | | // 释放资源 |
| | | private void release() { |
| | | if (originBuf != null) { |
| | | originBuf = null; |
| | | } |
| | | if (remainingBuf != null) { |
| | | remainingBuf = null; |
| | | } |
| | | if (readerThread != null && !readerThread.isAlive()) { |
| | | readerThread = null; |
| | | } |
| | | |
| | | try { |
| | | if (inputStream != null) |
| | | inputStream.close(); |
| | | } catch (IOException e) { |
| | | e.printStackTrace(); |
| | | } finally { |
| | | inputStream = null; |
| | | } |
| | | } |
| | | |
| | | // 初始化 |
| | | private void init() { |
| | | inputStream = connectionManager.getInputStream(); |
| | | // 没有定义消息协议 |
| | | if (socketOptions.getMessageProtocol() == null) { |
| | | originBuf = ByteBuffer.allocate(1024 * 4); |
| | | } |
| | | } |
| | | |
| | | @Override |
| | | public void setOption(EasySocketOptions socketOptions) { |
| | | this.socketOptions = socketOptions; |
| | | } |
| | | |
| | | // 关闭读数据线程 |
| | | private void shutDownThread() throws InterruptedException { |
| | | if (readerThread != null && readerThread.isAlive() && !readerThread.isInterrupted()) { |
| | | try { |
| | | stopThread = true; |
| | | readerThread.interrupt(); |
| | | readerThread.join(); |
| | | } catch (InterruptedException e) { |
| | | e.printStackTrace(); |
| | | } |
| | | } |
| | | } |
| | | } |
| New file |
| | |
| | | package com.easysocket.connection.iowork; |
| | | |
| | | import com.easysocket.config.EasySocketOptions; |
| | | import com.easysocket.interfaces.conn.IConnectionManager; |
| | | import com.easysocket.interfaces.conn.ISocketActionDispatch; |
| | | import com.easysocket.interfaces.io.IWriter; |
| | | import com.easysocket.utils.HexUtil; |
| | | import com.easysocket.utils.LogUtil; |
| | | |
| | | import java.io.IOException; |
| | | import java.io.OutputStream; |
| | | import java.nio.ByteBuffer; |
| | | import java.nio.charset.Charset; |
| | | import java.util.Arrays; |
| | | import java.util.concurrent.LinkedBlockingDeque; |
| | | |
| | | /** |
| | | * Author:Alex |
| | | * Date:2019/6/1 |
| | | * Note: |
| | | */ |
| | | public class EasyWriter implements IWriter<EasySocketOptions> { |
| | | |
| | | /** |
| | | * 输出流 |
| | | */ |
| | | private OutputStream outputStream; |
| | | |
| | | /** |
| | | * 连接管理器 |
| | | */ |
| | | private IConnectionManager connectionManager; |
| | | /** |
| | | * socket参数 |
| | | */ |
| | | private EasySocketOptions socketOptions; |
| | | /** |
| | | * 行为分发 |
| | | */ |
| | | private ISocketActionDispatch actionDispatch; |
| | | /** |
| | | * 写数据线程 |
| | | */ |
| | | private Thread writerThread; |
| | | /** |
| | | * 是否停止写数据 |
| | | */ |
| | | private boolean isStop; |
| | | /** |
| | | * 待写入数据 |
| | | */ |
| | | private LinkedBlockingDeque<byte[]> packetsToSend = new LinkedBlockingDeque<>(); |
| | | |
| | | public EasyWriter(IConnectionManager connectionManager, ISocketActionDispatch actionDispatch) { |
| | | this.connectionManager = connectionManager; |
| | | socketOptions = connectionManager.getOptions(); |
| | | this.actionDispatch = actionDispatch; |
| | | } |
| | | |
| | | @Override |
| | | public void openWriter() { |
| | | outputStream = connectionManager.getOutStream(); |
| | | if (writerThread == null) { |
| | | isStop = false; |
| | | writerThread = new Thread(writerTask, "writer thread"); |
| | | writerThread.start(); |
| | | } |
| | | } |
| | | |
| | | @Override |
| | | public void setOption(EasySocketOptions socketOptions) { |
| | | this.socketOptions = socketOptions; |
| | | } |
| | | |
| | | /** |
| | | * 写任务 |
| | | */ |
| | | private Runnable writerTask = new Runnable() { |
| | | @Override |
| | | public void run() { |
| | | // 循环写数据 |
| | | while (!isStop) { |
| | | try { |
| | | byte[] sender = packetsToSend.take(); |
| | | write(sender); |
| | | } catch (InterruptedException | IOException e) { |
| | | e.printStackTrace(); |
| | | } |
| | | } |
| | | } |
| | | }; |
| | | |
| | | @Override |
| | | public void write(byte[] sendBytes) throws IOException { |
| | | if (sendBytes != null) { |
| | | LogUtil.d("EasyWriter--Socket发送数据String-->" + HexUtil.bytesToHex(sendBytes)); |
| | | LogUtil.d("EasyWriter--Socket发送数据byte[]-->" + Arrays.toString(sendBytes)); |
| | | int packageSize = socketOptions.getMaxWriteBytes(); // 每次可以发送的最大数据 |
| | | int remainingCount = sendBytes.length; |
| | | ByteBuffer writeBuf = ByteBuffer.allocate(packageSize); |
| | | writeBuf.order(socketOptions.getReadOrder()); |
| | | int index = 0; |
| | | // 如果发送的数据大于单次可发送的最大数据,则分多次发送 |
| | | while (remainingCount > 0) { |
| | | int realWriteLength = Math.min(packageSize, remainingCount); |
| | | writeBuf.clear(); // 清空缓存 |
| | | writeBuf.rewind(); // 将position位置移到0 |
| | | writeBuf.put(sendBytes, index, realWriteLength); |
| | | writeBuf.flip(); |
| | | byte[] writeArr = new byte[realWriteLength]; |
| | | writeBuf.get(writeArr); |
| | | outputStream.write(writeArr); |
| | | outputStream.flush(); // 强制写入缓存中残留数据 |
| | | index += realWriteLength; |
| | | remainingCount -= realWriteLength; |
| | | } |
| | | } |
| | | } |
| | | |
| | | @Override |
| | | public void offer(byte[] sender) { |
| | | if (!isStop) |
| | | packetsToSend.offer(sender); |
| | | } |
| | | |
| | | @Override |
| | | public void closeWriter() { |
| | | try { |
| | | if (outputStream != null) |
| | | outputStream.close(); |
| | | shutDownThread(); |
| | | } catch (IOException | InterruptedException e) { |
| | | e.printStackTrace(); |
| | | } finally { |
| | | outputStream = null; |
| | | } |
| | | } |
| | | |
| | | private void shutDownThread() throws InterruptedException { |
| | | if (writerThread != null && writerThread.isAlive() && !writerThread.isInterrupted()) { |
| | | try { |
| | | isStop = true; |
| | | writerThread.interrupt(); |
| | | writerThread.join(); |
| | | writerThread = null; |
| | | } catch (InterruptedException e) { |
| | | e.printStackTrace(); |
| | | } |
| | | } |
| | | } |
| | | } |
| New file |
| | |
| | | package com.easysocket.connection.iowork; |
| | | |
| | | import com.easysocket.config.EasySocketOptions; |
| | | import com.easysocket.interfaces.config.IOptions; |
| | | import com.easysocket.interfaces.conn.IConnectionManager; |
| | | import com.easysocket.interfaces.conn.ISocketActionDispatch; |
| | | import com.easysocket.interfaces.io.IIOManager; |
| | | import com.easysocket.interfaces.io.IReader; |
| | | import com.easysocket.interfaces.io.IWriter; |
| | | |
| | | /** |
| | | * Author:Alex |
| | | * Date:2019/5/28 |
| | | * Note: |
| | | */ |
| | | public class IOManager implements IIOManager, IOptions { |
| | | /** |
| | | * socket行为回调 |
| | | */ |
| | | private ISocketActionDispatch actionDispatch; |
| | | /** |
| | | * 连接管理 |
| | | */ |
| | | private IConnectionManager connectionManager; |
| | | /** |
| | | * 写 |
| | | */ |
| | | private IWriter writer; |
| | | /** |
| | | * 读 |
| | | */ |
| | | private IReader reader; |
| | | |
| | | public IOManager(IConnectionManager connectionManager, |
| | | ISocketActionDispatch connActionDispatch) { |
| | | this.connectionManager = connectionManager; |
| | | this.actionDispatch = connActionDispatch; |
| | | initIO(); |
| | | } |
| | | |
| | | // 初始化io |
| | | private void initIO() { |
| | | //makesureHeaderProtocolNotEmpty(); |
| | | reader = new EasyReader(connectionManager, actionDispatch); // 读 |
| | | writer = new EasyWriter(connectionManager, actionDispatch); // 写 |
| | | } |
| | | |
| | | @Override |
| | | public void sendBytes(byte[] bytes) { |
| | | if (writer != null) |
| | | writer.offer(bytes); |
| | | } |
| | | |
| | | @Override |
| | | public void startIO() { |
| | | if (writer != null) |
| | | writer.openWriter(); |
| | | if (reader != null) |
| | | reader.openReader(); |
| | | } |
| | | |
| | | @Override |
| | | public void closeIO() { |
| | | if (writer != null) |
| | | writer.closeWriter(); |
| | | if (reader != null) |
| | | reader.closeReader(); |
| | | } |
| | | |
| | | @Override |
| | | public Object setOptions(EasySocketOptions socketOptions) { |
| | | //makesureHeaderProtocolNotEmpty(); |
| | | if (writer != null) |
| | | writer.setOption(socketOptions); |
| | | if (reader != null) |
| | | reader.setOption(socketOptions); |
| | | return this; |
| | | } |
| | | |
| | | @Override |
| | | public EasySocketOptions getOptions() { |
| | | return connectionManager.getOptions(); |
| | | } |
| | | |
| | | /** |
| | | * 确保包结构协议不为空 |
| | | */ |
| | | // private void makesureHeaderProtocolNotEmpty() { |
| | | // IMessageProtocol protocol = connectionManager.getOptions().getMessageProtocol(); |
| | | // if (protocol == null) { |
| | | // throw new NoNullException("The reader protocol can not be Null."); |
| | | // } |
| | | // |
| | | // if (protocol.getHeaderLength() == 0) { |
| | | // throw new NoNullException("The header length can not be zero."); |
| | | // } |
| | | // } |
| | | } |
| New file |
| | |
| | | package com.easysocket.connection.reconnect; |
| | | |
| | | import com.easysocket.entity.OriginReadData; |
| | | import com.easysocket.entity.SocketAddress; |
| | | import com.easysocket.interfaces.conn.IConnectionManager; |
| | | import com.easysocket.interfaces.conn.IReconnListener; |
| | | import com.easysocket.interfaces.conn.SocketActionListener; |
| | | |
| | | /** |
| | | * Author:Alex |
| | | * Date:2019/5/31 |
| | | * Note:抽象重连器 |
| | | */ |
| | | public abstract class AbsReconnection extends SocketActionListener implements IReconnListener { |
| | | /** |
| | | * 连接管理器 |
| | | */ |
| | | protected IConnectionManager connectionManager; |
| | | /** |
| | | * socket连接管理器是否已销毁 |
| | | */ |
| | | protected boolean isDetach; |
| | | |
| | | |
| | | @Override |
| | | public synchronized void attach(IConnectionManager iConnectionManager) { |
| | | if (!isDetach) { |
| | | detach(); |
| | | } |
| | | isDetach = false; |
| | | connectionManager = iConnectionManager; |
| | | connectionManager.subscribeSocketAction(this); // 监听socket行为 |
| | | } |
| | | |
| | | @Override |
| | | public synchronized void detach() { |
| | | isDetach = true; |
| | | if (connectionManager != null) |
| | | connectionManager.unSubscribeSocketAction(this); |
| | | } |
| | | |
| | | @Override |
| | | public void onSocketResponse(SocketAddress socketAddress, OriginReadData originReadData) { |
| | | // donothing |
| | | } |
| | | |
| | | /** |
| | | * 是否正在重连 |
| | | * @return |
| | | */ |
| | | public abstract boolean isReconning(); |
| | | } |
| New file |
| | |
| | | package com.easysocket.connection.reconnect; |
| | | |
| | | import android.os.Handler; |
| | | import android.os.HandlerThread; |
| | | |
| | | import com.easysocket.entity.SocketAddress; |
| | | import com.easysocket.interfaces.conn.IConnectionManager; |
| | | import com.easysocket.utils.LogUtil; |
| | | |
| | | /** |
| | | * Author:Alex |
| | | * Date:2019/5/28 |
| | | * Note:默认重连器 |
| | | */ |
| | | public class DefaultReConnection extends AbsReconnection { |
| | | /** |
| | | * 最大连接失败次数,超过可以切换到备用的服务器地址 |
| | | */ |
| | | private static final int MAX_CONNECTION_FAILED_TIMES = 10; |
| | | /** |
| | | * 连接失败的次数 |
| | | */ |
| | | private int connectionFailedTimes = 0; |
| | | /** |
| | | * 重连间隔不能小于10秒,为了避免全部客户端socket在同一时间连接服务端,间隔时间需要上下浮动50% |
| | | */ |
| | | private long reconnectTimeDelay = 10 * 1000; |
| | | /** |
| | | * 重连线程 |
| | | */ |
| | | private HandlerThread handlerThread; |
| | | /** |
| | | * 实现延时任务的 handler |
| | | */ |
| | | private Handler handler; |
| | | |
| | | public DefaultReConnection() { |
| | | } |
| | | |
| | | @Override |
| | | public synchronized void attach(IConnectionManager iConnectionManager) { |
| | | super.attach(iConnectionManager); |
| | | if (reconnectTimeDelay < connectionManager.getOptions().getConnectTimeout()) { |
| | | reconnectTimeDelay = connectionManager.getOptions().getConnectTimeout(); |
| | | } |
| | | } |
| | | |
| | | /** |
| | | * 重连任务 |
| | | */ |
| | | private final Runnable RcConnTask = new Runnable() { |
| | | @Override |
| | | public void run() { |
| | | LogUtil.d("---> 执行重连"); |
| | | if (isDetach) { |
| | | shutDown(); |
| | | return; |
| | | } |
| | | // 是否可连接的 |
| | | if (!connectionManager.isConnectViable()) { |
| | | LogUtil.d("当前条件不允许连接"); |
| | | // 尝试再次重连 |
| | | handler.postDelayed(RcConnTask, (long) (reconnectTimeDelay * (Math.random() + 0.5))); |
| | | return; |
| | | } |
| | | // 重连 |
| | | connectionManager.connect(); |
| | | } |
| | | }; |
| | | |
| | | /** |
| | | * 进行重连 |
| | | */ |
| | | private void reconnect() { |
| | | if (handlerThread == null) { |
| | | handlerThread = new HandlerThread("re_conn"); |
| | | handlerThread.start(); |
| | | handler = new Handler(handlerThread.getLooper()); |
| | | } |
| | | LogUtil.d("重连间隔时间-->" + reconnectTimeDelay * (Math.random() + 0.5)); |
| | | handler.postDelayed(RcConnTask, (long) (reconnectTimeDelay * (Math.random() + 0.5))); |
| | | } |
| | | |
| | | |
| | | // 关闭重连线程 |
| | | private void shutDown() { |
| | | if (handlerThread != null && handlerThread.isAlive()) { |
| | | handlerThread.quit(); |
| | | handlerThread = null; |
| | | handler = null; |
| | | } |
| | | } |
| | | |
| | | @Override |
| | | public boolean equals(Object o) { |
| | | // getClass返回Class类型的对象,比较它们的类型对象是否==,其实是比较它们是否为同一个Class创建的对象 |
| | | if (o == null || getClass() != o.getClass()) return false; |
| | | return true; |
| | | } |
| | | |
| | | @Override |
| | | public void onSocketConnSuccess(SocketAddress socketAddress) { |
| | | // 连接成功关闭重连线程 |
| | | shutDown(); |
| | | } |
| | | |
| | | @Override |
| | | public void onSocketConnFail(SocketAddress socketAddress, boolean isNeedReconnect) { |
| | | // 不需要重连,则关闭重连线程 |
| | | if (!isNeedReconnect) { |
| | | shutDown(); |
| | | return; |
| | | } |
| | | connectionFailedTimes++; |
| | | |
| | | // 如果大于最大连接次数并且有备用host,则轮流切换两个host |
| | | if (connectionFailedTimes > MAX_CONNECTION_FAILED_TIMES && socketAddress.getBackupAddress() != null) { |
| | | connectionFailedTimes = 0; // 归零 |
| | | SocketAddress backupAddress = socketAddress.getBackupAddress(); |
| | | SocketAddress nowAddress = new SocketAddress(socketAddress.getIp(), socketAddress.getPort()); |
| | | backupAddress.setBackupAddress(nowAddress); |
| | | if (connectionManager.isConnectViable()) { |
| | | connectionManager.switchHost(backupAddress); |
| | | // 切换主机地址,重新连接 |
| | | reconnect(); |
| | | } |
| | | } else { |
| | | reconnect(); |
| | | } |
| | | |
| | | } |
| | | |
| | | @Override |
| | | public void onSocketDisconnect(SocketAddress socketAddress, boolean isNeedReconnect) { |
| | | // 是否需要重连 |
| | | if (!isNeedReconnect) { |
| | | shutDown(); |
| | | return; |
| | | } |
| | | reconnect(); |
| | | } |
| | | |
| | | @Override |
| | | public boolean isReconning() { |
| | | return handlerThread != null && handlerThread.isAlive(); |
| | | } |
| | | |
| | | } |
| New file |
| | |
| | | package com.easysocket.entity; |
| | | |
| | | import com.easysocket.EasySocket; |
| | | import com.easysocket.utils.Utils; |
| | | |
| | | import java.io.Serializable; |
| | | import java.nio.charset.Charset; |
| | | |
| | | /** |
| | | * Author:Alex |
| | | * Date:2019/6/1 |
| | | * Note:读到的数据 |
| | | */ |
| | | public class OriginReadData implements Serializable { |
| | | |
| | | /** |
| | | * 包头数据 |
| | | */ |
| | | private byte[] headerData; |
| | | /** |
| | | * 包体数据 |
| | | */ |
| | | private byte[] bodyData; |
| | | |
| | | public byte[] getHeaderData() { |
| | | return headerData; |
| | | } |
| | | |
| | | public void setHeaderData(byte[] headerData) { |
| | | this.headerData = headerData; |
| | | } |
| | | |
| | | public byte[] getBodyBytes() { |
| | | return bodyData; |
| | | } |
| | | |
| | | public void setBodyData(byte[] bodyData) { |
| | | this.bodyData = bodyData; |
| | | } |
| | | |
| | | /** |
| | | * 获取数据body的string |
| | | * |
| | | * @return |
| | | */ |
| | | public String getBodyString() { |
| | | return new String(getBodyBytes(), Charset.forName(EasySocket.getInstance().getDefOptions().getCharsetName())); |
| | | } |
| | | |
| | | /** |
| | | * 获取完整的数据,包括包头和包体 |
| | | * |
| | | * @return |
| | | */ |
| | | public byte[] getOriginDataBytes() { |
| | | return Utils.concatBytes(getHeaderData(), getBodyBytes()); |
| | | } |
| | | } |
| New file |
| | |
| | | package com.easysocket.entity; |
| | | |
| | | /** |
| | | * Author:Alex |
| | | * Date:2019/5/31 |
| | | * Note:socket主机地址 |
| | | */ |
| | | public class SocketAddress { |
| | | |
| | | /** |
| | | * IPV4地址 |
| | | */ |
| | | private String ip; |
| | | /** |
| | | * 连接服务器端口号 |
| | | */ |
| | | private int port; |
| | | /** |
| | | * 当此IP地址Ping不通时的备用IP |
| | | */ |
| | | private SocketAddress backupAddress; |
| | | |
| | | /** |
| | | * 获取备用的Ip和端口号 |
| | | * |
| | | * @return 备用的端口号和IP地址 |
| | | */ |
| | | public SocketAddress getBackupAddress() { |
| | | return backupAddress; |
| | | } |
| | | |
| | | /** |
| | | * 设置备用的IP和端口号,可以不设置 |
| | | * |
| | | * @param backupAddress 备用的IP和端口号信息 |
| | | */ |
| | | public void setBackupAddress(SocketAddress backupAddress) { |
| | | this.backupAddress = backupAddress; |
| | | } |
| | | |
| | | public SocketAddress(String ip, int port){ |
| | | this.ip =ip; |
| | | this.port =port; |
| | | } |
| | | |
| | | public String getIp() { |
| | | return ip; |
| | | } |
| | | |
| | | public int getPort() { |
| | | return port; |
| | | } |
| | | |
| | | } |
| New file |
| | |
| | | package com.easysocket.entity.basemsg; |
| | | |
| | | /** |
| | | * Author:Alex |
| | | * Date:2019/12/8 |
| | | * Note: |
| | | */ |
| | | public interface IResponse { |
| | | } |
| New file |
| | |
| | | package com.easysocket.entity.basemsg; |
| | | |
| | | import java.io.Serializable; |
| | | |
| | | /** |
| | | * Author:Alex |
| | | * Date:2019/6/1 |
| | | * Note:发送数据的接口 |
| | | */ |
| | | public interface ISender extends Serializable { |
| | | } |
| New file |
| | |
| | | package com.easysocket.entity.basemsg; |
| | | |
| | | /** |
| | | * Author:Alex |
| | | * Date:2019/12/7 |
| | | */ |
| | | public abstract class SuperCallbackResponse implements IResponse { |
| | | |
| | | public abstract String getCallbackId(); |
| | | |
| | | public abstract void setCallbackId(String callbackId); |
| | | |
| | | } |
| New file |
| | |
| | | package com.easysocket.entity.basemsg; |
| | | |
| | | import com.easysocket.utils.Utils; |
| | | |
| | | /** |
| | | * Author:Alex |
| | | * Date:2019/10/19 |
| | | */ |
| | | public abstract class SuperCallbackSender extends SuperSender { |
| | | |
| | | private String callbackId; |
| | | |
| | | public SuperCallbackSender() { |
| | | generateCallbackId(); |
| | | } |
| | | |
| | | public String getCallbackId() { |
| | | return callbackId; |
| | | } |
| | | |
| | | /** |
| | | * 根据自己的协议打包消息 |
| | | * |
| | | * @return |
| | | */ |
| | | public abstract byte[] pack(); |
| | | |
| | | /** |
| | | * 随机生成一个回调标识 CallbackId,在消息发送前执行,CallbackId作为消息的唯一标识一起传给服务器,服务器反馈 |
| | | * 当前消息的时候也是携带同样的CallbackId给客户端,用以识别 |
| | | */ |
| | | public void generateCallbackId() { |
| | | callbackId= Utils.getRandomChar(20); |
| | | } |
| | | } |
| | | |
| New file |
| | |
| | | package com.easysocket.entity.basemsg; |
| | | |
| | | /** |
| | | * Author:Alex |
| | | * Date:2019/10/19 |
| | | * Note:基础消息 |
| | | */ |
| | | public class SuperSender implements ISender { |
| | | } |
| New file |
| | |
| | | package com.easysocket.exception; |
| | | |
| | | /** |
| | | * Author:Alex |
| | | * Date:2019/6/5 |
| | | * Note:初始化异常 |
| | | */ |
| | | public class InitialExeption extends RuntimeException{ |
| | | public InitialExeption(String s){ |
| | | super(s); |
| | | } |
| | | } |
| New file |
| | |
| | | package com.easysocket.exception; |
| | | |
| | | /** |
| | | * Author:Alex |
| | | * Date:2019/6/5 |
| | | * Note:非空异常 |
| | | */ |
| | | public class NotNullException extends RuntimeException { |
| | | public NotNullException(String e) { |
| | | super(e); |
| | | } |
| | | } |
| New file |
| | |
| | | package com.easysocket.exception; |
| | | |
| | | /** |
| | | * Author:Alex |
| | | * Date:2019/6/5 |
| | | * Note:可恢复socket读数据异常 |
| | | */ |
| | | public class ReadRecoverableExeption extends Exception { |
| | | |
| | | public ReadRecoverableExeption(String s){ |
| | | super(s); |
| | | } |
| | | } |
| New file |
| | |
| | | package com.easysocket.exception; |
| | | |
| | | /** |
| | | * Author:Mapogo |
| | | * Date:2020/12/29 |
| | | * Note:不可修复的读取错误 |
| | | */ |
| | | public class ReadUnrecoverableException extends Exception { |
| | | public ReadUnrecoverableException(String s) { |
| | | super(s); |
| | | } |
| | | } |
| New file |
| | |
| | | package com.easysocket.exception; |
| | | |
| | | /** |
| | | * Author:Alex |
| | | * Date:2019/6/4 |
| | | * Note:请求取消异常 |
| | | */ |
| | | public class RequestCancelException extends Exception{ |
| | | |
| | | public RequestCancelException(String s){ |
| | | super(s); |
| | | } |
| | | } |
| New file |
| | |
| | | package com.easysocket.exception; |
| | | |
| | | /** |
| | | * Author:Alex |
| | | * Date:2019/6/4 |
| | | * Note:请求超时异常 |
| | | */ |
| | | public class RequestTimeOutException extends Exception{ |
| | | |
| | | public RequestTimeOutException(String s){ |
| | | super(s); |
| | | } |
| | | } |
| New file |
| | |
| | | package com.easysocket.interfaces.callback; |
| | | |
| | | import com.easysocket.callback.SuperCallBack; |
| | | |
| | | /** |
| | | * Author:Alex |
| | | * Date:2019/6/5 |
| | | * Note: |
| | | */ |
| | | public interface ICallBack { |
| | | /** |
| | | * socket请求回调 |
| | | * @param callBack |
| | | */ |
| | | void onCallBack(SuperCallBack callBack); |
| | | |
| | | |
| | | } |
| New file |
| | |
| | | /* |
| | | * Copyright (C) 2017 zhouyou(478319399@qq.com) |
| | | * |
| | | * Licensed under the Apache License, Version 2.0 (the "License"); |
| | | * you may not use this file except in compliance with the License. |
| | | * You may obtain a copy of the License at |
| | | * |
| | | * http://www.apache.org/licenses/LICENSE-2.0 |
| | | * |
| | | * Unless required by applicable law or agreed to in writing, software |
| | | * distributed under the License is distributed on an "AS IS" BASIS, |
| | | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| | | * See the License for the specific language governing permissions and |
| | | * limitations under the License. |
| | | */ |
| | | |
| | | package com.easysocket.interfaces.callback; |
| | | |
| | | import android.app.Dialog; |
| | | |
| | | /** |
| | | * <p>描述:自定义对话框的dialog</p> |
| | | */ |
| | | public interface IProgressDialog { |
| | | Dialog getDialog(); |
| | | } |
| New file |
| | |
| | | /* |
| | | * Copyright (C) 2017 zhouyou(478319399@qq.com) |
| | | * |
| | | * Licensed under the Apache License, Version 2.0 (the "License"); |
| | | * you may not use this file except in compliance with the License. |
| | | * You may obtain a copy of the License at |
| | | * |
| | | * http://www.apache.org/licenses/LICENSE-2.0 |
| | | * |
| | | * Unless required by applicable law or agreed to in writing, software |
| | | * distributed under the License is distributed on an "AS IS" BASIS, |
| | | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| | | * See the License for the specific language governing permissions and |
| | | * limitations under the License. |
| | | */ |
| | | |
| | | package com.easysocket.interfaces.callback; |
| | | |
| | | import java.lang.reflect.Type; |
| | | |
| | | /** |
| | | * <p>描述:获取类型接口</p> |
| | | */ |
| | | public interface IType<T> { |
| | | |
| | | Type getType(); |
| | | |
| | | Class<?> getGenericityClazz(); |
| | | } |
| New file |
| | |
| | | /* |
| | | * Copyright (C) 2017 zhouyou(478319399@qq.com) |
| | | * |
| | | * Licensed under the Apache License, Version 2.0 (the "License"); |
| | | * you may not use this file except in compliance with the License. |
| | | * You may obtain a copy of the License at |
| | | * |
| | | * http://www.apache.org/licenses/LICENSE-2.0 |
| | | * |
| | | * Unless required by applicable law or agreed to in writing, software |
| | | * distributed under the License is distributed on an "AS IS" BASIS, |
| | | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| | | * See the License for the specific language governing permissions and |
| | | * limitations under the License. |
| | | */ |
| | | |
| | | package com.easysocket.interfaces.callback; |
| | | |
| | | /** |
| | | * <p>描述:进度框取消监听</p> |
| | | */ |
| | | public interface ProgressCancelListener { |
| | | void onCancelProgress(); |
| | | } |
| New file |
| | |
| | | package com.easysocket.interfaces.config; |
| | | |
| | | import com.easysocket.entity.SocketAddress; |
| | | import com.easysocket.interfaces.conn.IConnectionManager; |
| | | |
| | | /** |
| | | * Author:Alex |
| | | * Date:2019/6/4 |
| | | * Note: |
| | | */ |
| | | public interface IConnectionSwitchListener { |
| | | void onSwitchConnectionInfo(IConnectionManager manager, SocketAddress oldAddress, SocketAddress newAddress); |
| | | } |
| New file |
| | |
| | | package com.easysocket.interfaces.config; |
| | | |
| | | import java.nio.ByteOrder; |
| | | |
| | | /** |
| | | * 消息数据格式 |
| | | */ |
| | | public interface IMessageProtocol { |
| | | |
| | | /** |
| | | * 获取包头的长度 |
| | | */ |
| | | int getHeaderLength(); |
| | | |
| | | /** |
| | | * 获取数据包体的长度,根据协议这个长度应该写在包头中,在读取数据时用到 |
| | | */ |
| | | int getBodyLength(byte[] header, ByteOrder byteOrder); |
| | | |
| | | } |
| New file |
| | |
| | | package com.easysocket.interfaces.config; |
| | | |
| | | import com.easysocket.config.EasySocketOptions; |
| | | |
| | | /** |
| | | * Author:Alex |
| | | * Date:2019/6/1 |
| | | * Note: |
| | | */ |
| | | public interface IOptions<T> { |
| | | /** |
| | | * 设置配置信息 |
| | | * @param socketOptions |
| | | */ |
| | | T setOptions(EasySocketOptions socketOptions); |
| | | |
| | | /** |
| | | * 获取配置信息 |
| | | * @return |
| | | */ |
| | | EasySocketOptions getOptions(); |
| | | } |
| New file |
| | |
| | | package com.easysocket.interfaces.conn; |
| | | |
| | | import com.easysocket.entity.SocketAddress; |
| | | import com.easysocket.interfaces.callback.ICallBack; |
| | | import com.easysocket.interfaces.config.IOptions; |
| | | |
| | | import java.io.InputStream; |
| | | import java.io.OutputStream; |
| | | |
| | | /** |
| | | * Author:Alex |
| | | * Date:2019/5/29 |
| | | * Note:连接管理的接口规范 |
| | | */ |
| | | public interface IConnectionManager extends ISubscribeSocketAction, IOptions<IConnectionManager>,ISend, ICallBack { |
| | | /** |
| | | * 开始连接 |
| | | */ |
| | | void connect(); |
| | | |
| | | /** |
| | | * 关闭连接 |
| | | * @param isNeedReconnect 是否需要重连 |
| | | */ |
| | | void disconnect(boolean isNeedReconnect); |
| | | |
| | | |
| | | /** |
| | | * 获取socket连接状态 |
| | | * @return |
| | | */ |
| | | int getConnectionStatus(); |
| | | |
| | | /** |
| | | * 是否可连接的 |
| | | * @return |
| | | */ |
| | | boolean isConnectViable(); |
| | | |
| | | /** |
| | | * 切换host |
| | | * @param socketAddress |
| | | */ |
| | | void switchHost(SocketAddress socketAddress); |
| | | |
| | | /** |
| | | * 获取输入流 |
| | | * @return |
| | | */ |
| | | InputStream getInputStream(); |
| | | |
| | | /** |
| | | * 获取输出流 |
| | | * @return |
| | | */ |
| | | OutputStream getOutStream(); |
| | | |
| | | /** |
| | | * 获取心跳管理器 |
| | | * @return |
| | | */ |
| | | IHeartManager getHeartManager(); |
| | | |
| | | |
| | | } |
| New file |
| | |
| | | package com.easysocket.interfaces.conn; |
| | | |
| | | import com.easysocket.connection.heartbeat.HeartManager; |
| | | |
| | | /** |
| | | * Author:Alex |
| | | * Date:2019/12/8 |
| | | * Note: |
| | | */ |
| | | public interface IHeartManager { |
| | | |
| | | /** |
| | | * 开始心跳 |
| | | * @param clientHeart |
| | | */ |
| | | void startHeartbeat(byte[] clientHeart, HeartManager.HeartbeatListener listener); |
| | | |
| | | /** |
| | | * 停止心跳 |
| | | */ |
| | | void stopHeartbeat(); |
| | | |
| | | |
| | | /** |
| | | * 接收到心跳 |
| | | */ |
| | | void onReceiveHeartBeat(); |
| | | } |
| New file |
| | |
| | | package com.easysocket.interfaces.conn; |
| | | |
| | | /** |
| | | * Author:Alex |
| | | * Date:2019/6/1 |
| | | * Note: |
| | | */ |
| | | public interface IReconnListener { |
| | | |
| | | /** |
| | | * 关联连接器 |
| | | * @param iConnectionManager |
| | | */ |
| | | void attach(IConnectionManager iConnectionManager); |
| | | |
| | | /** |
| | | * 分离连接器 |
| | | */ |
| | | void detach(); |
| | | } |
| New file |
| | |
| | | package com.easysocket.interfaces.conn; |
| | | |
| | | import com.easysocket.entity.basemsg.SuperCallbackSender; |
| | | |
| | | /** |
| | | * Author:Alex |
| | | * Date:2019/6/5 |
| | | * Note:发送接口 |
| | | */ |
| | | public interface ISend { |
| | | |
| | | /** |
| | | * 发送一个有回调的消息 |
| | | * @param sender |
| | | * @return |
| | | */ |
| | | IConnectionManager upCallbackMessage(SuperCallbackSender sender); |
| | | |
| | | /** |
| | | * 发送bytes |
| | | * @param bytes |
| | | * @return |
| | | */ |
| | | IConnectionManager upBytes(byte[] bytes); |
| | | } |
| New file |
| | |
| | | package com.easysocket.interfaces.conn; |
| | | |
| | | import java.io.Serializable; |
| | | |
| | | /** |
| | | * Author:Alex |
| | | * Date:2019/6/1 |
| | | * Note:socket行为分发接口 |
| | | */ |
| | | public interface ISocketActionDispatch { |
| | | /** |
| | | * 停止分发线程 |
| | | */ |
| | | void stopDispatchThread(); |
| | | |
| | | void startDispatchThread(); |
| | | |
| | | void dispatchAction(String action); |
| | | |
| | | /** |
| | | * socket行为的分发 |
| | | * @param action |
| | | * @param serializable |
| | | */ |
| | | void dispatchAction(String action, Serializable serializable); |
| | | |
| | | /** |
| | | * 订阅socket行为 |
| | | * @param iSocketActionListener |
| | | */ |
| | | void subscribe(ISocketActionListener iSocketActionListener); |
| | | |
| | | /** |
| | | * 解除socket行为的订阅 |
| | | * @param iSocketActionListener |
| | | */ |
| | | void unsubscribe(ISocketActionListener iSocketActionListener); |
| | | } |
| New file |
| | |
| | | package com.easysocket.interfaces.conn; |
| | | |
| | | import com.easysocket.entity.OriginReadData; |
| | | import com.easysocket.entity.SocketAddress; |
| | | |
| | | /** |
| | | * Author:Alex |
| | | * Date:2019/6/1 |
| | | * Note:socket行为监听接口 |
| | | */ |
| | | public interface ISocketActionListener { |
| | | /** |
| | | * socket连接成功 |
| | | * @param socketAddress |
| | | */ |
| | | void onSocketConnSuccess(SocketAddress socketAddress); |
| | | |
| | | /** |
| | | * socket连接失败 |
| | | * @param socketAddress |
| | | * @param isNeedReconnect 是否需要重连 |
| | | */ |
| | | void onSocketConnFail(SocketAddress socketAddress, boolean isNeedReconnect); |
| | | |
| | | /** |
| | | * 断开socket连接 |
| | | * @param socketAddress |
| | | * @param isNeedReconnect 是否需要重连 |
| | | */ |
| | | void onSocketDisconnect(SocketAddress socketAddress, boolean isNeedReconnect); |
| | | |
| | | /** |
| | | * socket数据响应 |
| | | * @param socketAddress |
| | | * @param originReadData |
| | | */ |
| | | void onSocketResponse(SocketAddress socketAddress, OriginReadData originReadData); |
| | | |
| | | /** |
| | | * socket数据响应 |
| | | * @param socketAddress |
| | | * @param readData |
| | | */ |
| | | void onSocketResponse(SocketAddress socketAddress, String readData); |
| | | |
| | | /** |
| | | * socket数据响应 |
| | | * @param socketAddress |
| | | * @param readData |
| | | */ |
| | | void onSocketResponse(SocketAddress socketAddress, byte[] readData); |
| | | } |
| New file |
| | |
| | | package com.easysocket.interfaces.conn; |
| | | |
| | | /** |
| | | * Author:Alex |
| | | * Date:2019/6/1 |
| | | * Note:订阅监听socket |
| | | */ |
| | | public interface ISubscribeSocketAction { |
| | | /** |
| | | * 注册监听socket的行为 |
| | | * @param iSocketActionListener |
| | | */ |
| | | void subscribeSocketAction(ISocketActionListener iSocketActionListener); |
| | | |
| | | /** |
| | | * 注销监听socket的行为 |
| | | * @param iSocketActionListener |
| | | */ |
| | | void unSubscribeSocketAction(ISocketActionListener iSocketActionListener); |
| | | } |
| New file |
| | |
| | | package com.easysocket.interfaces.conn; |
| | | |
| | | import com.easysocket.entity.OriginReadData; |
| | | import com.easysocket.entity.SocketAddress; |
| | | |
| | | /** |
| | | * Author:Alex |
| | | * Date:2019/6/4 |
| | | * Note:socket行为监听的抽象类,继承此类可以选择性地重写方法 |
| | | */ |
| | | public abstract class SocketActionListener implements ISocketActionListener{ |
| | | /** |
| | | * socket连接成功 |
| | | * @param socketAddress |
| | | */ |
| | | @Override |
| | | public void onSocketConnSuccess(SocketAddress socketAddress) { |
| | | |
| | | } |
| | | /** |
| | | * socket连接失败 |
| | | * @param socketAddress |
| | | * @param isNeedReconnect 是否需要重连 |
| | | */ |
| | | @Override |
| | | public void onSocketConnFail(SocketAddress socketAddress, boolean isNeedReconnect) { |
| | | |
| | | } |
| | | /** |
| | | * 断开socket连接 |
| | | * @param socketAddress |
| | | * @param isNeedReconnect 是否需要重连 |
| | | */ |
| | | @Override |
| | | public void onSocketDisconnect(SocketAddress socketAddress, boolean isNeedReconnect) { |
| | | |
| | | } |
| | | /** |
| | | * socket读数据反馈 |
| | | * @param socketAddress |
| | | * @param originReadData |
| | | */ |
| | | @Override |
| | | public void onSocketResponse(SocketAddress socketAddress, OriginReadData originReadData) { |
| | | |
| | | } |
| | | |
| | | @Override |
| | | public void onSocketResponse(SocketAddress socketAddress, byte[] readData) { |
| | | |
| | | } |
| | | |
| | | @Override |
| | | public void onSocketResponse(SocketAddress socketAddress, String readData) { |
| | | |
| | | } |
| | | } |
| New file |
| | |
| | | package com.easysocket.interfaces.io; |
| | | |
| | | /** |
| | | * Author:Alex |
| | | * Date:2019/6/1 |
| | | * Note: |
| | | */ |
| | | public interface IIOManager { |
| | | |
| | | /** |
| | | * 发送字节流 |
| | | * |
| | | * @param bytes |
| | | */ |
| | | void sendBytes(byte[] bytes); |
| | | |
| | | /** |
| | | * 关闭io管理器 |
| | | */ |
| | | void closeIO(); |
| | | |
| | | /** |
| | | * 开启io操作 |
| | | */ |
| | | void startIO(); |
| | | |
| | | } |
| New file |
| | |
| | | package com.easysocket.interfaces.io; |
| | | |
| | | /** |
| | | * Author:Alex |
| | | * Date:2019/6/1 |
| | | * Note: |
| | | */ |
| | | public interface IReader<T> { |
| | | |
| | | /** |
| | | * 读数据 |
| | | */ |
| | | void read() throws Exception; |
| | | |
| | | /** |
| | | * 打开数据的读取 |
| | | */ |
| | | void openReader(); |
| | | |
| | | /** |
| | | * 关闭数据的读取 |
| | | */ |
| | | void closeReader(); |
| | | |
| | | /** |
| | | * 设置参数 |
| | | * @param t |
| | | */ |
| | | void setOption(T t); |
| | | |
| | | |
| | | } |
| New file |
| | |
| | | package com.easysocket.interfaces.io; |
| | | |
| | | import java.io.IOException; |
| | | |
| | | /** |
| | | * Author:Alex |
| | | * Date:2019/6/1 |
| | | * Note: |
| | | */ |
| | | public interface IWriter<T> { |
| | | /** |
| | | * 保存要写的数据 |
| | | */ |
| | | void offer(byte[] sender); |
| | | |
| | | /** |
| | | * 写数据 |
| | | * @param sender |
| | | */ |
| | | void write(byte[] sender) throws IOException; |
| | | |
| | | /** |
| | | * 关闭stream |
| | | */ |
| | | void closeWriter(); |
| | | |
| | | /** |
| | | * 开启写数据 |
| | | */ |
| | | void openWriter(); |
| | | |
| | | /** |
| | | * 设置参数 |
| | | * @param t |
| | | */ |
| | | void setOption(T t); |
| | | |
| | | } |
| New file |
| | |
| | | package com.easysocket.utils; |
| | | |
| | | import java.math.BigInteger; |
| | | import java.util.Arrays; |
| | | import java.util.Collections; |
| | | import java.util.List; |
| | | |
| | | /** |
| | | * Copyright (C), 2022, |
| | | * Author: zuo |
| | | * Date: 2022/3/9 14:55 |
| | | * Description: |
| | | */ |
| | | public class HexUtil { |
| | | |
| | | |
| | | /** |
| | | * hex字符串转byte数组 |
| | | * |
| | | * @param inHex 待转换的Hex字符串 |
| | | * @return 转换后的byte数组结果 |
| | | */ |
| | | public static byte[] hexToByteArray(String inHex) { |
| | | int hexlen = inHex.length(); |
| | | byte[] result; |
| | | if (hexlen % 2 == 1) { |
| | | //奇数 |
| | | hexlen++; |
| | | result = new byte[(hexlen / 2)]; |
| | | inHex = "0" + inHex; |
| | | } else { |
| | | //偶数 |
| | | result = new byte[(hexlen / 2)]; |
| | | } |
| | | int j = 0; |
| | | for (int i = 0; i < hexlen; i += 2) { |
| | | result[j] = hexToByte(inHex.substring(i, i + 2)); |
| | | j++; |
| | | } |
| | | return result; |
| | | } |
| | | |
| | | |
| | | /** |
| | | * Hex字符串转byte |
| | | * |
| | | * @param inHex 待转换的Hex字符串 |
| | | * @return 转换后的byte |
| | | */ |
| | | public static byte hexToByte(String inHex) { |
| | | return (byte) Integer.parseInt(inHex, 16); |
| | | } |
| | | |
| | | /** |
| | | * 字节转十六进制 |
| | | * |
| | | * @param b 需要进行转换的byte字节 |
| | | * @return 转换后的Hex字符串 |
| | | */ |
| | | public static String byteToHex(byte b) { |
| | | String hex = Integer.toHexString(b & 0xFF); |
| | | if (hex.length() < 2) { |
| | | hex = "0" + hex; |
| | | } |
| | | return hex.toUpperCase(); |
| | | } |
| | | |
| | | |
| | | /** |
| | | * 字节数组转16进制 |
| | | * |
| | | * @param bytes 需要转换的byte数组 |
| | | * @return 转换后的Hex字符串 |
| | | */ |
| | | public static String bytesToHex(byte[] bytes) { |
| | | try { |
| | | StringBuffer sb = new StringBuffer(); |
| | | for (int i = 0; i < bytes.length; i++) { |
| | | String hex = Integer.toHexString(bytes[i] & 0xFF); |
| | | if (hex.length() < 2) { |
| | | sb.append(0); |
| | | } |
| | | sb.append(hex); |
| | | } |
| | | return sb.toString(); |
| | | } catch (Exception e) { |
| | | e.printStackTrace(); |
| | | } |
| | | return ""; |
| | | } |
| | | |
| | | public static String byteArrayToHexString(byte[] byteArray) { |
| | | StringBuilder hexString = new StringBuilder(); |
| | | for (byte b : byteArray) { |
| | | // 将字节转换为无符号整数 |
| | | int unsignedInt = b & 0xff; |
| | | // 将无符号整数转换为16进制字符串 |
| | | String hex = Integer.toHexString(unsignedInt); |
| | | // 如果字符串长度小于2,在前面补0 |
| | | if (hex.length() < 2) { |
| | | hex = "0" + hex; |
| | | } |
| | | hexString.append(hex); |
| | | } |
| | | return hexString.toString(); |
| | | } |
| | | |
| | | /** |
| | | * 字节数组转16进制 不在末尾添加0 |
| | | * |
| | | * @param bytes 需要转换的byte数组 |
| | | * @return 转换后的Hex字符串 |
| | | */ |
| | | public static String bytesToHexNoAddZero(byte[] bytes) { |
| | | StringBuffer sb = new StringBuffer(); |
| | | for (int i = 0; i < bytes.length; i++) { |
| | | String hex = Integer.toHexString(bytes[i] & 0xFF); |
| | | sb.append(hex); |
| | | } |
| | | return sb.toString(); |
| | | } |
| | | |
| | | |
| | | /** |
| | | * 将 4字节的16进制字符串,转换为32位带符号的十进制浮点型 |
| | | * |
| | | * @param str 4字节 16进制字符 |
| | | * @return |
| | | */ |
| | | public static float hexToFloat(String str) { |
| | | return Float.intBitsToFloat(new BigInteger(str, 16).intValue()); |
| | | } |
| | | |
| | | /** |
| | | * 将带符号的32位浮点数装换为16进制 |
| | | * |
| | | * @param value |
| | | * @return |
| | | */ |
| | | public static String folatToHexString(Float value) { |
| | | return Integer.toHexString(Float.floatToIntBits(value)); |
| | | } |
| | | |
| | | /** |
| | | * 十进制转16进制 |
| | | * |
| | | * @param number |
| | | * @return |
| | | */ |
| | | public static String get10to16(int number) { |
| | | return Integer.toHexString(number); |
| | | } |
| | | |
| | | /** |
| | | * 十进制转16进制 补齐偶数 高位在前低位在后 |
| | | * |
| | | * @param number |
| | | * @return |
| | | */ |
| | | public static String get10to16CompleteHex(int number) { |
| | | String hex = Integer.toHexString(number); |
| | | if (hex.length() % 2 == 0) { |
| | | return hex; |
| | | } else { |
| | | return "0" + hex; |
| | | } |
| | | } |
| | | |
| | | /** |
| | | * 十进制转16进制低位在前高位在后 |
| | | * |
| | | * @param number 十进制数 |
| | | * @param length 补足多少位 |
| | | * @return |
| | | */ |
| | | public static String get10to16LowHigh(int number, int length) { |
| | | String str = ""; |
| | | try { |
| | | str = Integer.toHexString(number); |
| | | str = getHexToLenght(str, length); |
| | | str = spaceHex(str); |
| | | str = HighLowHex(str); |
| | | |
| | | } catch (Exception e) { |
| | | e.printStackTrace(); |
| | | } |
| | | return str; |
| | | } |
| | | |
| | | /** |
| | | * 16进制转10进制高低位转换 |
| | | * |
| | | * @param hex |
| | | * @return |
| | | */ |
| | | public static int get16to10LowHigh(String hex) { |
| | | try { |
| | | String str = ""; |
| | | str = spaceHex(hex); |
| | | str = HighLowHex(str); |
| | | return Integer.parseInt(str, 16); |
| | | } catch (NumberFormatException e) { |
| | | e.printStackTrace(); |
| | | } |
| | | return 0; |
| | | } |
| | | |
| | | |
| | | /** |
| | | * 返回特定长度的16进制字符串 |
| | | * |
| | | * @param data |
| | | * @param length |
| | | * @return |
| | | */ |
| | | public static String getHexToLenght(String data, int length) { |
| | | StringBuffer stringBuilder = new StringBuffer(data); |
| | | for (int i = 0; i < length - data.length(); i++) { |
| | | stringBuilder.insert(0, "0"); |
| | | } |
| | | return stringBuilder.toString(); |
| | | } |
| | | |
| | | /** |
| | | * 十六进制数隔空位 |
| | | * |
| | | * @param str |
| | | * @return |
| | | */ |
| | | public static String spaceHex(String str) { |
| | | char[] array = str.toCharArray(); |
| | | if (str.length() <= 2) return str; |
| | | StringBuffer bufferHex = new StringBuffer(); |
| | | for (int i = 0; i < array.length; i++) { |
| | | int start = i + 1; |
| | | if (start % 2 == 0) { |
| | | bufferHex.append(array[i]).append(" "); |
| | | } else { |
| | | bufferHex.append(array[i]); |
| | | } |
| | | } |
| | | return bufferHex.toString(); |
| | | } |
| | | |
| | | /** |
| | | * 高位16进制转低位 |
| | | * |
| | | * @param str |
| | | * @return |
| | | */ |
| | | private static String HighLowHex(String str) { |
| | | if (str.trim().length() <= 2) return str; |
| | | List<String> list = Arrays.asList(str.split(" ")); |
| | | Collections.reverse(list); |
| | | StringBuffer stringBuffer = new StringBuffer(); |
| | | for (String string : list) { |
| | | stringBuffer.append(string); |
| | | } |
| | | return stringBuffer.toString(); |
| | | } |
| | | |
| | | /** |
| | | * @param hex |
| | | * @return |
| | | */ |
| | | public static int get16to10(String hex) { |
| | | int x = 0; |
| | | try { |
| | | x = Integer.parseInt(hex, 16); |
| | | } catch (NumberFormatException e) { |
| | | e.printStackTrace(); |
| | | } |
| | | return x; |
| | | } |
| | | } |
| New file |
| | |
| | | package com.easysocket.utils; |
| | | import android.util.Log; |
| | | |
| | | import com.easysocket.EasySocket; |
| | | |
| | | |
| | | public class LogUtil { |
| | | public static final String LOGTAG = "easysocket"; |
| | | public static boolean debugEnabled = EasySocket.getInstance().getDefOptions().isDebug(); |
| | | |
| | | public LogUtil() { |
| | | } |
| | | |
| | | private static String getDebugInfo() { |
| | | Throwable stack = new Throwable().fillInStackTrace(); |
| | | StackTraceElement[] trace = stack.getStackTrace(); |
| | | int n = 2; |
| | | return trace[n].getClassName() + " " + trace[n].getMethodName() + "()" + ":" + trace[n].getLineNumber() + |
| | | " "; |
| | | } |
| | | |
| | | private static String getLogInfoByArray(String[] infos) { |
| | | StringBuilder sb = new StringBuilder(); |
| | | for (String info : infos) { |
| | | sb.append(info); |
| | | sb.append(" "); |
| | | } |
| | | return sb.toString(); |
| | | } |
| | | |
| | | public static void i(String... s) { |
| | | if (debugEnabled) { |
| | | i(LOGTAG, getDebugInfo() + getLogInfoByArray(s)); |
| | | } |
| | | } |
| | | |
| | | public static void e(Throwable tr) { |
| | | if (debugEnabled) { |
| | | Log.e(LOGTAG, getDebugInfo() ,tr); |
| | | } |
| | | } |
| | | |
| | | public static void e(String... s) { |
| | | if (debugEnabled) { |
| | | e(LOGTAG, getDebugInfo() + getLogInfoByArray(s)); |
| | | } |
| | | } |
| | | |
| | | public static void d(String... s) { |
| | | if (debugEnabled) { |
| | | d(LOGTAG, getDebugInfo() + getLogInfoByArray(s)); |
| | | } |
| | | } |
| | | |
| | | public static void v(String... s) { |
| | | if (debugEnabled) { |
| | | v(LOGTAG, getDebugInfo() + getLogInfoByArray(s)); |
| | | } |
| | | } |
| | | |
| | | public static void w(String... s) { |
| | | if (debugEnabled) { |
| | | w(LOGTAG, getDebugInfo() + getLogInfoByArray(s)); |
| | | } |
| | | } |
| | | |
| | | private static void i(String name, String log) { |
| | | System.out.println(name + ":" + log); |
| | | } |
| | | |
| | | private static void d(String name, String log) { |
| | | System.out.println(name + ":" + log); |
| | | } |
| | | |
| | | private static void v(String name, String log) { |
| | | System.out.println(name + ":" + log); |
| | | } |
| | | |
| | | private static void e(String name, String log) { |
| | | System.err.println(name + ":" + log); |
| | | } |
| | | |
| | | private static void w(String name, String log) { |
| | | System.err.println(name + ":" + log); |
| | | } |
| | | |
| | | } |
| New file |
| | |
| | | package com.easysocket.utils; |
| | | |
| | | import android.content.Context; |
| | | import android.net.ConnectivityManager; |
| | | import android.net.NetworkInfo; |
| | | import android.os.Handler; |
| | | import android.os.Looper; |
| | | |
| | | import java.lang.reflect.ParameterizedType; |
| | | import java.lang.reflect.Type; |
| | | import java.lang.reflect.TypeVariable; |
| | | import java.util.Random; |
| | | |
| | | /** |
| | | * Created by LXR ON 2018/8/30. |
| | | */ |
| | | public class Utils { |
| | | |
| | | /** |
| | | * 获取泛型参数的类型 |
| | | * |
| | | * @param <T> |
| | | * @return |
| | | */ |
| | | public static <T> Type findGenericityType(Class<T> cls) { |
| | | Type genType = cls.getGenericSuperclass(); //返回直接继承的父类(包含泛型参数)类型,如果有泛型T,也要包括进去 |
| | | //getActualTypeArguments 获取泛型中的实际类型,比如Map<Sting,String>中的String类型 |
| | | Type[] params = ((ParameterizedType) genType).getActualTypeArguments(); |
| | | Type type = params[0]; //泛型的实际类型 |
| | | Type finalNeedType; |
| | | if (type instanceof ParameterizedType) { //二级泛型,这里就处理最多二级吧,形如 A<B<T>>,两个<> |
| | | finalNeedType = ((ParameterizedType) type).getActualTypeArguments()[0]; |
| | | } else { // 一级泛型,形如A<T> |
| | | finalNeedType = type; |
| | | } |
| | | //如果泛型类型还是变量类型,比如T、V之类的,代表没有填写泛型参数 |
| | | if (finalNeedType instanceof TypeVariable) throw new IllegalStateException("没有填写泛型参数"); |
| | | return finalNeedType; |
| | | } |
| | | |
| | | /** |
| | | * 字符串是否为空 |
| | | * |
| | | * @param str |
| | | * @return |
| | | */ |
| | | public static boolean isStringEmpty(String str) { |
| | | return str == null || str.trim().length() == 0; |
| | | } |
| | | |
| | | /** |
| | | * 生成随机字符串 |
| | | * |
| | | * @param length |
| | | * @return |
| | | */ |
| | | public static String getRandomChar(int length) { |
| | | char[] chr = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', |
| | | 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z'}; |
| | | Random random = new Random(); |
| | | StringBuffer buffer = new StringBuffer(); |
| | | for (int i = 0; i < length; i++) { |
| | | buffer.append(chr[random.nextInt(36)]); |
| | | } |
| | | return buffer.toString(); |
| | | } |
| | | |
| | | |
| | | /** |
| | | * 获取handler对象 |
| | | * |
| | | * @param isMainHandler 是否为主线程的handler,为false时返回的是当前线程handler |
| | | * @return |
| | | */ |
| | | public static Handler getHandler(boolean isMainHandler) { |
| | | Handler handler; |
| | | if (isMainHandler) { |
| | | handler = new Handler(Looper.getMainLooper()); |
| | | } else { |
| | | Looper.prepare(); |
| | | handler = new Handler(); |
| | | } |
| | | return handler; |
| | | } |
| | | |
| | | /** |
| | | * 睡眠多少毫秒 |
| | | * |
| | | * @param milliSecond 毫秒 |
| | | */ |
| | | public static void sleep(long milliSecond) { |
| | | try { |
| | | Thread.sleep(milliSecond); |
| | | } catch (InterruptedException e) { |
| | | e.printStackTrace(); |
| | | } |
| | | } |
| | | |
| | | /** |
| | | * 非空检查 |
| | | * |
| | | * @param object |
| | | * @param emsg |
| | | * @throws |
| | | */ |
| | | public static void checkNotNull(Object object, String emsg) { |
| | | try { |
| | | if (object == null) { |
| | | throw new Exception(emsg); |
| | | } |
| | | } catch (Exception e) { |
| | | e.printStackTrace(); |
| | | } |
| | | } |
| | | |
| | | public static void throwNotNull(Object object, String emsg) throws Exception { |
| | | if (object == null) { |
| | | throw new Exception(emsg); |
| | | } |
| | | } |
| | | |
| | | // 判断是否连接网络 |
| | | public static boolean isNetConnected(Context context) { |
| | | ConnectivityManager cm = (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE); |
| | | NetworkInfo info = cm.getActiveNetworkInfo(); |
| | | return info != null && info.isConnected(); |
| | | } |
| | | |
| | | /** |
| | | * 拼接两个byte[] |
| | | * |
| | | * @param |
| | | * @param |
| | | * @return |
| | | */ |
| | | public static byte[] concatBytes(byte[] bt1, byte[] bt2) { |
| | | if (bt1 == null) { |
| | | return bt2; |
| | | } |
| | | if (bt2 == null) { |
| | | return bt1; |
| | | } |
| | | byte[] bt3 = new byte[bt1.length + bt2.length]; |
| | | System.arraycopy(bt1, 0, bt3, 0, bt1.length); |
| | | System.arraycopy(bt2, 0, bt3, bt1.length, bt2.length); |
| | | return bt3; |
| | | } |
| | | |
| | | } |
| | |
| | | } |
| | | } |
| | | rootProject.name = "chargeQh" |
| | | include ':app',':pickerviewlibrary' |
| | | include ':app',':pickerviewlibrary',':easysocket' |