Java Essay - BIO, Socket instance, realize the transmission of multiple data

Java Essay - BIO, Socket instance, realize the transmission of multiple data

Realize the transmission of multiple data

  • In a previous blog ( Java Notes - BIO Socket Programming Example ), two simple Socket programming examples are given. One is to implement byte stream transmission. Since the server does not know when the data transmission of the client ends, you can see in line 23 of the client code that the server is notified by calling socket.shutdownOutput( ), the client The data of the client has been transmitted. Correspondingly, you can see in line 24 of the code of the server that the server detects whether the client has completed the transmission of its data by calling socket.isInputShutdown( ).

  • Although the above method can realize the identification effect after the data transmission is completed, once the client calls the socket.shutdownOutput( ) method, it can no longer use the current connection to send other data to the server. If you want to send new data, then Only a new connection can be initiated, and the cost of initiating a connection can be imagined. Therefore, we must try to transmit multiple data on one connection as much as possible. But the problem also follows. When transmitting multiple strings or multiple files, we need to develop a transmission protocol to identify the boundary of each data. There are two commonly used data boundary identification methods: one is to add an end mark at the end of each data; the other is to send the length of the data before sending each data.

  • exist Java Notes - BIO Socket Programming Example In , another programming example implements character stream transmission, which adopts the first method in the above, and uses a newline character as the terminator of each string data. However, this strategy is not applicable in byte stream transmission, because similar terminators are very easy to conflict with the data in the file, resulting in data transmission errors. So in byte stream transmission, the second method above should be used.

  • The following code will use this strategy, after the connection is established, the client sends multiple files to the server. Before sending each file, the client needs to calculate the number of bytes of the current file (stored in an int type variable), and then convert it into 4 bytes and send it to the server, so as to let the server know the next transmission. File size. After the connection is established, the server first reads 4 bytes from the input stream and converts it to int type, so as to know the size of the file that the client will transmit next, and then read and accumulate cyclically to convert the size of the file to the client. The data transmitted from the client is written to the file of the server. After the accumulated file size is reached, the data transmission is completed and an "ok" string is returned to the client. Subsequent servers continue to try to read 4 bytes of data from the input stream, start new data transmission, and so on. After the client completes the transmission of all files, it calls socket.shutdownOutput( ) to inform the server that its data transmission is complete, and the server decides whether to end the data reading cycle through socket.isInputShutdown( ). The specific code implementation is as follows:

    // Client code Client.java
    public class Client {
    
        public static final Logger logger = LoggerFactory.getLogger(Client.class);
    
        public static byte[] intToBytearray(int num) {
            return new byte[] {(byte)((num >> 24) & 0xFF), (byte)((num >> 16) & 0xFF),
                    (byte)((num >> 8) & 0xFF), (byte)(num & 0xFF)};
        }
    
        public static void main(String[] args) {
            BufferedReader bufferedReader = null;
            BufferedInputStream bufferedInputStream = null;
            BufferedOutputStream bufferedOutputStream = null;
            byte[] buffer = new byte[16];
            Socket socket = null;
            try {
                socket = new Socket("127.0.0.1" ,8080);
                bufferedReader = new BufferedReader(new InputStreamReader(socket.getInputStream()));
                bufferedOutputStream = new BufferedOutputStream(socket.getOutputStream());
                for (int i = 1; i < 4; i++) {
                    String fileName = "tmpClient" + i + ".txt";
                    bufferedInputStream = new BufferedInputStream(new FileInputStream(new File(fileName)));
                    int t = bufferedInputStream.available();
                    logger.info("send file {}, size = {}", fileName, t);
                    bufferedOutputStream.write(intToBytearray(t));
                    int r = 0;
                    while ((r = bufferedInputStream.read(buffer)) > 0) {
                        t -= r;
                        logger.info("read {} bytes from file, {} bytes remain.", r, t);
                        bufferedOutputStream.write(buffer, 0, r);
                        bufferedOutputStream.flush();
                    }
                    String s = bufferedReader.readLine();
                    if ("ok".equalsIgnoreCase(s)) {
                        logger.info("transport file {} ok", fileName);
                    } else {
                        logger.error("transport file {} fail", fileName);
                    }
                }
                socket.shutdownOutput();
            } catch (Exception e) {
                e.printStackTrace();
            } finally {
                StreamUtil.close(bufferedReader);
                StreamUtil.close(bufferedOutputStream);
                StreamUtil.close(bufferedInputStream);
                StreamUtil.close(socket);
            }
        }
    
    }
    
    // The server code Processor.java implements the Runnable interface
    //  Every time the Server receives a client request, it instantiates a Processor object and starts a thread for processing.
    
    public class Processor implements Runnable {
    
        public static final Logger logger = LoggerFactory.getLogger(Processor.class);
    
        private Socket socket;
    
        public Processor(Socket socket) {
            this.socket = socket;
        }
    
        public static int byteArrayToInt(byte[] arr) {
            return (arr[0] & 0xFF) << 24 | (arr[1] & 0xFF) << 16 |
                    (arr[2] & 0xFF) << 8 | (arr[3] & 0xFF);
        }
    
        @Override
        public void run() {
            PrintWriter printWriter = null;
            BufferedInputStream bufferedInputStream = null;
            BufferedOutputStream bufferedOutputStream = null;
            byte[] buffer = new byte[16];
            int cnt = 0;
            byte[] header = new byte[4];
            try {
                bufferedInputStream = new BufferedInputStream(socket.getInputStream());
                printWriter = new PrintWriter(socket.getOutputStream(), true);
                while (!socket.isInputShutdown() && bufferedInputStream.read(header) == 4) {
                    int t = byteArrayToInt(header);
                    if (t < 1) {
                        printWriter.println("error");
                        break;
                    }
                    logger.info("incoming file size = {}", t);
                    String fileName = "tmpServer" + cnt + ".txt";
                    bufferedOutputStream = new BufferedOutputStream(new FileOutputStream(new File(fileName)));
                    int r = 0;
                    while (t > 0 && (r = bufferedInputStream.read(buffer)) > 0) {
                        t -= r;
                        logger.info("t bytes remain, {}", t);
                        bufferedOutputStream.write(buffer, 0, r);
                        bufferedOutputStream.flush();
                    }
                    logger.info("received file {}", fileName);
                    printWriter.println("ok");
                    cnt++;
                }
            } catch (Exception e) {
                e.printStackTrace();
            } finally {
                StreamUtil.close(printWriter);
                StreamUtil.close(bufferedOutputStream);
                StreamUtil.close(bufferedInputStream);
                StreamUtil.close(socket);
            }
        }
    }
    
    // Server code Server.java
    // Mainly responsible for monitoring connection requests from clients
    
    public class Server implements Runnable {
    
        public static final Logger logger = LoggerFactory.getLogger(Server.class);
    
        private final ExecutorService threadPool = Executors.newCachedThreadPool();
        private ServerSocket serverSocket = null;
    
        public void start() throws IOException {
            serverSocket = new ServerSocket(8080);
            threadPool.execute(this);
        }
    
        @Override
        public void run() {
            Socket socket = null;
            try {
                while ((socket = serverSocket.accept()) != null) {
                    logger.info("client {} connected.", socket.getRemoteSocketAddress());
                    threadPool.execute(new Processor(socket));
                }
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    
        public void close() {
            if (serverSocket != null && !serverSocket.isClosed()) {
                try {
                    serverSocket.close();
                } catch (IOException e) {
                    logger.error("error on close.", e);
                }
            }
            threadPool.shutdown();
        }
    
        public static void main(String[] args) {
            Server server = new Server();
            BufferedReader keyboardReader = null;
            try {
                server.start();
    
                System.out.println("type 'exit' to end.");
    
                keyboardReader = new BufferedReader(new InputStreamReader(System.in));
                String cmd = null;
                while ((cmd = keyboardReader.readLine()) != null) {
                    if ("exit".equalsIgnoreCase(cmd)) {
                        break;
                    }
                }
            } catch (Exception e) {
                e.printStackTrace();
            } finally {
                StreamUtil.close(keyboardReader);
                server.close();
            }
        }
    
    }
    
    // Tools
    	
    public class StreamUtil {
    	
        public static final Logger logger = LoggerFactory.getLogger(StreamUtil.class);
    	
        public static void close(Closeable stream) {
            if (stream == null) {
                return ;
            }
            try {
                stream.close();
            } catch (Exception e) {
                logger.error("errors on close {}", stream.getClass().getName(), e);
            }
        }
    	
    }
    

Tags: Java socket server bio

Posted by Zephyris on Sat, 07 May 2022 14:07:06 +0300