Socket creation and connection

1. Define Socket status, SocketStatus.java

 /**
   * @Description: The status of the Socket connection
   */
  public interface SocketStatus {

      /**
       * disconnected
       */
      int SOCKET_DISCONNECTED = 0;

      /**
       * connecting
       */
      int SOCKET_CONNECTING = 1;

      /**
       * connected
       */
      int SOCKET_CONNECTED = 2;

      /**
       * Disconnecting
       */
      int SOCKET_DISCONNECTING = 3;
  }

2. Create a host address entity class, SocketAddress.java

 /**
   * @Description: Socket host address
   */
  public class SocketAddress {

      /**
       * IPV4 address
       */
      private String ip;

      /**
       * Connection server port number
       */
      private int port;

      /**
       * Alternate IP and port when the current IP address cannot be pinged
       */
      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;
      }

      /**
       * Get alternate IP and port number
       *
       * @return alternate port number and IP address
       */
      public SocketAddress getBackupAddress() {
          return backupAddress;
      }

      /**
       * Set the alternate IP and port number, you can not set it
       *
       * @param backupAddress backup IP and port information
       */
      public void setBackupAddress(SocketAddress backupAddress) {
          this.backupAddress = backupAddress;
      }

      @Override
      public String toString() {
          return "SocketAddress {" + "ip='" + ip + '\'' + ", port=" + port + '}';
      }
  }

3. Create an exception entity class, NotNullException.java

 /**
   * @Description: non-null exception
   */
  public class NotNullException extends RuntimeException {
      public NotNullException(String message) {
          super(message);
      }
  }

4. Create Socket factory class, SocketFactory.java

 /**
   * @Description: Socket factory
   */
  public abstract class SocketFactory {

      /**
       * Create a Socket based on the host address and configuration parameters
       *
       * @param socketAddress host address
       * @param options configuration parameters
       * @return
       * @throws Exception
       */
      public abstract Socket createSocket(SocketAddress socketAddress, IOSocketOptions options) throws Exception;
  }

