当前位置:网站首页>Network programming NiO: Bio and NiO

Network programming NiO: Bio and NiO

2020-11-06 01:15:00 Little goofy

BIO

BIO(Blocking I/O), Synchronous blocking , The implementation mode is one connection and one thread , When there is a client connection , The server side needs to assign a single thread to it , If the connection does not do anything, it will cause unnecessary thread overhead .BIO It's traditional Java io Programming , Its related classes and interfaces are in java.io It's a bag .

BIO It is suitable for architecture with small and fixed number of connections , High requirements for server resources , yes JDK1.4 The only choice before , But the program is simple and easy to understand .

BIO Programming flow

  1. The server starts a SeverSocket

  2. The client starts Socket Initiate communication to the server , By default, the server needs to create a thread for each client to communicate with it

  3. After the client initiates the request , First ask the server whether there is thread response , If not, they will wait or be rejected

  4. If there is a thread response , The client thread will wait for the end of the request , And go on with it

Simple code implementation

//BIO- Server side 
public class BIOSever {
    public static void main(String[] args) throws IOException {
        // stay BIO in , You can use thread pools to optimize 
        ExecutorService cachedThreadPool = Executors.newCachedThreadPool();
        ServerSocket serverSocket = new ServerSocket(6666);
        System.out.println(" The server has started ");

        while (true){
            System.out.println(" Wait for the client to connect .....( Blocked )");
            Socket socket = serverSocket.accept();
            System.out.println(" Client connection ");
            cachedThreadPool.execute(new Runnable() {
                public void run() {
                    handler(socket);
                }
            });
        }
    }

