Socket 创建与连接

news/2024/5/17 18:20:42 标签: Android, Java, TCP

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));


http://www.niftyadmin.cn/n/177598.html

相关文章

聚观早报 | 拼多多驳斥Google的指控;80%美国人工作将被AI影响

今日要闻&#xff1a;拼多多驳斥Google“恶意软件”的指控&#xff1b;80%美国人工作将被AI影响&#xff1b;iPhone 15 Pro设计图上热搜&#xff1b;贾扬清离职阿里投身AI大模型创业&#xff1b;OPPO Find X6 系列发布拼多多驳斥Google“恶意软件”的指控 3 月 21 日&#xff0…

springcloud seata(1.6.1版本)

一、下载Seata&#xff0c;地址&#xff1a;下载中心 二、解压部署 1、进入conf目录修改配置文件&#xff0c;依据 application.example.yml 文件为样例&#xff0c;修改application.yml server:port: 7091spring:application:name: seata-serverlogging:config: cla…

Spring-Security框架的启动过程

在springboot的spring.factories配置文件中通过org.springframework.boot.autoconfigure.EnableAutoConfiguration配置项配置了SecurityAutoConfiguration为自动加载的配置类&#xff0c;可以看到通过Import注解导入SpringBootWebSecurityConfiguration、WebSecurityEnablerCon…

【算法竞赛-初级】基础数据结构-链表篇

这个系列的文章主要是用来记录系统学习算法的心得思路&#xff0c;主要参考书籍是罗勇军老师的算法竞赛。现在目前目标是完成该书的初级部分&#xff0c;这篇文章是第一章基本数据结构中的链表部分。后续将持续更新该系列&#xff0c;欢迎大家一键三连&#xff01;&#x1f601…

YOLOV5详解

1. YOLOV5的前处理Anchor的改进 1.1 Anchor生成的改进 首先YOLOV3/V4/V5都是根据训练的数据集来生成anchor, 就是在训练之前用一个独立的程序去计算Anchor, 但是还不够好 因为自动生成的anchor是拿来整个数据集去做的&#xff0c;但是我们知道目标检测训练的时候是分batch训练…

Linux--数据链路层--ARP协议--0319-21

目录 1. 认识以太网 1.1 以太网帧格式 1.2 基于以太网帧简单模拟局域网通信 问题一&#xff1a;如果有多台主机都在发送数据呢&#xff1f; 问题二&#xff1a;发送方知不知道自己的数据被影响了呢&#xff1f; 1.3 MTU 1.3.1 MTU对IP协议的影响 1.3.2 MTU对UDP协议的影…

《计算机网络-自顶向下》02. 应用层

文章目录应用层协议原理应用程序体系结构进程通信客户和服务器进程进程与计算网络之间的接口进程寻址可供应用使用的运输层服务可靠数据传输吞吐量定时安全性因特网提供的运输服务TCP安全的 TCPUDP因特网运输协议所不提供的服务一些因特网应用所使用的运输层协议网络应用 —— …

用于电力电子器件的栅极驱动器

栅极驱动器是一种功率放大器&#xff0c;它接受来自控制器IC的低功耗输入&#xff0c;并为功率器件产生适当的高电流栅极驱动。随着对电力电子器件的要求不断提高&#xff0c;栅极驱动器电路的设计和性能变得越来越重要。 功率半导体器件是现代电力电子系统的核心。这些系统利…