first commit
This commit is contained in:
4
easysocket/src/main/AndroidManifest.xml
Normal file
4
easysocket/src/main/AndroidManifest.xml
Normal file
@@ -0,0 +1,4 @@
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
package="com.easysocket" >
|
||||
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
|
||||
</manifest>
|
||||
136
easysocket/src/main/java/com/easysocket/ConnectionHolder.java
Normal file
136
easysocket/src/main/java/com/easysocket/ConnectionHolder.java
Normal file
@@ -0,0 +1,136 @@
|
||||
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 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) {
|
||||
// 首先断开连接,销毁相关线程和资源
|
||||
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]));
|
||||
}
|
||||
|
||||
}
|
||||
335
easysocket/src/main/java/com/easysocket/EasySocket.java
Normal file
335
easysocket/src/main/java/com/easysocket/EasySocket.java
Normal file
@@ -0,0 +1,335 @@
|
||||
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;
|
||||
|
||||
/**
|
||||
* 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) {
|
||||
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() {
|
||||
// 断开连接
|
||||
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);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,116 @@
|
||||
|
||||
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("网络请求被取消"));
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
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) {
|
||||
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -0,0 +1,44 @@
|
||||
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);
|
||||
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
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);
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
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
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
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];
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,478 @@
|
||||
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;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
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;
|
||||
}
|
||||
@@ -0,0 +1,81 @@
|
||||
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;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
package com.easysocket.connection.action;
|
||||
|
||||
/**
|
||||
* Author:Alex
|
||||
* Date:2019/6/3
|
||||
* Note:
|
||||
*/
|
||||
public interface IOAction {
|
||||
// 收到消息响应
|
||||
String ACTION_READ_COMPLETE = "action_read_complete";
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
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";
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
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;
|
||||
}
|
||||
@@ -0,0 +1,340 @@
|
||||
package com.easysocket.connection.connect;
|
||||
|
||||
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() {
|
||||
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();
|
||||
LogUtil.d("---> 关闭socket连接");
|
||||
connectionStatus.set(SocketStatus.SOCKET_DISCONNECTED);
|
||||
actionDispatcher.dispatchAction(SocketAction.ACTION_DISCONNECTION, new Boolean(isNeedReconnect));
|
||||
} 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连接成功");
|
||||
actionDispatcher.dispatchAction(SocketAction.ACTION_CONN_SUCCESS);
|
||||
connectionStatus.set(SocketStatus.SOCKET_CONNECTED);
|
||||
openSocketManager();
|
||||
}
|
||||
|
||||
// 开启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) {
|
||||
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;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,141 @@
|
||||
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;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,187 @@
|
||||
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() {
|
||||
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();
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 关闭线程
|
||||
*/
|
||||
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();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
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);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,221 @@
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,181 @@
|
||||
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);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,327 @@
|
||||
package com.easysocket.connection.iowork;
|
||||
|
||||
import com.easysocket.config.EasySocketOptions;
|
||||
import com.easysocket.connection.action.IOAction;
|
||||
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.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收到数据-->" + originalData.getBodyString());
|
||||
// 分发数据
|
||||
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收到数据-->" + originalData.getBodyString());
|
||||
// 分发
|
||||
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();
|
||||
// 重连
|
||||
connectionManager.disconnect(true);
|
||||
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
// 重连
|
||||
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收到数据-->" + readData.getBodyString());
|
||||
// 分发数据
|
||||
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()) {
|
||||
stopThread = true;
|
||||
readerThread.interrupt();
|
||||
readerThread.join();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,146 @@
|
||||
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.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("Socket发送数据String-->" + new String(sendBytes, Charset.forName("utf-8")));
|
||||
LogUtil.d("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()) {
|
||||
isStop = true;
|
||||
writerThread.interrupt();
|
||||
writerThread.join();
|
||||
writerThread = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,98 @@
|
||||
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.");
|
||||
// }
|
||||
// }
|
||||
}
|
||||
@@ -0,0 +1,52 @@
|
||||
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();
|
||||
}
|
||||
@@ -0,0 +1,148 @@
|
||||
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 = 30 * 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();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,58 @@
|
||||
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());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,54 @@
|
||||
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;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
package com.easysocket.entity.basemsg;
|
||||
|
||||
/**
|
||||
* Author:Alex
|
||||
* Date:2019/12/8
|
||||
* Note:
|
||||
*/
|
||||
public interface IResponse {
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
package com.easysocket.entity.basemsg;
|
||||
|
||||
import java.io.Serializable;
|
||||
|
||||
/**
|
||||
* Author:Alex
|
||||
* Date:2019/6/1
|
||||
* Note:发送数据的接口
|
||||
*/
|
||||
public interface ISender extends Serializable {
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
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);
|
||||
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,9 @@
|
||||
package com.easysocket.entity.basemsg;
|
||||
|
||||
/**
|
||||
* Author:Alex
|
||||
* Date:2019/10/19
|
||||
* Note:基础消息
|
||||
*/
|
||||
public class SuperSender implements ISender {
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
package com.easysocket.exception;
|
||||
|
||||
/**
|
||||
* Author:Alex
|
||||
* Date:2019/6/5
|
||||
* Note:初始化异常
|
||||
*/
|
||||
public class InitialExeption extends RuntimeException{
|
||||
public InitialExeption(String s){
|
||||
super(s);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
package com.easysocket.exception;
|
||||
|
||||
/**
|
||||
* Author:Alex
|
||||
* Date:2019/6/5
|
||||
* Note:非空异常
|
||||
*/
|
||||
public class NotNullException extends RuntimeException {
|
||||
public NotNullException(String e) {
|
||||
super(e);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
package com.easysocket.exception;
|
||||
|
||||
/**
|
||||
* Author:Alex
|
||||
* Date:2019/6/5
|
||||
* Note:可恢复socket读数据异常
|
||||
*/
|
||||
public class ReadRecoverableExeption extends Exception {
|
||||
|
||||
public ReadRecoverableExeption(String s){
|
||||
super(s);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
package com.easysocket.exception;
|
||||
|
||||
/**
|
||||
* Author:Mapogo
|
||||
* Date:2020/12/29
|
||||
* Note:不可修复的读取错误
|
||||
*/
|
||||
public class ReadUnrecoverableException extends Exception {
|
||||
public ReadUnrecoverableException(String s) {
|
||||
super(s);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
package com.easysocket.exception;
|
||||
|
||||
/**
|
||||
* Author:Alex
|
||||
* Date:2019/6/4
|
||||
* Note:请求取消异常
|
||||
*/
|
||||
public class RequestCancelException extends Exception{
|
||||
|
||||
public RequestCancelException(String s){
|
||||
super(s);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
package com.easysocket.exception;
|
||||
|
||||
/**
|
||||
* Author:Alex
|
||||
* Date:2019/6/4
|
||||
* Note:请求超时异常
|
||||
*/
|
||||
public class RequestTimeOutException extends Exception{
|
||||
|
||||
public RequestTimeOutException(String s){
|
||||
super(s);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
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);
|
||||
|
||||
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
/*
|
||||
* 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();
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
/*
|
||||
* 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();
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
/*
|
||||
* 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();
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
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);
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
package com.easysocket.interfaces.config;
|
||||
|
||||
import java.nio.ByteOrder;
|
||||
|
||||
/**
|
||||
* 消息数据格式
|
||||
*/
|
||||
public interface IMessageProtocol {
|
||||
|
||||
/**
|
||||
* 获取包头的长度
|
||||
*/
|
||||
int getHeaderLength();
|
||||
|
||||
/**
|
||||
* 获取数据包体的长度,根据协议这个长度应该写在包头中,在读取数据时用到
|
||||
*/
|
||||
int getBodyLength(byte[] header, ByteOrder byteOrder);
|
||||
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
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();
|
||||
}
|
||||
@@ -0,0 +1,65 @@
|
||||
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();
|
||||
|
||||
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
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();
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
package com.easysocket.interfaces.conn;
|
||||
|
||||
/**
|
||||
* Author:Alex
|
||||
* Date:2019/6/1
|
||||
* Note:
|
||||
*/
|
||||
public interface IReconnListener {
|
||||
|
||||
/**
|
||||
* 关联连接器
|
||||
* @param iConnectionManager
|
||||
*/
|
||||
void attach(IConnectionManager iConnectionManager);
|
||||
|
||||
/**
|
||||
* 分离连接器
|
||||
*/
|
||||
void detach();
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
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);
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
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);
|
||||
}
|
||||
@@ -0,0 +1,52 @@
|
||||
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);
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
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);
|
||||
}
|
||||
@@ -0,0 +1,57 @@
|
||||
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) {
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
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();
|
||||
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
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);
|
||||
|
||||
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
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);
|
||||
|
||||
}
|
||||
87
easysocket/src/main/java/com/easysocket/utils/LogUtil.java
Normal file
87
easysocket/src/main/java/com/easysocket/utils/LogUtil.java
Normal file
@@ -0,0 +1,87 @@
|
||||
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);
|
||||
}
|
||||
|
||||
}
|
||||
149
easysocket/src/main/java/com/easysocket/utils/Utils.java
Normal file
149
easysocket/src/main/java/com/easysocket/utils/Utils.java
Normal file
@@ -0,0 +1,149 @@
|
||||
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;
|
||||
}
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user