NIO entry small case (to achieve simple data communication between the server and the client)

              The NIO (JDK1.4) model is a synchronous non-blocking IO, which has three core parts: Channel, Buffer, and Selector. Traditional IO operates based on byte stream and character stream, while NIO operates based on Channel and Buffer (buffer). Data is always read from the channel to the buffer, or written from the buffer to the channel. Selector (multiplexer) is used to monitor events of multiple channels (for example: connection open, data arrival). Therefore, a single thread can monitor multiple data channels.
              The first biggest difference between NIO and traditional IO (hereafter referred to as IO) is that IO is stream-oriented, while NIO is buffer-oriented. Java IO stream-oriented means that each time one or more bytes are read from the stream, until all the bytes are read, they are not cached anywhere. In addition, it cannot move data in the stream back and forth. If you need to move the data read from the stream back and forth, you need to cache it in a buffer first.
              The various streams of IO are blocked. This means that when a thread calls read() or write(), the thread is blocked until some data is read or the data is completely written. The thread can no longer do anything during this period. NIO's non-blocking mode allows a thread to send a request to read data from a channel, but it can only get the currently available data. If there is currently no data available, it will not get anything. Instead of keeping the thread blocked, the thread can continue to do other things until the data becomes readable. The same is true for non-blocking writes. A thread requests to write some data to a certain channel, but does not need to wait for it to be completely written, this thread can do other things at the same time. Threads usually use the idle time of non-blocking IO to perform IO operations on other channels, so a single thread can now manage multiple input and output channels.

NIO advantages:

  1. A kind of communication between the client and the server is realized through the state registered by the Channel to the Selector.
  2. The data in Channel is read through Buffer, a non-blocking way of reading.
  3. Selector multiplexer single-threaded model, thread resource overhead is relatively small.

 

Server code: NIOServer.java 

import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.SelectionKey;
import java.nio.channels.Selector;
import java.nio.channels.ServerSocketChannel;
import java.nio.channels.SocketChannel;
import java.util.Iterator;
import java.util.Set;

public class NIOServer {
    public static void main(String[] args) throws Exception{
        //创建一个serverSocketChannel
        ServerSocketChannel serverSocketChannel = ServerSocketChannel.open();

        //创建Selector
        Selector selector = Selector.open();

        //绑定端口6666,在服务端监听
        serverSocketChannel.socket().bind(new InetSocketAddress(6666));

        //设置非阻塞模式
        serverSocketChannel.configureBlocking(false);

        //将channel注册到selector中,关心事件为op_ACCEPT
        serverSocketChannel.register(selector, SelectionKey.OP_ACCEPT);

        //循环监听客户端连接
        while (true){
            //selector等待1s钟,如果没有事件发生则可以去做别的事情
            if(selector.select(1000)==0){
                System.out.println("服务端等待了1s,没有事件发生");
                continue;
            }

            //如果>0,则得到selectionKeys集合,已经获取到关注的事件了,selectionKeys是关注事件的集合
            //通过selectionKeys反向获取通道
            Set<SelectionKey> selectionKeys = selector.selectedKeys();

            //遍历selectionKeys集合,使用迭代器
            Iterator<SelectionKey> iterator = selectionKeys.iterator();

            while (iterator.hasNext()){
                SelectionKey key = iterator.next();

                //根据key对应的通道发生的事件做相应的处理
                //相当于有客户端连接,给该客户端生成一个socketchannel
                if(key.isAcceptable()){
                    SocketChannel socketChannel = serverSocketChannel.accept();
                    //设置为非阻塞
                    socketChannel.configureBlocking(false);
                    System.out.println("客户端连接成功,生成了一个socketchannel"+socketChannel.hashCode());
                    //将sockerchannel注册到selector,关注事件为read,同行关联一个buffer
                    socketChannel.register(selector,SelectionKey.OP_READ, ByteBuffer.allocate(1024));
                }
                if(key.isReadable()){
                    //通过key反向获取socketChannel
                    SocketChannel channel = (SocketChannel)key.channel();
                    //获取到该channel关联的buffer
                    ByteBuffer buffer=(ByteBuffer)key.attachment();
                    channel.read(buffer);
                    System.out.println("客户端"+new String(buffer.array()));
                }
                //手动从集合中移除当前的selectionkey,防止重复操作
                iterator.remove();
            }
        }
    }
}

 

Two client code

NIOClient.java

import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.SocketChannel;

public class NIOClient {
    public static void main(String[] args) throws Exception{
        //得到一个网络通道
        SocketChannel socketChannel = SocketChannel.open();
        //设置非阻塞
        socketChannel.configureBlocking(false);
        //提供服务器端的IP和端口
        InetSocketAddress inetSocketAddress = new InetSocketAddress("127.0.0.1", 6666);
        //连接服务器
        if(!socketChannel.connect(inetSocketAddress)){
            while (!socketChannel.finishConnect()){
                System.out.println("因为连接需要时间,客户端没有阻塞,可以做其他工作。。");
            }
        }

        //如果连接成功,发送数据
        String str="客户端ZTY已连接";
        //直接将字符串对应的字节数组包裹到buffer中,不用指定大小
        ByteBuffer buffer = ByteBuffer.wrap(str.getBytes());
        //发送数据,将buffer写入到channel
        socketChannel.write(buffer);
        System.in.read();
    }
}

NIOClient2.java

import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.SocketChannel;

public class NIOClient2 {
    public static void main(String[] args) throws Exception{
        //得到一个网络通道
        SocketChannel socketChannel = SocketChannel.open();
        //设置非阻塞
        socketChannel.configureBlocking(false);
        //提供服务器端的IP和端口
        InetSocketAddress inetSocketAddress = new InetSocketAddress("127.0.0.1", 6666);
        //连接服务器
        if(!socketChannel.connect(inetSocketAddress)){
            while (!socketChannel.finishConnect()){
                System.out.println("因为连接需要时间,客户端没有阻塞,可以做其他工作。。");
            }
        }

        //如果连接成功,发送数据
        String str="客户端LPJ已连接";
        //直接将字符串对应的字节数组包裹到buffer中,不用指定大小
        ByteBuffer buffer = ByteBuffer.wrap(str.getBytes());
        //发送数据,将buffer写入到channel
        socketChannel.write(buffer);
        System.in.read();
    }
}

Run the server first. Screenshot below:

After running client 1 (NIOClient.java), the server interface produces output:

 After running client 2 (NIOClient2.java), the server interface produces output:

 

After finishing NIO, it is not far from mastering Netty, because Netty is based on NIO. 

Guess you like

Origin blog.csdn.net/Zhongtongyi/article/details/107348605