Java Network IO (BIO,NIO,AIO)
1.BIO
BIO, called Block-IO, is a synchronous blocking communication mode. Stock IO is commonly referred to as BIO. It is a more traditional way of communication with simple mode and convenient use. However, the transmission processing capacity is low, communication time-consuming and depends on network speed.
BIO Design Principle:
The server is responsible for monitoring client requests and creating a new thread for each client for link processing through an Acceptor thread. A typical request-response model. If the number of clients increases, creating and destroying threads frequently will put a lot of pressure on the server. The latter is called pseudo-asynchronous IO, which uses thread pool instead of new threads.
The server provides IP address and port for monitoring. The client connects to the server through three handshakes of TCP. After successful connection, dual playback can communicate through socket (Stock).
Summary: Socket and Server Socket are used to implement socket channel in BIO model. Blocking, synchronization, and connection establishment are time-consuming.
1.1 BIO
BIO server code, responsible for starting services, blocking services, listening to client requests, new thread processing tasks.
/**
* ServerSocket Server-side code
* Created by lyyz on 2018/5/24.
*/
public class BioSocketServer {
public static void main(String[] args) {
ServerSocket serverSocket = null;
Socket socket = null;
try {
//socket address
SocketAddress socketAddress = new InetSocketAddress("127.0.0.1",8765);
// Create ServerScoket Server
serverSocket = new ServerSocket();
// Binding address
serverSocket.bind(socketAddress);
// Loop Blocking Waiting for Client Connection
while(true){
socket = serverSocket.accept();
//Create a new thread to execute the client's request and reply to the response
Thread thread = new Thread(new BioSocketHandler(socket));
thread.start();
}
} catch (IOException e) {
e.printStackTrace();
}finally {
if(serverSocket != null){
try {
serverSocket.close();
} catch (IOException e) {
e.printStackTrace();
}
}
serverSocket = null;
}
}
}
/**
* Specific Processors of Responses
* Created by lyyz on 2018/5/24.
*/
public class BioSocketHandler implements Runnable {
private Socket socket;
public BioSocketHandler(Socket socket) {
this.socket = socket;
}
@Override
public void run() {
BufferedReader in = null;
PrintWriter out = null;
try {
in = new BufferedReader(new InputStreamReader(socket.getInputStream()));
out = new PrintWriter(new OutputStreamWriter(socket.getOutputStream()),true);
String request;
request = in.readLine();
//Receive client requests and respond
System.out.println(request);
out.println("Bio Server response data response");
} catch (IOException e) {
e.printStackTrace();
}finally {
if(in != null){
try {
in.close();
} catch (IOException e) {
e.printStackTrace();
}
}
if(out != null){
try {
out.close();
} catch (Exception e) {
e.printStackTrace();
}
}
if(socket != null){
try {
socket.close();
} catch (IOException e) {
e.printStackTrace();
}
}
socket = null;
}
}
}
/**
* Client Code
* Created by lyyz on 2018/5/24.
*/
public class BioSocketClient {
public static void main(String[] args) {
Socket socket =null;
PrintWriter out = null;
BufferedReader in = null;
try {
// Create Socket
socket = new Socket();
SocketAddress socketAddress = new InetSocketAddress("127.0.0.1",8765);
//Connect servers
socket.connect(socketAddress);
out = new PrintWriter(new OutputStreamWriter(socket.getOutputStream()),true);
in = new BufferedReader(new InputStreamReader(socket.getInputStream()));
//Send a request to the server
out.println("Bio Client sends request information request");
String response;
//Receive server response data
response = in.readLine();
System.out.println(new String(response));
} catch (IOException e) {
e.printStackTrace();
}finally {
if(in != null){
try {
in.close();
} catch (IOException e) {
e.printStackTrace();
}
}
if(out != null){
try {
out.close();
} catch (Exception e) {
e.printStackTrace();
}
}
if(socket != null){
try {
socket.close();
} catch (IOException e) {
e.printStackTrace();
}
}
socket = null;
}
}
}
1.2 Pseudo-Asynchronous I/O Programming
To improve this one-connection-one-thread model, we can use thread pools to manage these threads (see the previous article for more information), implement one or more threads to process N client models (but synchronous blocking I/O is still used at the bottom), commonly referred to as the "pseudo-asynchronous I/O model".
The implementation is very simple. We just need to put the new thread into the thread pool management. We just need to change the Server code.
/**
* Pseudo-asynchronous IO mode
* ServerSocket Server-side code
* Created by lyyz on 2018/5/24.
*/
public class Bio2SocketServer {
public static void main(String[] args) {
// Create a thread pool to avoid creating a thread every client connection
ExecutorService executorService = new ThreadPoolExecutor(Runtime.getRuntime().availableProcessors(),100,120, TimeUnit.SECONDS,new ArrayBlockingQueue<Runnable>(400));
ServerSocket serverSocket = null;
Socket socket = null;
try {
//socket address
SocketAddress socketAddress = new InetSocketAddress("127.0.0.1",8765);
// Create ServerScoket Server
serverSocket = new ServerSocket();
// Binding address
serverSocket.bind(socketAddress);
// Loop Blocking Waiting for Client Connection
while(true){
socket = serverSocket.accept();
//Create a new thread to execute the client's request and reply to the response
executorService.execute(new BioSocketHandler(socket));
}
} catch (IOException e) {
e.printStackTrace();
}finally {
if(serverSocket != null){
try {
serverSocket.close();
} catch (IOException e) {
e.printStackTrace();
}
}
serverSocket = null;
}
}
}
2.NIO
NIO, also known as Non-Block IO, is a non-blocking synchronous communication mode.
NIO is a big improvement over BIO. The client and server communicate through Channel. NIO can read and write on Channel. These Channels are registered on Selector multiplexers. Selector polls these Channels continuously through a thread. Find Channel ready to perform IO operations.
NIO implements tens of millions of client requests through a thread polling, which is the characteristic of non-blocking NIO.
NIO provides two different socket channel implementations, Socket Channel and Server Socket Channel, corresponding to the Socket and Server Socket in the traditional BIO model.
Both new channels support both blocking and non-blocking modes.
Blocking mode is as simple as traditional support, but its performance and reliability are not good; non-blocking mode is just the opposite.
For low-load, low-concurrent applications, synchronous blocking I/O can be used to improve development speed and better maintainability; for high-load, high-concurrent (network) applications, NIO non-blocking mode should be used to develop.
Buffer: It's an important difference between NIO and BIO. BIO is to write or read data directly into Stream objects. NIO data operations are performed in buffers. The buffer is actually an array. The most common type of Buffer is ByteBuffer, along with Char Buffer, Short Buffer, Int Buffer, Long Buffer, Float Buffer, Double Buffer.
Channel: Unlike streams, channels are bidirectional. NIO can read, write and simultaneously read and write data through Channel. Channels can be divided into two categories: Selectable Channel and File Channel. Both Socket Channel and Server Socket Channel are subclasses of Selectable Channel.
Multiplexer Selector: The basis of NIO programming. Multiplexers provide the ability to select tasks that are ready. Selector polls the Channel registered on it continuously. If a Channel is ready, it will be polled by Selector. Then, through SelectionKey, it can get the set of Channels ready for subsequent IO operations. As long as the server provides a thread responsible for polling the Selector, it can access thousands of clients, which is the great progress of JDK NIO library.
Note: The code here only realizes the function of sending requests from clients and receiving data from servers. The aim is to simplify the code and make it easy to understand. There is complete code in the github source code.
Summary: Socket Channel and Server Socket Channel are used to implement socket channel in NIO model. Non-blocking/blocking, synchronization, avoiding the overhead of three handshakes for TCP connection establishment.
NIO server code, responsible for opening multiplexer, opening channels, registration channels, polling channels, processing channels.
/**
* Created by lyyz on 2018/5/24.
*/
public class NioSocketServer implements Runnable{
//1 Multiplexer (manage all channels)
private Selector selector;
// Read buffer
private ByteBuffer readBuffer = ByteBuffer.allocate(1024);
public NioSocketServer() {
try {
// 1. Turn on multiplexer
selector = Selector.open();
//2. Open Server Channel (Network Read-Write Channel)
ServerSocketChannel serverSocketChannel = ServerSocketChannel.open();
// 3. Set the server channel as non-blocking mode, true as blocking mode and false as non-blocking mode.
serverSocketChannel.configureBlocking(false);
// 4. Binding ports
serverSocketChannel.bind(new InetSocketAddress("127.0.0.1",8765));
// 5 Register Server Channel to Multiplexer
// SelectionKey.OP_READ: Expresses concern about read data ready events
// SelectionKey.OP_WRITE: Expresses concern about write data ready events
// SelectionKey.OP_CONNECT: Expresses concern about socket channel connection completion events
// SelectionKey.OP_ACCEPT: Expresses concern about the accept event of server-socket channel
serverSocketChannel.register(selector,SelectionKey.OP_ACCEPT);
System.out.println("Server initialization completed-------------");
} catch (IOException e) {
e.printStackTrace();
}
}
@Override
public void run() {
while(true){
try {
/**
* a.select() Blocked to at least one channel ready for your registered event
* b.select(long timeOut) Blocked to at least one channel ready for your registered event or timeOut timeout
* c.selectNow() Return immediately. If there is no ready channel, return to 0
* select The return value of the method represents the number of ready channels.
*/
// 1. Multiplexer listening blocking
selector.select();
// 2. Result Set Selected by Multiplexer
Iterator<SelectionKey> selectionKeys = selector.selectedKeys().iterator();
// 3. Continuous polling
while (selectionKeys.hasNext()) {
// 4. Get a selected key
SelectionKey key = selectionKeys.next();
// 5. Remove it from the container after acquisition
selectionKeys.remove();
// 6. Get only valid key s
if (!key.isValid()) {
continue;
}
// Blocking state processing
if (key.isAcceptable()) {
accept(key);
}
// Readable state processing
if (key.isReadable()) {
read(key);
}
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
// Set the block and wait for the Client request. In traditional IO programming, ServerSocket and Socket are used. Server Socket Channel and Socket Channel used in NIO
private void accept(SelectionKey selectionKey) {
try {
// 1. Access Channel Service
ServerSocketChannel serverSocketChannel = (ServerSocketChannel) selectionKey.channel();
// 2. Execution of blocking methods
SocketChannel socketChannel = serverSocketChannel.accept();
// 3. Set the server channel as non-blocking mode, true as blocking mode and false as non-blocking mode.
socketChannel.configureBlocking(false);
// 4. Register channels on multiplexers and set read identifiers
socketChannel.register(selector, SelectionKey.OP_READ);
} catch (IOException e) {
e.printStackTrace();
}
}
private void read(SelectionKey selectionKey) {
try {
// 1. Emptying Buffer Data
readBuffer.clear();
// 2. Get the channel registered on the multiplexer
SocketChannel socketChannel = (SocketChannel) selectionKey.channel();
// 3. Read the data and return it.
int count = socketChannel.read(readBuffer);
// 4. Return content - 1 indicates no data
if (-1 == count) {
selectionKey.channel().close();
selectionKey.cancel();
return ;
}
// 5. If there is data, reset operation is performed before reading data.
readBuffer.flip();
// 6. Create a bytes array of the corresponding size according to the buffer size to get the value
byte[] bytes = new byte[readBuffer.remaining()];
// 7. Receiving Buffer Data
readBuffer.get(bytes);
// 8. Print the acquired data
System.out.println("NIO Server : " + new String(bytes)); // You cannot use bytes.toString()
} catch (IOException e) {
e.printStackTrace();
}
}
public static void main(String[] args) {
new Thread(new NioSocketServer()).start();
}
}
/**
* Created by lyyz on 2018/5/24.
*/
public class NioSocketClient {
public static void main(String[] args) {
// 1. Create a connection address
InetSocketAddress inetSocketAddress = new InetSocketAddress("127.0.0.1", 8765);
// 2. Declare a connection channel
SocketChannel socketChannel = null;
// 3. Create a buffer
ByteBuffer byteBuffer = ByteBuffer.allocate(1024);
try {
// 4. Open the Channel
socketChannel = SocketChannel.open();
// 5. Connecting servers
socketChannel.connect(inetSocketAddress);
while(true){
// 6. Define a byte array, and then use the system input function:
byte[] bytes = new byte[1024];
// 7. Keyboard input data
System.in.read(bytes);
// 8. Put the data in the buffer
byteBuffer.put(bytes);
// 9. Reset the buffer
byteBuffer.flip();
// 10. Write out the data
socketChannel.write(byteBuffer);
// 11. Emptying Buffer Data
byteBuffer.clear();
}
} catch (IOException e) {
e.printStackTrace();
} finally {
if (null != socketChannel) {
try {
socketChannel.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}
3.AIO
AIO, also known as NIO 2.0, is a non-blocking asynchronous communication mode. On the basis of NIO, a new concept of asynchronous channel is introduced, and the implementation of asynchronous file channel and asynchronous socket channel is provided.
AIO does not use NIO multiplexers, but uses the concept of asynchronous channels. The read and write method return types are Future objects. The Future model is asynchronous, and its core idea is to remove the waiting time of the main function.
Summary: In AIO model, socket channel is implemented by Asynchronous Socket Channel and Asynnous Server Socket Channel. Non-blocking, asynchronous.
/**
* AIO, Also known as NIO 2.0 is an asynchronous non-blocking communication mode.
* AIO The concept of asynchronous channel is introduced. Asynchronous Server Socket Channel and Asynnous Socket Channel whose read and write method return value types are Future objects.
*/
public class AioSocketServer {
private ExecutorService executorService; // Thread pool
private AsynchronousChannelGroup threadGroup; // Channel group
public AsynchronousServerSocketChannel asynServerSocketChannel; // Server channel
public void start(Integer port){
try {
// 1. Create a cache pool
executorService = Executors.newCachedThreadPool();
// 2. Creating Channel Groups
threadGroup = AsynchronousChannelGroup.withCachedThreadPool(executorService, 1);
// 3. Create server channels
asynServerSocketChannel = AsynchronousServerSocketChannel.open(threadGroup);
// 4. Binding
asynServerSocketChannel.bind(new InetSocketAddress(port));
System.out.println("server start , port : " + port);
// 5. Waiting for Client Request
asynServerSocketChannel.accept(this, new AioServerHandler());
// The server is blocked all the time, and the real environment is running under tomcat, so this line of code is not needed.
Thread.sleep(Integer.MAX_VALUE);
} catch (Exception e) {
e.printStackTrace();
}
}
public static void main(String[] args) {
AioSocketServer server = new AioSocketServer();
server.start(8888);
}
}
public class AioSocketClient implements Runnable{
private static Integer PORT = 8888;
private static String IP_ADDRESS = "127.0.0.1";
private AsynchronousSocketChannel asynSocketChannel ;
public AioSocketClient() throws Exception {
asynSocketChannel = AsynchronousSocketChannel.open(); // Open the channel
}
public void connect(){
asynSocketChannel.connect(new InetSocketAddress(IP_ADDRESS, PORT)); // Creating connections is the same as NIO
}
public void write(String request){
try {
asynSocketChannel.write(ByteBuffer.wrap(request.getBytes())).get();
ByteBuffer byteBuffer = ByteBuffer.allocate(1024);
asynSocketChannel.read(byteBuffer).get();
byteBuffer.flip();
byte[] respByte = new byte[byteBuffer.remaining()];
byteBuffer.get(respByte); // Put buffer data into byte arrays
System.out.println(new String(respByte,"utf-8").trim());
} catch (Exception e) {
e.printStackTrace();
}
}
@Override
public void run() {
while(true){
}
}
public static void main(String[] args) throws Exception {
for (int i = 0; i < 10; i++) {
AioSocketClient myClient = new AioSocketClient();
myClient.connect();
new Thread(myClient, "myClient").start();
myClient.write("aaaaaaaaaaaaaaaaaaaaaa");
}
}
}
public class AioServerHandler implements CompletionHandler<AsynchronousSocketChannel, AioSocketServer> {
private final Integer BUFFER_SIZE = 1024;
@Override
public void completed(AsynchronousSocketChannel asynSocketChannel, AioSocketServer attachment) {
// Ensure that multiple clients can block
attachment.asynServerSocketChannel.accept(attachment, this);
read(asynSocketChannel);
}
//Read data
private void read(final AsynchronousSocketChannel asynSocketChannel) {
ByteBuffer byteBuffer = ByteBuffer.allocate(BUFFER_SIZE);
asynSocketChannel.read(byteBuffer, byteBuffer, new CompletionHandler<Integer, ByteBuffer>() {
@Override
public void completed(Integer resultSize, ByteBuffer attachment) {
//After reading, reset the identifier bit
attachment.flip();
//Get the number of bytes read
System.out.println("Server -> " + "The length of data received from the client is:" + resultSize);
//Get the read data
String resultData = new String(attachment.array()).trim();
System.out.println("Server -> " + "The data information received from the client is:" + resultData);
String response = "Server response, Received data from client: " + resultData;
write(asynSocketChannel, response);
}
@Override
public void failed(Throwable exc, ByteBuffer attachment) {
exc.printStackTrace();
}
});
}
// Write data
private void write(AsynchronousSocketChannel asynSocketChannel, String response) {
try {
// Write the data into the buffer
ByteBuffer buf = ByteBuffer.allocate(BUFFER_SIZE);
buf.put(response.getBytes());
buf.flip();
// Write from buffer to channel
asynSocketChannel.write(buf).get();
} catch (InterruptedException e) {
e.printStackTrace();
} catch (ExecutionException e) {
e.printStackTrace();
}
}
@Override
public void failed(Throwable exc, AioSocketServer attachment) {
exc.printStackTrace();
}
}
Reference to this blog
https://blog.csdn.net/anxpp/article/details/51512200
https://segmentfault.com/a/1190000012976683