1. 定义 Socket 状态,SocketStatus.java
/**
* @Description: Socket 连接的状态
*/
public interface SocketStatus {
/**
* 已断开连接
*/
int SOCKET_DISCONNECTED = 0;
/**
* 正在连接
*/
int SOCKET_CONNECTING = 1;
/**
* 已连接
*/
int SOCKET_CONNECTED = 2;
/**
* 正在断开连接
*/
int SOCKET_DISCONNECTING = 3;
}
2. 创建主机地址实体类,SocketAddress.java
/**
* @Description: Socket 主机地址
*/
public class SocketAddress {
/**
* IPV4 地址
*/
private String ip;
/**
* 连接服务器端口号
*/
private int port;
/**
* 当前 IP 地址 ping 不通时的备用 IP 和端口
*/
private SocketAddress backupAddress;
public SocketAddress(String ip, int port) {
this.ip = ip;
this.port = port;
}
public String getIp() {
return ip;
}
public int getPort() {
return port;
}
/**
* 获取备用的 IP 和端口号
*
* @return 备用的端口号和 IP 地址
*/
public SocketAddress getBackupAddress() {
return backupAddress;
}
/**
* 设置备用的 IP 和端口号,可以不设置
*
* @param backupAddress 备用的 IP 和端口号信息
*/
public void setBackupAddress(SocketAddress backupAddress) {
this.backupAddress = backupAddress;
}
@Override
public String toString() {
return "SocketAddress {" + "ip='" + ip + '\'' + ", port=" + port + '}';
}
}
3. 创建异常实体类,NotNullException.java
/**
* @Description: 非空异常
*/
public class NotNullException extends RuntimeException {
public NotNullException(String message) {
super(message);
}
}
4. 创建 Socket 工厂类,SocketFactory.java
/**
* @Description: Socket 工厂
*/
public abstract class SocketFactory {
/**
* 根据主机地址和配置参数创建 Socket
*
* @param socketAddress 主机地址
* @param options 配置参数
* @return
* @throws Exception
*/
public abstract Socket createSocket(SocketAddress socketAddress, IOSocketOptions options) throws Exception;
}
5. 创建 SSL/安全套接字协议 配置类,SocketSSLConfig.java
/**
* @Description: Socket 的 ssl/安全套接字协议 配置
*/
public class SocketSSLConfig {
/**
* 安全协议名称(缺省值为 SSL)
*/
private String mProtocol;
/**
* 信任证书管理器(缺省值为 X509)
*/
private TrustManager[] mTrustManager;
/**
* 证书密钥管理器(缺省值为 null)
*/
private KeyManager[] mKeyManager;
/**
* 自定义 SSLFactory(缺省值为 null)
*/
private SSLSocketFactory mCustomSSLFactory;
public static class Builder {
private SocketSSLConfig mConfig;
public Builder() {
mConfig = new SocketSSLConfig();
}
public Builder setProtocol(String mProtocol) {
mConfig.mProtocol = mProtocol;
return this;
}
public Builder setTrustManager(TrustManager[] mTrustManager) {
mConfig.mTrustManager = mTrustManager;
return this;
}
public Builder setKeyManager(KeyManager[] mKeyManager) {
mConfig.mKeyManager = mKeyManager;
return this;
}
public Builder setCustomSSLFactory(SSLSocketFactory mCustomSSLFactory) {
mConfig.mCustomSSLFactory = mCustomSSLFactory;
return this;
}
public SocketSSLConfig build() {
return mConfig;
}
}
public String getProtocol() {
return mProtocol;
}
public TrustManager[] getTrustManager() {
return mTrustManager;
}
public KeyManager[] getKeyManager() {
return mKeyManager;
}
public SSLSocketFactory getCustomSSLFactory() {
return mCustomSSLFactory;
}
}
6. 创建 X509 安全信任证书管理器 类,DefaultX509ProtocolTrustManager.java
/**
* @Description: 默认 X509 安全信任证书管理器
*/
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];
}
}
7. 创建 Socket 相关配置类,IOSocketOptions.java
/**
* @Description: Socket 相关配置
*/
public class IOSocketOptions {
/**
* 是否为调试模式,默认为 true
*/
private static boolean isDebug = true;
/**
* Socket 主机地址
*/
private SocketAddress socketAddress;
/**
* Socket 备用主机地址
*/
private SocketAddress backupAddress;
/**
* 连接超时时间(单位毫秒)
*/
private int connectTimeout;
/**
* Socket 重连管理器
*/
private AbsReConnection reConnectionManager;
/**
* Socket 工厂
*/
private SocketFactory socketFactory;
/**
* Socket 安全套接字协议相关配置
*/
private SocketSSLConfig socketSSLConfig;
/**
* 静态内部类
*/
public static class Builder {
IOSocketOptions socketOptions;
//首先获取一个默认的配置
public Builder() {
this(getDefaultOptions());
}
public Builder(IOSocketOptions defaultOptions) {
socketOptions = defaultOptions;
}
/**
* 设置 Socket 主机地址
*
* @param socketAddress
* @return
*/
public Builder setSocketAddress(SocketAddress socketAddress) {
socketOptions.socketAddress = socketAddress;
return this;
}
/**
* 设置 Socket 备用主机地址
*
* @param backupAddress
* @return
*/
public Builder setBackupAddress(SocketAddress backupAddress) {
socketOptions.backupAddress = backupAddress;
return this;
}
/**
* 设置连接超时时间(单位毫秒)
*
* @param connectTimeout
* @return
*/
public Builder setConnectTimeout(int connectTimeout) {
socketOptions.connectTimeout = connectTimeout;
return this;
}
/**
* 设置 Socket 重连管理器
*
* @param reConnectionManager
* @return
*/
public Builder setReConnectionManager(AbsReConnection reConnectionManager) {
socketOptions.reConnectionManager = reConnectionManager;
return this;
}
/**
* 自定义创建 socket 工厂
*
* @param socketFactory
*/
public Builder setSocketFactory(SocketFactory socketFactory) {
socketOptions.socketFactory = socketFactory;
return this;
}
/**
* 安全套接字协议的配置
*
* @param socketSSLConfig
* @return
*/
public Builder setSocketSSLConfig(SocketSSLConfig socketSSLConfig) {
socketOptions.socketSSLConfig = socketSSLConfig;
return this;
}
public IOSocketOptions build() {
return socketOptions;
}
}
/**
* 获取默认的配置
*/
public static IOSocketOptions getDefaultOptions() {
IOSocketOptions options = new IOSocketOptions();
options.socketAddress = null;
options.backupAddress = null;
options.reConnectionManager = new DefaultReConnection(); //默认 Socket 重连器
options.connectTimeout = 10 * 1000; //连接超时默认 5 秒
options.socketFactory = null;
options.socketSSLConfig = null;
return options;
}
public static boolean isIsDebug() {
return isDebug;
}
public SocketAddress getSocketAddress() {
return socketAddress;
}
public SocketAddress getBackupAddress() {
return backupAddress;
}
public int getConnectTimeout() {
return connectTimeout;
}
public AbsReConnection getReConnectionManager() {
return reConnectionManager;
}
public SocketFactory getSocketFactory() {
return socketFactory;
}
public SocketSSLConfig getSocketSSLConfig() {
return socketSSLConfig;
}
public static void setIsDebug(boolean isDebug) {
IOSocketOptions.isDebug = isDebug;
}
public void setConnectTimeout(int connectTimeout) {
this.connectTimeout = connectTimeout;
}
public void setSocketFactory(SocketFactory socketFactory) {
this.socketFactory = socketFactory;
}
public void setSocketSSLConfig(SocketSSLConfig socketSSLConfig) {
this.socketSSLConfig = socketSSLConfig;
}
}
8. 创建配置参数接口,IOptions.java
/**
* @Description: 配置参数
*/
public interface IOptions<T> {
/**
* 设置配置参数
*
* @param socketOptions
* @return
*/
T setOptions(IOSocketOptions socketOptions);
/**
* 获取配置参数
*
* @return
*/
IOSocketOptions getOptions();
}
9. 创建连接管理器的接口规范类 IConnectionManager.java
/**
* @Description: 连接管理器的接口规范
*/
public interface IConnectionManager extends IOptions<IConnectionManager> {
/**
* 开始连接
*/
void connect();
/**
* 关闭连接
*
* @param isNeedReconnect 是否需要重连
*/
void disConnect(boolean isNeedReconnect);
/**
* 获取连接状态
*
* @return
*/
int getConnectionStatus();
/**
* 是否可连接的
*
* @return
*/
boolean isConnectViable();
/**
* 切换 host
*
* @param socketAddress
*/
void switchHost(SocketAddress socketAddress);
/**
* 获取输入流
*
* @return
*/
InputStream getInputStream();
/**
* 获取输出流
*
* @return
*/
OutputStream getOutputStream();
}
10. 创建 Socket 连接的超类,SuperConnection.java
/**
* @Description: socket 连接的超类
*/
public abstract class SuperConnection implements IConnectionManager {
/**
* 连接的状态,初始值为断开连接
*/
protected final AtomicInteger connectionStatus = new AtomicInteger(SocketStatus.SOCKET_DISCONNECTED);
/**
* 连接线程
*/
private ExecutorService connectionExecutor;
/**
* Socket 主机地址
*/
protected SocketAddress socketAddress;
/**
* Socket 重连管理器
*/
private AbsReConnection reConnection;
/**
* Socket 配置参数
*/
protected IOSocketOptions socketOptions;
public SuperConnection(SocketAddress socketAddress) {
this.socketAddress = socketAddress;
}
@Override
public IConnectionManager setOptions(IOSocketOptions socketOptions) {
if (socketOptions == null) {
return this;
}
this.socketOptions = socketOptions;
// 是否更改重连管理器
if (reConnection != null && !reConnection.equals(socketOptions.getReConnectionManager())) {
// 停止分离重连管理器
reConnection.detach();
reConnection = socketOptions.getReConnectionManager();
// 关联连接器
reConnection.attach(this);
}
return this;
}
@Override
public IOSocketOptions getOptions() {
return socketOptions;
}
/**
* 连接成功
*/
protected void onConnectionOpened() {
LogUtil.i("Socket 连接成功... " + socketAddress.toString());
connectionStatus.set(SocketStatus.SOCKET_CONNECTED);
}
/**
* 连接 Socket 任务
*/
private Runnable connectTask = () -> {
try {
openConnection();
} catch (Exception e) {
LogUtil.e("Socket 连接失败...");
e.printStackTrace();
// Socket 状态: 已断开连接
connectionStatus.set(SocketStatus.SOCKET_DISCONNECTED);
}
};
@Override
public synchronized void connect() {
LogUtil.i("Socket 开始连接...");
if (socketAddress.getIp() == null) {
throw new NotNullException("请检查是否设置了IP地址.");
}
// Socket 状态: 正在连接
connectionStatus.set(SocketStatus.SOCKET_CONNECTING);
// 重连管理器
if (reConnection != null) {
reConnection.detach(); // 停止分离重连管理器
}
reConnection = socketOptions.getReConnectionManager();
if (reConnection != null) {
reConnection.attach(this);//关联连接器
}
// 开启线程,进行连接
if (connectionExecutor == null || connectionExecutor.isShutdown()) {
// 核心线程数为 0,非核心线程数可以有 Integer.MAX_VALUE 个,存活时间为 60 秒,适合于在不断的进行连接情况下,避免重复创建和销毁线程
connectionExecutor = Executors.newCachedThreadPool();
LogUtil.i("Executors newCachedThreadPool");
}
// 执行连接任务
connectionExecutor.execute(connectTask);
}
@Override
public synchronized void disConnect(boolean isNeedReconnect) {
// 只有在已经连接的状态下才能断开连接
if (connectionStatus.get() != SocketStatus.SOCKET_CONNECTED) {
return;
}
//正在重连中, 则不断开 Socket
if (reConnection.isReConnecting()) {
return;
}
// Socket 状态: 正在断开连接
connectionStatus.set(SocketStatus.SOCKET_DISCONNECTING);
// 开启断开连接线程
String info = socketAddress.toString();
Thread disConnectThread = new DisConnectThread("DisConnection thread: " + info, isNeedReconnect);
// setDaemon: true 既设置该线程为守护线程,表示该线程是不重要的,进程退出时,不需要等待这个线程执行完成,目的,避免子线程无限死循环,导致退不出程序
disConnectThread.setDaemon(true);
disConnectThread.start();
}
/**
* 断开连接线程
*/
private class DisConnectThread extends Thread {
// 当前连接断开后,是否需要自动重连
boolean isNeedReconnect;
public DisConnectThread(@NonNull String name, boolean isNeedReconnect) {
super(name);
this.isNeedReconnect = isNeedReconnect;
}
@Override
public void run() {
try {
// 关闭连接线程
if (connectionExecutor != null && !connectionExecutor.isShutdown()) {
connectionExecutor.shutdown();
connectionExecutor = null;
}
closeConnection();
LogUtil.i("关闭Socket连接... " + getName());
// Socket 状态: 已断开连接
connectionStatus.set(SocketStatus.SOCKET_DISCONNECTED);
} catch (Exception e) {
//断开连接时,发生异常
e.printStackTrace();
LogUtil.e(e.getMessage());
}
}
}
@Override
public synchronized void switchHost(SocketAddress socketAddress) {
if (socketAddress != null) {
SocketAddress oldAddress = this.socketAddress;
this.socketAddress = socketAddress;
// 切换主机
}
}
@Override
public int getConnectionStatus() {
return connectionStatus.get();
}
@Override
public boolean isConnectViable() {
// 当前 socket 是否处于可连接的状态
return connectionStatus.get() == SocketStatus.SOCKET_DISCONNECTED;
}
/**
* 打开连接
*
* @throws Exception
*/
protected abstract void openConnection() throws Exception;
/**
* 关闭连接
*
* @throws IOException
*/
abstract void closeConnection() throws Exception;
}
11. 实现 Tcp 的连接,开放接口类,TcpConnection.java
/**
* @Description: Tcp 连接
*/
public class TcpConnection extends SuperConnection {
/**
* Socket 对象
*/
private Socket socket;
public TcpConnection(SocketAddress socketAddress) {
super(socketAddress);
}
@Override
protected void openConnection() throws Exception {
try {
//获取 Socket
socket = getSocket();
} catch (Exception e) {
e.printStackTrace();
//Socket 状态: 未连接
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();
}
}
/**
* 根据配置信息获取对应的 Socket
*
* @return
* @throws Exception
*/
private synchronized Socket getSocket() throws Exception {
//自定义 socket 生成工厂,默认为空
if (socketOptions.getSocketFactory() != null) {
return socketOptions.getSocketFactory().createSocket(socketAddress, socketOptions);
}
//默认操作 Socket SSL/安全套接字协议,默认为空
SocketSSLConfig config = socketOptions.getSocketSSLConfig();
if (config == null) {
return new Socket();
}
//获取 SSL/安全套接字协议 配置工厂
SSLSocketFactory factory = config.getCustomSSLFactory();
if (factory == null) {
//配置安全套接字协议参数
String protocol = "SSL";
if (!Util.isStringEmpty(config.getProtocol())) {
protocol = config.getProtocol();
}
TrustManager[] trustManagers = config.getTrustManager();
if (trustManagers == null || trustManagers.length == 0) {
//缺省信任所有证书
trustManagers = new TrustManager[]{new DefaultX509ProtocolTrustManager()};
}
try {
//创建安全套接字协议 Socket
SSLContext sslContext = SSLContext.getInstance(protocol);
sslContext.init(config.getKeyManager(), trustManagers, new SecureRandom());
return sslContext.getSocketFactory().createSocket();
} catch (Exception e) {
e.printStackTrace();
LogUtil.e(e.getMessage());
//异常时创建默认 Socket
return new Socket();
}
} else {
try {
return factory.createSocket();
} catch (Exception e) {
e.printStackTrace();
LogUtil.e(e.getMessage());
//异常时创建默认 Socket
return new Socket();
}
}
}
@Override
void closeConnection() throws Exception {
if (socket != null) {
socket.close();
}
}
@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 getOutputStream() {
if (socket != null && socket.isConnected() && !socket.isClosed()) {
try {
return socket.getOutputStream();
} catch (IOException e) {
e.printStackTrace();
}
}
return null;
}
}
12. 测试方法
IOSocketOptions socketOptions = new IOSocketOptions.Builder().build();
SocketAddress socketAddress = new SocketAddress("192.168.1.1", 6688);
TcpConnection tcpConnection = new TcpConnection(socketAddress);
tcpConnection.setOptions(socketOptions);
view.findViewById(R.id.but_connect).setOnClickListener(v -> tcpConnection.connect());
view.findViewById(R.id.but_disconnect).setOnClickListener(v -> tcpConnection.disConnect(false));