5. Create SSL/secure socket protocol configuration class, SocketSSLConfig.java

 /**
   * @Description: Socket's ssl/secure socket protocol configuration
   */
  public class SocketSSLConfig {

      /**
       * Security protocol name (default is SSL)
       */
      private String mProtocol;

      /**
       * trust certificate manager (default is X509)
       */
      private TrustManager[] mTrustManager;

      /**
       * certificate key manager (default is null)
       */
      private KeyManager[] mKeyManager;

      /**
       * Custom SSLFactory (default is 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. Create X509 security trust certificate manager class, DefaultX509ProtocolTrustManager.java

 /**
   * @Description: Default X509 security trust certificate manager
   */
  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. Create Socket-related configuration classes, IOSocketOptions.java

 /**
   * @Description: Socket related configuration
   */
  public class IOSocketOptions {

      /**
       * Whether it is debug mode, the default is true
       */
      private static boolean isDebug = true;

      /**
       * Socket host address
       */
      private SocketAddress socketAddress;

      /**
       * Socket alternate host address
       */
      private SocketAddress backupAddress;


      /**
       * Connection timeout (in milliseconds)
       */
      private int connectTimeout;

      /**
       * Socket reconnection manager
       */
      private AbsReConnection reConnectionManager;

      /**
       * Socket factory
       */
      private SocketFactory socketFactory;

      /**
       * Socket secure socket protocol related configuration
       */
      private SocketSSLConfig socketSSLConfig;

      /**
       * Static inner class
       */
      public static class Builder {
          IOSocketOptions socketOptions;

          //First get a default configuration
          public Builder() {
              this(getDefaultOptions());
          }

          public Builder(IOSocketOptions defaultOptions) {
              socketOptions = defaultOptions;
          }

          /**
           * Set the Socket host address
           *
           * @param socketAddress
           * @return
           */
          public Builder setSocketAddress(SocketAddress socketAddress) {
              socketOptions.socketAddress = socketAddress;
              return this;
          }

          /**
           * Set the Socket alternate host address
           *
           * @param backupAddress
           * @return
           */
          public Builder setBackupAddress(SocketAddress backupAddress) {
              socketOptions. backupAddress = backupAddress;
              return this;
          }

          /**
           * Set the connection timeout (in milliseconds)
           *
           * @param connectTimeout
           * @return
           */
          public Builder setConnectTimeout(int connectTimeout) {
              socketOptions.connectTimeout = connectTimeout;
              return this;
          }

          /**
           * Set up the Socket reconnection manager
           *
           * @param reConnectionManager
           * @return
           */
          public Builder setReConnectionManager(AbsReConnection reConnectionManager) {
              socketOptions. reConnectionManager = reConnectionManager;
              return this;
          }


          /**
           * Custom create socket factory
           *
           * @param socketFactory
           */
          public Builder setSocketFactory(SocketFactory socketFactory) {
              socketOptions.socketFactory = socketFactory;
              return this;
          }

          /**
           * Configuration of the secure socket protocol
           *
           * @param socketSSLConfig
           * @return
           */
          public Builder setSocketSSLConfig(SocketSSLConfig socketSSLConfig) {
              socketOptions.socketSSLConfig = socketSSLConfig;
              return this;
          }

          public IOSocketOptions build() {
              return socketOptions;
          }
      }

      /**
       * Get the default configuration
       */
      public static IOSocketOptions getDefaultOptions() {
          IOSocketOptions options = new IOSocketOptions();
          options.socketAddress = null;
          options. backupAddress = null;
          options.reConnectionManager = new DefaultReConnection(); //Default Socket reconnector
          options.connectTimeout = 10 * 1000; //Connection timeout is 5 seconds by default
          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. Create configuration parameter interface, IOptions.java

 /**
   * @Description: configuration parameters
   */
  public interface IOptions<T> {

      /**
       * Set configuration parameters
       *
       * @param socketOptions
       * @return
       */
      T setOptions(IOSocketOptions socketOptions);

      /**
       * Get configuration parameters
       *
       * @return
       */
      IOSocketOptions getOptions();
  }

9. Create the interface specification class IConnectionManager.java of the connection manager

 /**
   * @Description: The interface specification of the connection manager
   */
  public interface IConnectionManager extends IOptions<IConnectionManager> {

      /**
       * start connection
       */
      void connect();

      /**
       * close the connection
       *
       * @param isNeedReconnect Whether reconnection is required
       */
      void disConnect(boolean isNeedReconnect);

      /**
       * Get connection status
       *
       * @return
       */
      int getConnectionStatus();

      /**
       * Is it connectable
       *
       * @return
       */
      boolean isConnectViable();

      /**
       * switch host
       *
       * @param socketAddress
       */
      void switchHost(SocketAddress socketAddress);

      /**
       * Get the input stream
       *
       * @return
       */
      InputStream getInputStream();

      /**
       * Get the output stream
       *
       * @return
       */
      OutputStream getOutputStream();
  }

10. Create the super class of Socket connection, SuperConnection.java

 /**
   * @Description: The superclass of socket connection
   */
  public abstract class SuperConnection implements IConnectionManager {

      /**
       * The state of the connection, the initial value is disconnected
       */
      protected final AtomicInteger connectionStatus = new AtomicInteger(SocketStatus. SOCKET_DISCONNECTED);

      /**
       * Connection thread
       */
      private ExecutorService connectionExecutor;

      /**
       * Socket host address
       */
      protected SocketAddress socketAddress;

      /**
       * Socket reconnection manager
       */
      private AbsReConnection reConnection;

      /**
       * Socket configuration parameters
       */
      protected IOSocketOptions socketOptions;

      public SuperConnection(SocketAddress socketAddress) {
          this.socketAddress = socketAddress;
      }

      @Override
      public IConnectionManager setOptions(IOSocketOptions socketOptions) {
          if (socketOptions == null) {
              return this;
          }
          this.socketOptions = socketOptions;

          // Whether to change the reconnection manager
          if (reConnection != null & amp; & amp; !reConnection.equals(socketOptions.getReConnectionManager())) {
              // stop detach reconnect manager
              reConnection. detach();
              reConnection = socketOptions. getReConnectionManager();
              // associated connector
              reConnection. attach(this);
          }
          return this;
      }

      @Override
      public IOSocketOptions getOptions() {
          return socketOptions;
      }

      /**
       * connection succeeded
       */
      protected void onConnectionOpened() {
          LogUtil.i("Socket connection successful... " + socketAddress.toString());
          connectionStatus.set(SocketStatus.SOCKET_CONNECTED);
      }

      /**
       * Connect Socket task
       */
      private Runnable connectTask = () -> {
          try {
              openConnection();
          } catch (Exception e) {
              LogUtil.e("Socket connection failed...");
              e.printStackTrace();
              // Socket status: disconnected
              connectionStatus.set(SocketStatus.SOCKET_DISCONNECTED);
          }
      };

      @Override
      public synchronized void connect() {
          LogUtil.i("Socket starts connection...");
          if (socketAddress. getIp() == null) {
              throw new NotNullException("Please check whether the IP address is set.");
          }

          // Socket status: connecting
          connectionStatus.set(SocketStatus.SOCKET_CONNECTING);

          // reconnection manager
          if (reConnection != null) {
              reConnection.detach(); // Stop detaching the reconnection manager
          }
          reConnection = socketOptions. getReConnectionManager();
          if (reConnection != null) {
              reConnection.attach(this);//associated connector
          }

          // Start the thread and connect
          if (connectionExecutor == null || connectionExecutor.isShutdown()) {
              // The number of core threads is 0, the number of non-core threads can be Integer.MAX_VALUE, and the survival time is 60 seconds, which is suitable for avoiding repeated creation and destruction of threads in the case of continuous connection
              connectionExecutor = Executors. newCachedThreadPool();
              LogUtil.i("Executors newCachedThreadPool");
          }
          // Execute the connection task
          connectionExecutor. execute(connectTask);
      }

      @Override
      public synchronized void disConnect(boolean isNeedReconnect) {
          // Disconnect only when already connected
          if (connectionStatus.get() != SocketStatus.SOCKET_CONNECTED) {
              return;
          }

          //If reconnecting, do not disconnect the Socket
          if (reConnection. isReConnecting()) {
              return;
          }

          // Socket status: disconnecting
          connectionStatus.set(SocketStatus.SOCKET_DISCONNECTING);
          // start the disconnection thread
          String info = socketAddress. toString();
          Thread disConnectThread = new DisConnectThread("DisConnection thread: " + info, isNeedReconnect);
          // setDaemon: true sets the thread as a daemon thread, indicating that the thread is not important. When the process exits, there is no need to wait for the thread to complete. The purpose is to avoid the infinite endless loop of the child thread, resulting in the inability to exit the program
          disConnectThread. setDaemon(true);
          disConnectThread. start();
      }

      /**
       * Disconnect thread
       */
      private class DisConnectThread extends Thread {
          // Whether to automatically reconnect after the current connection is disconnected
          boolean isNeedReconnect;

          public DisConnectThread(@NonNull String name, boolean isNeedReconnect) {
              super(name);
              this.isNeedReconnect = isNeedReconnect;
          }

          @Override
          public void run() {
              try {
                  // close the connection thread
                  if (connectionExecutor != null & amp; & amp; !connectionExecutor.isShutdown()) {
                      connectionExecutor. shutdown();
                      connectionExecutor = null;
                  }
                  closeConnection();
                  LogUtil.i("Close Socket connection... " + getName());
                  // Socket status: disconnected
                  connectionStatus.set(SocketStatus.SOCKET_DISCONNECTED);
              } catch (Exception e) {
                  //When disconnecting, an exception occurred
                  e.printStackTrace();
                  LogUtil.e(e.getMessage());
              }
          }
      }

      @Override
      public synchronized void switchHost(SocketAddress socketAddress) {
          if (socketAddress != null) {
              SocketAddress oldAddress = this.socketAddress;
              this.socketAddress = socketAddress;
              // switch host
          }
      }

      @Override
      public int getConnectionStatus() {
          return connectionStatus. get();
      }

      @Override
      public boolean isConnectViable() {
          // Whether the current socket is in a connectable state
          return connectionStatus.get() == SocketStatus.SOCKET_DISCONNECTED;
      }

      /**
       * open connection
       *
       * @throws Exception
       */
      protected abstract void openConnection() throws Exception;

      /**
       * close the connection
       *
       * @throws IOException
       */
      abstract void closeConnection() throws Exception;
  }

11. Implement Tcp connection, open interface class, TcpConnection.java

 /**
   * @Description: Tcp connection
   */
  public class TcpConnection extends SuperConnection {
      /**
       * Socket object
       */
      private Socket socket;

      public TcpConnection(SocketAddress socketAddress) {
          super(socketAddress);
      }

      @Override
      protected void openConnection() throws Exception {
          try {
              //Get Socket
              socket = getSocket();
          } catch (Exception e) {
              e.printStackTrace();
              //Socket status: not connected
              connectionStatus.set(SocketStatus.SOCKET_DISCONNECTED);
              throw new RuntimeException("Failed to create Socket.");
          }
          //Make Socket connection
          socket.connect(new InetSocketAddress(socketAddress.getIp(), socketAddress.getPort()), socketOptions.getConnectTimeout());
          //Close the Nagle algorithm, regardless of the size of the TCP packet, send it immediately
          socket.setTcpNoDelay(true);
          //The connection is already open
          if (socket.isConnected() & amp; & amp; !socket.isClosed()) {
              onConnectionOpened();
          }
      }

      /**
       * Obtain the corresponding Socket according to the configuration information
       *
       * @return
       * @throws Exception
       */
      private synchronized Socket getSocket() throws Exception {
          //Custom socket generation factory, default is empty
          if (socketOptions. getSocketFactory() != null) {
              return socketOptions.getSocketFactory().createSocket(socketAddress, socketOptions);
          }
          //Default operation Socket SSL/secure socket protocol, default is empty
          SocketSSLConfig config = socketOptions.getSocketSSLConfig();
          if (config == null) {
              return new Socket();
          }
          //Get the SSL/Secure Socket Protocol configuration factory
          SSLSocketFactory factory = config.getCustomSSLFactory();
          if (factory == null) {
              //Configure secure socket protocol parameters
              String protocol = "SSL";
              if (!Util.isStringEmpty(config.getProtocol())) {
                  protocol = config. getProtocol();
              }

              TrustManager[] trustManagers = config. getTrustManager();
              if (trustManagers == null || trustManagers. length == 0) {
                  //Trust all certificates by default
                  trustManagers = new TrustManager[]{new DefaultX509ProtocolTrustManager()};
              }

              try {
                  //Create a secure socket protocol 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());
                  //Create a default Socket when there is an exception
                  return new Socket();
              }
          } else {
              try {
                  return factory. createSocket();
              } catch (Exception e) {
                  e.printStackTrace();
                  LogUtil.e(e.getMessage());
                  //Create a default Socket when there is an exception
                  return new Socket();
              }
          }
      }

      @Override
      void closeConnection() throws Exception {
          if (socket != null) {
              socket. close();
          }
      }

      @Override
      public InputStream getInputStream() {
          if (socket != null & amp; & amp; socket.isConnected() & amp; & amp; !socket.isClosed()) {
              try {
                  return socket. getInputStream();
              } catch (IOException e) {
                  e.printStackTrace();
              }
          }
          return null;
      }

      @Override
      public OutputStream getOutputStream() {
          if (socket != null & amp; & amp; socket.isConnected() & amp; & amp; !socket.isClosed()) {
              try {
                  return socket. getOutputStream();
              } catch (IOException e) {
                  e.printStackTrace();
              }
          }
          return null;
      }
  }

12. Test method

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

The knowledge points of the article match the official knowledge files, and you can further learn related knowledge Java skill tree Network programmingTCP communication 108680 people are learning the system