IO model
Two stages of IO request (Linux)
- IO call phase : The user process initiates a system call to the kernel
IO execution stage : At this time, the user waits for the IO request processing to complete and return. This stage is divided into two steps
- Wait for the data to be ready and write to the kernel buffer
Data from the kernel buffer to the user mode buffer
- Kernel mode: run operating system programs and operate hardware
- User mode: Run user program
Five Linux IO models
1. Synchronous blocking IO (BIO)
The kernel can only process one request at the same time, divided into two stages (i.e. the aforementioned IO execution stage ):
- System call
- Data is read from the kernel buffer to the user buffer
Both operations are blocking, so other IO can only be processed after these two operations are completed.
2. Synchronous non-blocking IO (NIO)
Process requests will not wait all the time but there are dedicated threads to poll these IO processes for data, but there will be up and down switching caused by system calls during the polling process. If there are too many requests, there will be serious system performance. Consume
3.IO multiplexing
Multiplexer means plurality of data channels , multiplexing refers one or more fixed threads to process each Socket
connection, select
poll
epoll
are achieved IO multiplexing, a thread can select
multiple The data status of each data channel solves the problem of NIO
performance consumption of 061c0a88ca2a5a
-File descriptor fd
File descriptor is a non-negative integer in the form of an index value that points to the record table of files opened by the process maintained by the kernel for each process.
- select
This function will monitor 3 types of file descriptors, which are writefds
, readfds
, exceptfds
calling the select function, it will block until the select has the above 3 descriptor files ready or timeout. Once a descriptor is ready, the program will be notified to perform the related Because select poll epoll are synchronous IO, they all need to be responsible for reading and writing after the event is ready. That is, select will block and listen to related events, and will not unblock until the read and write operations are processed or after the timeout. The number of files that a single process can monitor is limited.Linux generally defaults to 1024
int select(int n,fd_set *readfds,fd_set *writefds,fd_set *exceptfds,struct timeval *timeout);
- poll
int poll (struct pollfd *fds, unsigned int nfds, int timeout);
struct pollfd {
int fd; /* file descriptor */
short events; /* requested events to watch */
short revents; /* returned events witnessed */
};
pollfd
uses a structure of 061c0a88ca2bd8 to conduct events that need to be monitored and events to occur. In addition, the number of file descriptors monitored by
- epoll
No polling, time complexity is O(1)
epoll_create create a whiteboard store fd_events
epoll_ctl for the kernel register or a new descriptor for a file descriptor changes state. The registered descriptors will be maintained on a red-black tree in the kernel
epoll_wait by callback kernel will be I / O ready descriptor added to a list in management, process calls epoll_wait()
can get complete event descriptor
Two trigger modes:
LT: Horizontal trigger
When epoll_wait()
detects the arrival of a descriptor event, it will notify the process of this event. The process may not process the event immediately. The next call to epoll_wait()
will notify the process again. It is the default mode, and supports Blocking
and No-Blocking
at the same time.
ET: Edge trigger
Unlike the LT mode, the process must process the event immediately after notification.
The next time you call epoll_wait()
, you will no longer be notified of the arrival of the event. greatly reduces the number of times the epoll event is triggered repeatedly, so the efficiency is higher than LT mode . No-Blocking
is supported to avoid starvation of the task of processing multiple file descriptors due to a blocked read/blocked write operation of a file handle.
4. Signal-driven model
The signal-driven model is not commonly used, it is a semi-asynchronous IO. When the data is ready, the kernel will send a SIGIO message to the application process, and the process will then start reading and writing messages.
5. Asynchronous IO
The system call will return the result immediately, and then the reading and writing of the message will be completed asynchronously.
BIO
BIO-Block-IO block synchronous communication method
BIO :
Blocking\synchronization, BIO is very dependent on the network, the network speed is not good, the blocking time will be very long; each request is executed and returned by the program, which is a synchronization defect
BIO workflow:
- Server start
- Block waiting for client connection
- Client connection
- Monitor client content
- Client disconnected
- Back to the first step
BioServer
public class BioServer {
public static void main(String[] args) {
try {
// 服务端绑定端口
ServerSocket server = new ServerSocket(9000);
while (true) {
// 创建一个Socket接收连接 - 当没有时阻塞
Socket socket = server.accept();
// 获取输入流
InputStream inputStream = socket.getInputStream();
BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream));
String message;
while (null != (message = reader.readLine())) {
System.out.println(message);
}
inputStream.close();
socket.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
BioClient
public class BioClient {
public static void main(String[] args) {
try {
// 创建socket
Socket socket = new Socket("localhost",9000);
// 获取Socket输出流
OutputStream outputStream = socket.getOutputStream();
// 输出流
outputStream.write("hello socket".getBytes());
// 关闭
outputStream.close();
socket.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
Multithreading solve BIO obstruction ask problem
- solved by , other threads can still process the requests sent by other clients. It avoids the problem of a request blocking that causes other client requests to wait all the time.
- is still a problem with added to the server. The given fixed number of threads is 10, and there are 10 clients who have created a connection but no one sends a message. Then all 10 threads will be blocked, or some clients will not operate for a long time. Necessary resource occupation.
Multi-threaded BioServer code
public class BioServer {
private static ExecutorService executorService = Executors.newFixedThreadPool(10);
public static void main(String[] args) {
ServerSocket serverSocket;
try {
serverSocket = new ServerSocket(9000);
while (true){
//new Thread(new BioHandler(serverSocket.accept()){}).start();
executorService.execute(new BioHandler(serverSocket.accept()));
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
public class BioHandler implements Runnable {
private Socket socket;
public BioHandler(Socket socket) {
this.socket = socket;
}
public void run() {
try {
InputStream input = socket.getInputStream();
BufferedReader reader = new BufferedReader(new InputStreamReader(input));
String m;
while (null != (m = reader.readLine())){
System.out.println(m);
}
input.close();
socket.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
NIO
java 1.4版本引入,给予缓冲区面 \ 向通道的io操作
bio | nio |
---|---|
Flow oriented | Buffer-oriented |
Blocking io | Non-blocking io |
Synchronize | Synchronize |
none | Selector (Selector) |
Buffer
buffer introduction
The buffer is a container of a specific data type, defined by the java.nio package, and all buffers are subclasses of the Buffer abstract class
Buffer is mainly used to communicate with the NIO channel. Data is read from the channel to the buffer, and then read from the buffer to the channel
Buffer is like a piece of data that can hold multiple data of the same type
Subclass
ByteBuffer
CharBuffer
ShortBuffer
IntBuffer
LongBuffer
FloatBuffer
DoubleBuffer
Basic attributes
1. capacity (capacity): means that the maximum capacity of the buffer cannot be modified once it is created
2. limit (limit): The , that is, the data behind limit is unreadable
3. position (position): the index of the next data to be read or written
4. flip: sets the position at this time to limit and the position to 0. Generally, the data is read from the inputChannel to the buffer, and then the buffer is flipped to read the data from the buffer outputChannel
5. mark and recovery (reset): The mark is an index, and a specific position is specified by Buffer.mark(), and the reset method can be used to restore to this position
public class BufferSample {
public static void main(String[] args) {
ByteBuffer buffer = ByteBuffer.allocate(1024);
System.out.println("capacity:" + buffer.capacity());
System.out.println("limit:" + buffer.limit(10));
System.out.println("position:" + buffer.position());
/**
* 结果:
* capacity:1024
* limit:java.nio.HeapByteBuffer[pos=0 lim=10 cap=1024]
* position:0
*/
System.out.println("==============================");
String str = "hello";
buffer.put(str.getBytes());
System.out.println("position:" + buffer.position());
/**
* 结果:
* position:5
*/
System.out.println("==============================");
System.out.println("pos 和 limit之间元素的个数:" + buffer.remaining());
buffer.mark();
buffer.put("oo".getBytes());
System.out.println("reset前position:" + buffer.position());
buffer.reset();
System.out.println("reset后position:" + buffer.position());
/**
* 结果:
* pos 和 limit之间元素的个数:5
* reset前position:7
* reset后position:5
*/
System.out.println("==============================");
buffer.rewind();
System.out.println("position:" + buffer.position());
/**
* 结果:
* position:0
*/
System.out.println("==============================");
byte[] dst = new byte[3];
buffer.get(dst);
System.out.println(new String(dst));
System.out.println("position:" + buffer.position());
/**
* 结果:
* hel
* position:3
*/
System.out.println("==============================");
//将此时的position转为limit,并将position置为0 - 一般flip以后就是开始读取缓冲区类
buffer.flip();
System.out.println("capacity:" + buffer.capacity());
System.out.println("limit:" + buffer.limit());
System.out.println("position:" + buffer.position());
byte[] b = new byte[buffer.limit()];
buffer.get(b,0,2);
System.out.println(new String(b,0,2));
/**
* 结果:
* capacity:1024
* limit:3
* position:0
* he
*/
}
}
direct/indirect buffer
- Direct buffer: the program directly manipulates the physical mapping file
- Indirect buffer: jvm-operating system-physical memory
Channel
Channel: similar to a stream, but the Channel cannot directly access the data and can only interact with the buffer
Channel body implementation class
1. FileChannel
: channel used to read, write, and manipulate files
2. DataGramChannel
: read the data channel in the network via UDP
3. SocketChannel
: Read and write channel data through Tcp
4. ServerSocketChannel
: It can monitor the newly entered Tcp connection and create a SocketChannel for each new connection
The class that provides the getChannel() method
1.FileInputStream
2.FileOutputStream
3.RandomAccessFile
4.Socket
5.ServerSocket
6.DataGramSocket
Channel direct transmission
1.transferFrom()
2.transferTo()
public class ChannelSimple {
/**
* 利用通道完成文件复制(非直接缓冲区)
*/
public static void FileNoDirectBufferTest(){
try {
//创建输入输出流
FileInputStream inputStream = new FileInputStream("../test.txt");
FileOutputStream outputStream = new FileOutputStream("../test2.txt");
//根据流获取通道
FileChannel inputChannel = inputStream.getChannel();
FileChannel outputChannel = outputStream.getChannel();
//创建缓冲区
ByteBuffer byteBuffer = ByteBuffer.allocate(1024);
//从通道读取数据到缓冲区
while (-1 != inputChannel.read(byteBuffer)){
//limit - position,position - 0
byteBuffer.flip();
//将缓冲区中的数据写出
outputChannel.write(byteBuffer);
byteBuffer.clear();
}
outputChannel.close();
inputChannel.close();
outputStream.close();
inputStream.close();
} catch (IOException e) {
e.printStackTrace();
}
}
/**
* 利用直接缓冲区完成文件复制(内存映射文件)
* @throws IOException
*/
public static void FileMpDirectBufferTest() throws IOException{
//创建通道
FileChannel inputChannel = FileChannel.open(Paths.get("../test.txt"), StandardOpenOption.READ);
FileChannel outputChannel = FileChannel.open(Paths.get("../test2.txt"),StandardOpenOption.CREATE,StandardOpenOption.WRITE,StandardOpenOption.READ);
//内存映射文件
MappedByteBuffer inputBuffer = inputChannel.map(FileChannel.MapMode.READ_ONLY,0,inputChannel.size());
MappedByteBuffer outputBuffer = outputChannel.map(FileChannel.MapMode.READ_WRITE,0,inputChannel.size());
//直接对缓冲区进行数据读写操作
byte [] dst = new byte[inputBuffer.limit()];
inputBuffer.get(dst);
outputBuffer.put(dst);
outputChannel.close();
inputChannel.close();
}
/**
* 利用直接缓冲区复制
* @throws IOException
*/
public static void FileDirectBufferTest() throws IOException {
//创建通道
FileChannel inputChannel = FileChannel.open(Paths.get("../test.txt"), StandardOpenOption.READ);
FileChannel outputChannel = FileChannel.open(Paths.get("../test2.txt"),StandardOpenOption.CREATE,StandardOpenOption.WRITE,StandardOpenOption.READ);
//inputChannel.transferTo(0,inputChannel.size(),outputChannel);
//等同 上面的注释
outputChannel.transferFrom(inputChannel,0,inputChannel.size());
outputChannel.close();
inputChannel.close();
}
}
Decentralized read and aggregate write
- Scatter reading (Scatter):
Channel
the data in oneBuffer
- Gathering write (Gather): write multiple
Buffer
to the sameChannel
public class ScatterAndGather {
public static void main(String[] args) {
try {
//创建输入输出流
FileInputStream inputStream = new FileInputStream("../test.txt");
FileOutputStream outputStream = new FileOutputStream("../test2.txt");
//根据流获取通道
FileChannel inputChannel = inputStream.getChannel();
FileChannel outputChannel = outputStream.getChannel();
//创建缓冲区
ByteBuffer byteBuffer1 = ByteBuffer.allocate((int)inputChannel.size()/2);
ByteBuffer byteBuffer2 = ByteBuffer.allocate((int)inputChannel.size()/2);
ByteBuffer[] byteBuffers = new ByteBuffer[]{byteBuffer1,byteBuffer2};
//从通道读取数据到缓冲区 - 分散写入
while (-1 != inputChannel.read(byteBuffers)){
for (ByteBuffer buffer:byteBuffers){
//limit - position,position - 0
buffer.flip();
}
//聚集写出
for (ByteBuffer buffer:byteBuffers) {
//将缓冲区中的数据写出
outputChannel.write(buffer);
buffer.clear();
}
}
outputChannel.close();
inputChannel.close();
outputStream.close();
inputStream.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
Selector
Selector
generally called a selector, also known as a multiplexer. It is used to check whether one or more channels are readable\writable, so that one thread can manage multiple Channel
using Selector
Use fewer threads to process Channel
, which can prevent performance consumption caused by context switching
Channel
can be multiplexed
Channel
that can be selected (multiplexed) are inherited from SelectableChannel
SelectableChannel
||
AbstractSelectableChannel
|| || ||
DataGramChannel SocketChannel ServerSocketChannel
So FileChannel
does not adapt to Selector
, that is, it cannot be switched to non-blocking mode
Selector uses basic steps
1. Create Selector: Selector selector = Selector.open();
2. Set to non-blocking as:
`channel.configureBlocking(false);`
3. Register Channel
to Selector
:
/**
* 参数-1:要注册到的多路复用器
* 参数-2:是一个"interest集合",即要监听事件的集合(有以下四种)
* OP_CONNECT 连接
* OP_ACEEPT 接收
* OP_READ 读
* OP_WRITE 写
*/
SelectionKey key = channel.register(selector,SelectionKey.OP_READ);
如果要监听多种事件如下:
SelectionKey key = channel.register(selector,SelectionKey.OP_CONNECT | SelectionKey.OP_READ);
4. Then it is ready to connect | ready to receive | ready to read | ready to write
Selector main method
method | describe |
---|---|
Set<SelectKey> keys() | Return all SelectionKey collections, on behalf of registered in this on the Selector |
Set<SelectKey> selectedKeys() | Return the selected one (that is, the one with io operation) SelectionKey |
int select() | Monitor all registered Channel , if there is an operation that requires io, the corresponding selectKey added to the selectedKeys collection, and the number returned is the number of selected (with io operation) Channel . During this operation, only the selected ones are blocked. Channel quantity>=1 before returning |
int select(timeout) | There is a timeout period, Channel that has not been operated by io appears, and it will automatically return after the timeout appears |
int selectNow() | Return immediately without blocking |
Selector wakeUp() | Make the select() return immediately |
void close() | closure |
SelectionKey main method
SelectionKey
represents the relationship between Channel
and Selector
Channel
to Selector
will generate a SelectionKey
method | describe |
---|---|
int interestOps() | Collection of events of interest boolean isInterested = interestSet & SelectionKey.OP_CONNECT ... |
int readyOps() | Get the channel ready for operation |
SelectableChannel channel() | Get registered channel |
Selector selector() | Get selector |
boolean isConnectable() | Check if there is a connection event ready in Channel |
boolean isAcceptable() | Check if there is a receive event ready in Channel |
boolean isReadaable() | Check if there is a read event ready in Channel |
boolean isWriteable() | Check if there is a write event ready in Channel |
Object attach() | Attach an object to SelectionKey , mainly for identification information |
Object attachment() | For registration information may also be in Channel attached registration information when SelectionKey key = channel.register(selector, SelectionKey.OP_READ, theObject); |
void cancel() | Request to cancel the registration of this key's channel to its selector |
NioServer
public class NioServer {
public static void main(String[] args) throws IOException {
Integer flag = 0;
//创建服务端通道
ServerSocketChannel serverSocketChannel = ServerSocketChannel.open();
//非阻塞模式
serverSocketChannel.configureBlocking(false);
//绑定端口
serverSocketChannel.bind(new InetSocketAddress(9021));
//创建选择器
Selector selector = Selector.open();
//注册 接收
serverSocketChannel.register(selector, SelectionKey.OP_ACCEPT);
//有一个事件时就操作
while (selector.select() > 0) {
//获取事件集合
Iterator<SelectionKey> iterator = selector.selectedKeys().iterator();
while (iterator.hasNext()) {
SelectionKey selectionKey = iterator.next();
//如果是接收就绪
if (selectionKey.isAcceptable()) {
//获取客户端连接
SocketChannel socketChannel = serverSocketChannel.accept();
//切换成非阻塞
socketChannel.configureBlocking(false);
//注册在多路复用器上 读
socketChannel.register(selector, SelectionKey.OP_READ);
//读事件
} else if (selectionKey.isReadable()) {
//获取客户端连接
SocketChannel socketChannel = (SocketChannel) selectionKey.channel();
//设置缓存
ByteBuffer byteBuffer = ByteBuffer.allocate(1024);
int len = 0;
while (-1 != (len = socketChannel.read(byteBuffer))) {
flag = 0;
byteBuffer.flip();
System.out.println(new String(byteBuffer.array(),0,len));
byteBuffer.clear();
}
flag++;
//判断此时是否有io事件,陷入空轮询 - 连续空轮询100次
//请求取消此键的通道在其选择器的注册,也就是 selector.select();的数量 -1
if(flag == 100){
selectionKey.cancel();
socketChannel.close();
}
}
}
iterator.remove();
}
}
}
NioClient
package com.yuan.nio.selector;
import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.SocketChannel;
public class NioClient {
public static void main(String[] args) {
try {
SocketChannel socketChannel = SocketChannel.open();
socketChannel.connect(new InetSocketAddress("localhost",9021));
ByteBuffer byteBuffer = ByteBuffer.allocate(1024);
byteBuffer.put("hello".getBytes());
byteBuffer.flip();
socketChannel.write(byteBuffer);
socketChannel.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。