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