    // From the customer service side socket Reading data 
    public static void handler(Socket socket){
        try{
            InputStream inputStream = socket.getInputStream();
            byte[] b = new byte[1024];
            while (true){
                System.out.println(" Waiting for client input .....( Blocked )");
                int read = inputStream.read(b);
                if (read != -1){
                    System.out.println(new String(b, 0, read));
                }else {
                    break;
                }
            }
            inputStream.close();
        }catch (Exception e){
            e.printStackTrace();
        }finally {
            try {
                socket.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
}
//BIO- client 
public class BIOClient {
    public static void main(String[] args) throws IOException {
        Socket socket = new Socket("localhost", 6666);
        OutputStream outputStream = socket.getOutputStream();
        Scanner scanner = new Scanner(System.in);
        while (scanner.hasNextLine()){
            String message = scanner.nextLine();
            if ("exit".equals(message)) {
                break;
            }
            outputStream.write(message.getBytes());
        }
        outputStream.close();
        socket.close();
    }
}

BIO Problem analysis

As you can see from the code above BIO Two problems of programming :

  1. When the server is listening to the client connection (serverSocket.accept()), The server side is blocked , Can't handle anything else

  2. The server needs to set up a thread for each client , Although thread pools can be used to optimize , But when the concurrency is large , Thread overhead is still high

  3. When the connected client does not send data , The server will be blocked in read Operationally , Waiting for client input , This causes a waste of thread resources

 

NIO

from JDK1.4 Start ,java Provides a series of improved inputs / New features of output , Collectively referred to as NIO, Full name n by new I/O, It's synchronous and non blocking , So some people call it non-blocking I/O.NIO All the related classes are placed in java.nio Under a bag or its subpackage , And to the original java.io Many classes in the package have been rewritten .

NIO The three cores of

buffer (Buffer)

NIO It's buffer oriented , Or block oriented programming . stay NIO Of IO In transit , The data is read into the buffer first , Write from the buffer when needed , This reduces the number of direct reads and writes to the disk , Improved IO Transmission efficiency .

buffer (buffer) It's essentially a block of memory that can read and write data , That is, a certain storage space is reserved in the memory space , This storage space is used to buffer input and output data , This part of the reserved storage space is called buffer .

stay NIO In the program , passageway channel Although responsible for data transmission , But both input and output data must go through the buffer buffer.

stay java in , Buffer related classes are in java.nio It's a bag , The top-level class is Buffer, It's an abstract class .

Buffer Class 4 An important attribute :

  • mark: Mark

  • position: Location , The index of the next element to be read or written , This value is changed every time the buffer is read or written , Prepare for your next reading and writing

  • limit: Represents the end of the buffer , You cannot read or write to a location in the buffer that exceeds the limit , And the limits are modifiable

  • capacity: Capacity , The maximum amount of data that the buffer can hold , This value is set when the buffer is created , And it can't be modified

Buffer Class common methods :

Buffer A common subclass of ( The biggest difference between them is the data type of the underlying implementation array ):

  • ByteBuffer: Store byte data into a buffer

  • CharBuffer: Store character data in a buffer

  • IntBuffer: Store integer data into a buffer

  • ShortBuffer: Store short integer data into a buffer

  • LongBuffer: Store long integer data into a buffer

  • FloatBuffer: Store floating-point data into a buffer

  • DoubleBuffer: Store double precision floating-point data into a buffer

ByteBuffer

stay Buffer In all subclasses of , The most commonly used is still ByteBuffer, Its common method :

 

passageway (Channel)

stay NIO In the program, the data between the server and the client is not read and written through the stream , It's about reading and writing through channels .

Channels are similar to flows , It's all for reading and writing data , But there's also a difference between them :

  • The channel is bidirectional , You can read or write , And the flow is unidirectional , Can only read or write

  • Channels can read and write data asynchronously

  • The channel can read data from the buffer , You can also write data to a buffer

java in channel The related classes of are in java.nio.channel It's a bag .Channel It's an interface , Its common implementation classes are :

  • FileChannel: Data reading and writing for files , Its real implementation class is FileChannelImpl

  • DatagramChannel: be used for UDP Reading and writing data of , Its real implementation class is DatagramChannelImpl

  • ServerSocketChannel: For monitoring TCP Connect , Whenever there is a client connection, a SocketChannel, The function is similar to ServerSocket, Its real implementation class is ServerSocketChannelImpl

  • SocketChannel: be used for TCP Reading and writing data of , The function is similar to Node flow +Socket, Its real implementation class is SocketChannelImpl

FileChannel

FileChannel It is mainly used for local files IO operation , Such as file copying, etc . Its common methods are :

There is an attribute in the file transfer stream channel, It is empty by default , It can be done by getChanel() Method generates the corresponding... According to the properties of the current file stream FileChannel.

public FileChannel getChannel() {
        synchronized (this) {
            if (channel == null) {
                channel = FileChannelImpl.open(fd, path, false, true, append, this);
            }
            return channel;
        }
    }
}

Here is an example of the code used by the channel

public class NIOChannel {
    public static void main(String[] args) throws IOException {
    }

    // Write data to the target file 
    public static void writeFile() throws IOException{
        String str = "Hello, gofy";
        // Create a file output stream 
        FileOutputStream fileOutputStream = new FileOutputStream("f:\\file.txt");
        // Generate file channel according to file output stream 
        FileChannel fileChannel = fileOutputStream.getChannel();
        // Create a byte buffer , And the string will be converted into bytes and stored in 
        ByteBuffer byteBuffer = ByteBuffer.allocate(1024);
        byteBuffer.put(str.getBytes());
        // Be careful , When you need to write after saving , The buffer needs to be flipped 
        byteBuffer.flip();
        // Write buffer data to channel 
        fileChannel.write(byteBuffer);
        // Close the file output stream ( This method also closes the channel )
        fileOutputStream.close();
    }

    // Reading data from a file 
    public static void readFile() throws IOException{
        // Create a file input stream 
        File file = new File("f:\\file.txt");
        FileInputStream fileInputStream = new FileInputStream(file);
        // Generate file channel according to file input stream 
        FileChannel fileChannel = fileInputStream.getChannel();
        // Create a byte buffer , The size is the file size 
        ByteBuffer byteBuffer = ByteBuffer.allocate((int)file.length());
        // Read channel data into buffer 
        fileChannel.read(byteBuffer);
        // Again , When all data in the buffer needs to be taken out after reading , The buffer needs to be flipped 
        byteBuffer.flip();
        System.out.println(new String(byteBuffer.array()));
        fileInputStream.close();
    }

    // Transfer file data to another file 
    public static void readAndWriteFile() throws IOException{
        // Create file input stream and file output stream , And generate the corresponding channel 
        FileInputStream fileInputStream = new FileInputStream("file1.txt");
        FileChannel inputStreamChannel= fileInputStream.getChannel();
        FileOutputStream fileOutputStream = new FileOutputStream("file2.txt");
        FileChannel outputStreamChannel = fileOutputStream.getChannel();
        // Create a byte buffer 
        ByteBuffer byteBuffer = ByteBuffer.allocate(1024);
        // Read the data 
        while (true){
            // Clear buffer before reading 
            byteBuffer.clear();
            // Reads data from the file input channel into the buffer 
            int read = inputStreamChannel.read(byteBuffer);
            // When read by -1 when , That is, the channel data has been read 
            if (read == -1){
                break;
            }
            // After flipping the buffer , Write buffer data to the channel of file output 
            byteBuffer.flip();
            outputStreamChannel.write(byteBuffer);
        }
        fileInputStream.close();
        fileOutputStream.close();
    }

    // Copy and paste the document 
    public static void copyAndPaste() throws IOException{
        // Copied file input stream 
        FileInputStream fileInputStream = new FileInputStream("f:\\a.jpg");
        FileChannel srcChannel = fileInputStream.getChannel();
        // Pasted file output stream 
        FileOutputStream fileOutputStream = new FileOutputStream("f:\\b.jpg");
        FileChannel targetChannel = fileOutputStream.getChannel();
        // Use transferFrom Copy and paste 
        targetChannel.transferFrom(srcChannel, 0, srcChannel.size());
        fileInputStream.close();
        fileOutputStream.close();
    }
}

 

Selectors (Selector)

stay NIO In the program , You can use a selector Selector Realization One selector handles multiple channels , That is, a thread processes multiple connections . Just register the channel to Selector On , You can go through Selector To monitor the channel , If something happens to the channel , Then get the event channel and process each event accordingly . such , Only in the passage ( Connect ) There's real reading / Write about when the event happened , Then read and write , Greatly reduces system overhead , And you don't have to create a separate thread for each connection , You don't have to maintain too many threads .

The relevant class of the selector is in java.nio.channels Under the bag and its subpackages , The top class is Selector, It's an abstract class , Its common methods are :

Channel registration

stay ServerSocketChannel and SocketChannel Class has a registration method register(Selector sel, int ops),sel For the selector to register with ,ops The type of operation event monitored for this channel , You can use this method to ServerSocketChannel or SocketChannel Register in the target selector , This method returns a SelectionKey( The real implementation class is SelectionKeyImpl) Stored in the registered Selector Of publicKeys Set attribute .SelectionKey Stores the event type of the channel and the registered channel object , Can pass SelectionKey.channel() Method to get SelectionKey The corresponding channel .

Each channel registered with the selector needs to define the type of operation event to be performed , By looking at SelectionKey Class properties can be used to know the types of operation events 4 Kind of :

public static final int OP_READ = 1 << 0; // Read operations 
public static final int OP_WRITE = 1 << 2; // Write operations 
public static final int OP_CONNECT = 1 << 3; // Connection operation 
public static final int OP_ACCEPT = 1 << 4; // Receive operation 

Check the selector

We can go through the selector check method , Such as select() To find out the number of channels that have happened , When the number is greater than or equal to 0 when , That is, at least one channel has an event , You can use selectedKeys() Method to get the corresponding channel of all events SelectionKey, adopt SelectionKey To determine the type of event to be processed in the corresponding channel , According to the event to make the corresponding processing .

public final boolean isReadable() { // Determine whether it is a read operation 
    return (readyOps() & OP_READ) != 0;
}

public final boolean isWritable() { // Determine whether it is a write operation 
    return (readyOps() & OP_WRITE) != 0;
}

public final boolean isConnectable() { // Determine whether it is a join operation 
    return (readyOps() & OP_CONNECT) != 0;
}

public final boolean isAcceptable() { // Determine whether it is a receive operation 
    return (readyOps() & OP_ACCEPT) != 0;
}

 

NIO Realize simple chat group

// Server side 
public class GroupChatSever {
    private final static int PORT = 6666;// Listening port 
    private Selector selector;// Selectors 
    private ServerSocketChannel serverSocketChannel;

    public GroupChatSever(){
        try{
            selector = Selector.open();// Turn on the selector 
            serverSocketChannel = ServerSocketChannel.open();// Open the channel 
            serverSocketChannel.configureBlocking(false);// Set the channel to a non blocking state 
            serverSocketChannel.socket().bind(new InetSocketAddress(PORT));// Channel binding listening port 
            serverSocketChannel.register(selector, SelectionKey.OP_ACCEPT);// Register the channel to the selector , The event type is receive 
            listen();
        }catch (IOException e){
            e.printStackTrace();
        }
    }

    // Listen to the port 
    public void listen(){
        try {
            while (true){
                // Check the registration channel for events , The inspection time is 2 second 
                int count = selector.select(2000);
                if (count > 0){// If an event occurs in the registration channel, it will be handled 
                    // Get the corresponding channel of all events SelectionKey
                    Iterator<SelectionKey> keyIterator = selector.selectedKeys().iterator();
                    while (keyIterator.hasNext()){
                        SelectionKey key = keyIterator.next();
                        if (key.isAcceptable()){// Judge that key Whether the corresponding channel needs receiving operation 
                            // although accept() Method is blocked , But because the channel has been judged ,
                            // It can be confirmed that there is a client connection , So call accept It doesn't block 
                            SocketChannel socketChannel = serverSocketChannel.accept();
                            socketChannel.configureBlocking(false);
                            // After receiving , Register the acquired client channel to the selector , The event type is read 
                            socketChannel.register(selector, SelectionKey.OP_READ);
                            System.out.println(socketChannel.getRemoteAddress() + " go online !");
                        }
                        if (key.isReadable()){// Judge that key Whether the corresponding channel needs to be read 
                            readFromClient(key);
                        }
                        // Notice that when you're done with a channel key when , It needs to be removed from the iterator 
                        keyIterator.remove();
                    }
                }
            }
        }catch (IOException e){
            e.printStackTrace();
        }
    }

    /**
     *  Read the message from the client 
     * @param key  Corresponding to the channel to be read SelectionKey
     */
    public void readFromClient(SelectionKey key){
        SocketChannel socketChannel = null;
        try{
            // adopt SelectionKey Get the corresponding channel 
            socketChannel = (SocketChannel)key.channel();
            ByteBuffer byteBuffer = ByteBuffer.allocate(1024);
            int read = socketChannel.read(byteBuffer);
            if (read > 0){
                String message = new String(byteBuffer.array());
                System.out.println(" client : " + message);
                sendToOtherClient(message, socketChannel);
            }
        }catch (IOException e){
            // It's simplified here , All exceptions triggered by the client are disconnected , Please don't do this in actual projects 
            try{
                System.out.println(socketChannel.getRemoteAddress() + " Offline ");
                key.cancel();// Will be SelectionKey revoke 
                socketChannel.close();// Then close the corresponding channel 
            }catch (IOException e2){
                e2.printStackTrace();
            }
        }
    }

    /**
     *  Forward messages sent by clients to other clients 
     * @param message  Forward message 
     * @param from  The client channel that sends the message 
     * @throws IOException
     */
    public void sendToOtherClient(String message, SocketChannel from) throws IOException{
        System.out.println(" Message forwarding ......");
        for (SelectionKey key : selector.keys()){// Traverse all the... In the selector SelectionKey
            Channel channel = key.channel();// according to SelectionKey Get the corresponding channel 
            // Eliminate the channels that send messages , Write messages to other client channels 
            if (channel instanceof SocketChannel && channel != from){
                SocketChannel socketChannel = (SocketChannel)channel;
                ByteBuffer byteBuffer = ByteBuffer.wrap(message.getBytes());
                socketChannel.write(byteBuffer);
            }
        }
    }

    public static void main(String[] args) {
        GroupChatSever groupChatSever = new GroupChatSever();
    }
}
// client 
public class GroupChatClient {
    private final static String SEVER_HOST = "127.0.0.1";// Connected client host 
    private final static int SEVER_PORT = 6666;// The client port of the connection 
    private Selector selector;// Selectors 
    private SocketChannel socketChannel;
    private String username;// Storage client ip Address 

    public GroupChatClient(){
        try {
            selector = Selector.open();// Turn on the selector 
            socketChannel = SocketChannel.open(new InetSocketAddress(SEVER_HOST, SEVER_PORT));// Open the channel 
            socketChannel.configureBlocking(false);// Set the channel to non blocking 
            socketChannel.register(selector, SelectionKey.OP_READ);// Register the channel on the selector , The event type is read 
            username = socketChannel.getLocalAddress().toString().substring(1);// Get the client ip Address 
            String message = "  Join the chat group !";
            sendMessage(message);
        }catch (IOException e){
            e.printStackTrace();
        }
    }

    // Send a message 
    public void sendMessage(String message){
        message = username+": "+message;
        try{
            ByteBuffer byteBuffer = ByteBuffer.wrap(message.getBytes());
            socketChannel.write(byteBuffer);
        }catch (IOException e){
            e.printStackTrace();
        }
    }

    // Read messages sent from the server 
    public void readMessage(){
        try{
            int read = selector.select();
            if (read > 0){
                Iterator<SelectionKey> keyIterator = selector.selectedKeys().iterator();
                while (keyIterator.hasNext()){
                    SelectionKey key = keyIterator.next();
                    if (key.isReadable()){
                        SocketChannel socketChannel = (SocketChannel)key.channel();
                        ByteBuffer byteBuffer = ByteBuffer.allocate(1024);
                        socketChannel.read(byteBuffer);
                        System.out.println(new String(byteBuffer.array()));
                    }
                    keyIterator.remove();
                }
            }
        }catch (IOException e){
            e.printStackTrace();
        }
    }

    public static void main(String[] args) {
        final GroupChatClient groupChatClient = new GroupChatClient();
        // The client opens a thread to listen for messages forwarded by the server 
        new Thread(){
            @Override
            public void run() {
                while (true){
                    groupChatClient.readMessage();
                    try {
                        Thread.currentThread().sleep(1000);
                    }catch (InterruptedException e){
                        e.printStackTrace();
                    }
                }
            }
        }.start();
        
        Scanner scanner = new Scanner(System.in);
        while (scanner.hasNextLine()){
            String message = scanner.nextLine();
            groupChatClient.sendMessage(message);
        }
    }
}

 

版权声明
本文为[Little goofy]所创,转载请带上原文链接,感谢