Network Security Internet Technology Development Database Servers Mobile Phone Android Software Apple Software Computer Software News IT Information

In addition to Weibo, there is also WeChat

Please pay attention

WeChat public account

Shulou

What is it, IO stream?

2025-01-16 Update From: SLTechnology News&Howtos shulou NAV: SLTechnology News&Howtos > Development >

Share

Shulou(Shulou.com)06/02 Report--

This article introduces the relevant knowledge of "what is the IO flow". In the operation of actual cases, many people will encounter such a dilemma. Then let the editor lead you to learn how to deal with these situations. I hope you can read it carefully and be able to achieve something!

Traditional BIO

Java IO stream is a huge ecological environment, which provides many different input and output streams, including byte stream and character stream, and even buffer stream to improve IO performance. Conversion stream converts byte stream into character stream and has fear of IO. IO operations on files are necessary in daily development, although apache already provides encapsulated components such as Commons IO. However, in the face of special scenarios, we still need to encapsulate a high-performance file IO utility class. This article will analyze the various classes involved in Java IO and explain how to use them correctly and efficiently.

The difference between BIO NIO and AIO

We will use a classic example of boiling water to explain the difference between them.

Type boiled water BIO has been monitoring a kettle, the kettle boiled water and then monitor the next kettle NIO every once in a while to see the status of all the kettles, which kettle boiled water to deal with which kettle AIO does not need to monitor the kettle, each kettle boiled water will take the initiative to inform the thread: "my water is boiling, come and deal with me."

BIO (synchronous blocking Istroke O)

Suppose a scene of boiling water, there is a row of kettles boiling water, BIO's mode of work is that the little pineapple looks at the kettle until it boils before dealing with the next kettle. The thread did nothing while waiting for the kettle to boil.

NIO (synchronous non-blocking Istroke O)

Take boiling water as an example. NIO's practice is that the small pineapple plays with the phone and looks at the status of each kettle every once in a while to see if the state of the kettle has changed. if a kettle boils, you can first deal with the kettle, then continue to play with the phone, and then continue to check the status of each kettle once in a while.

AIO (Asynchronous non-blocking Istroke O)

The little pineapple thought it was too hard to have a look at the kettle every once in a while, so she bought a batch of kettles that could beep when boiling water. After the little pineapple began to boil the water, she went directly to the living room to play with her mobile phone. When the water was boiled, she beeped and told the little pineapple to turn off the kettle.

What is flow?

Popular science of knowledge: we know that any file exists in the device in binary form, and the computer has only 0 and 1, and everything you can see is made up of these two numbers. When you read this article, this article is also made up of 01, but these binary strings have evolved into words and pictures on the screen after various transformations.

Stream is to transfer these binary strings between various devices. If you think it's a little abstract, I'll give you an example to understand it better:

"the picture below is a picture, which consists of 01 strings. We can copy a picture to a folder through the program.

Convert the picture into a binary data set and transfer the data to the folder bit by bit, similar to the flow of water, so that the whole data is a data stream. "

The characteristics of IO stream for reading and writing data:

Read and write sequentially. When reading and writing data, in most cases, it is read and written sequentially, from the first byte of the file to the last byte, and also when writing out (RandomAccessFile can read and write randomly)

Byte array. Reading and writing data is essentially a read and write operation to the byte array, even if it is a character stream, it is converted into characters on the basis of the byte stream, so the byte array is the essence of IO stream reading and writing data.

Classification of streams

Classified according to data flow: input stream and output stream

Input stream: input data into a process from a disk or other device

Output stream: output data from a process to disk or other device for saving

The hard disk in the picture is just one of the devices, and there are many other devices that can be used in IO streams, such as printers, hard drives, monitors, mobile phones.

Classified according to the basic units of data processing: byte stream and character stream

Byte stream: data transmission in bytes (8 bit)

Character stream: data transmission in characters (1 character = 2 bytes)

"the essence of character stream is also read through byte stream. The characters in Java are based on Unicode standard. In the process of reading and output, byte is converted to corresponding character by looking up the corresponding code table in the unit of character."

Faced with byte streams and character streams, many readers wonder: when do you need to use a byte stream and when do you want to use a character stream?

I'll make a simple summary here, and you can use it according to this standard:

Character stream is only transmitted for character data, so if it is text data, character stream transmission is preferred; in addition, other types of data (pictures, audio, etc.) are best transmitted by byte stream.

Based on these two different categories, we can make the following table, which contains the four core top-level abstract classes in IO:

Data flow / data type byte stream character stream input stream InputStreamReader output stream OutputStreamWriter

Now to see if IO has some ideas, it won't be very confusing. Let's take a look at all the members under these four categories.

[from cxuan's "Java Foundation Core Summary"]

Don't panic, most of the input stream and output stream under byte stream and character stream correspond to each other one by one. With the support of the table above, we no longer need to worry about seeing a class confused.

When you see Stream, you know it's a byte stream, and when you see Reader / Writer, you know it's a character stream.

One additional point here: Java IO provides a transformation class that converts a byte stream into a character stream, called a transformation stream.

Conversion stream / data type conversion between byte stream and character stream (input) byte stream = > character stream InputStreamReader (output) character stream = > byte stream OutputStreamWriter

Note that the conversion between byte stream and character stream is strictly defined:

Input stream: you can stream byte stream = > character stream

Output stream: you can set character stream = > byte stream

Why can't character stream = > byte stream in input stream and byte stream = > character stream in output stream?

"on storage devices, all data is stored in bytes, so input to memory must be in bytes, and output to storage devices must be in bytes. Byte stream is the most fundamental way of computer storage, while character stream converts data on the basis of byte stream and outputs characters, but each character is still stored in bytes."

Node flow and processing flow

You need to insert an additional section here to explain the node flow and the processing flow.

Node flow: a node flow is a stream object that actually transmits data, which is used to read and write data to a specific place (node). It is called a node flow. For example, FileInputStream

Processing flow: the processing flow is the encapsulation of the node flow, using the outer processing flow to read and write data, essentially using the function of the node flow, the outer processing flow can provide additional functions. The base classes for processing streams all start with Filter.

The above figure encapsulates ByteArrayInputStream into DataInputStream, which can convert the input byte array into data of the corresponding data type. For example, if you want to read int type data, it will be converted to a number in 2 bytes.

The core class File of Java IO

Java provides the File class, which points to files and directories in the computer operating system, through which only files and directories can be accessed, not content. It provides three main operations internally:

Access file attributes: absolute path, relative path, file name

File detection: whether a file, a directory, whether a file exists, read / write / execute permissions of a file

Manipulate files: create directories, create files, delete files

The operations illustrated above are very commonly used in development. The File class is much more than these operations. More operations can be found directly in the API document according to the requirements.

Access the properties of the file:

The API function String getAbsolutePath () returns the absolute pathname of the file in the system String getPath () returns the relative path of the file, usually the same as the path passed in by new File () String getName () returns the file name of the file

File detection:

API function boolean isFIle () verifies whether the path points to a file boolean isDirectory () checks whether the path points to a directory boolean isExist () verifies whether the file / directory pointed to by the path exists boolean canWrite () verifies whether the file is writable boolean canRead () verifies whether the file is readable boolean canExecute () verifies whether the file / directory can be executed

Operation file:

API function mkdirs () recursively creates multiple folders. It is possible that createNewFile () does not exist in some folders in the middle of the path to create new files. It is an atomic operation with two steps: check the existence of files, create new files delete () to delete files or directories, and make sure that the directory is empty when deleting the directory.

Know more.

The read / write / execute permissions of files are usually not shown in Windows, but this can be well reflected in Linux, because Linux has strict user rights grouping, and users under different groups have different permissions to operate on files, so these methods are better understood in Linux than in Windows. The following figure shows the details of some files in the redis folder, and the execution permissions of different users are marked in a red box:

R (Read): indicates that the file can be read by the current user, and the sequence number of the operation permission is 4

W (Write): indicates that the file can be written by the current user, and the sequence number of the operation permission is 2

X (Execute): this file can be executed by the current user, and the sequence number of the operation permission is 1

Root root represents the owner of the current file and the grouping of users to which the current file belongs. There are three types of users' permissions to operate files under Linux:

File owner: the permission is the first three letters in the red box,-indicates that there is no permission

All users of the group in which the file belongs: the permissions are the middle three letters in the red box

All users in other groups: the permissions are the last three letters in the red box

Java IO stream object

There are two categories of retrospective streams:

According to the data flow direction, it is divided into input stream and output stream.

According to the data type, it is divided into byte stream and character stream.

Therefore, this section will take the byte stream and the character stream as the main segmentation point, and then subdivide it into the input stream and the output stream.

Byte stream object

Byte stream objects most input streams and output streams appear in pairs, so when learning, you can associate the one-to-one corresponding stream objects of the input stream and the output stream, the input stream and the output stream just have different data flows, and the data can be processed in the same way.

Be careful not to think that what stream is used to read the data needs to write out the data with the corresponding stream, which is not specified in Java. The following figure is only a corresponding relationship between objects, not a mandatory association between two classes.

"there are a lot of classes below. I will introduce the methods of the base class. It is very necessary to understand these methods. The function of the subclass is extended based on the parent class. Only if you really understand what the parent class is doing, the cost of learning the subclass will be reduced."

InputStream

InputStream is the abstract base class of byte input streams and provides a common way to read them so that subclasses can use or override them. Here are some important methods commonly used by InputStream.

The important method function public abstract int read () reads the next byte from the input stream and returns-1public int read (byte b []) when it is read to the tail. Public int read (byte b [], int off, int len) reads the bytes of length b.length from the input stream into the byte array b (byte b [], int off, int len) reads the specified range of byte data from the input stream into the byte array b. Public void close () closes the input stream and releases all resources associated with the input stream.

There are other methods that are less commonly used, which I have also listed.

Other method functions public long skip (long n) skip the next n bytes, return the number of bytes actually skipped public long available () return the estimated number of bytes that can be read (skipped) next time and will not be blocked by the method public synchronized void mark (int readlimit) marks the current position of this input stream, and subsequent calls to the reset () method will be relocated to the location marked by mark () You can re-read the same byte public boolean markSupported () to determine whether the input stream supports the mark () and reset () methods, that is, whether the byte public synchronized void reset () can be read repeatedly to relocate the location of the stream to where it was when the mark () method was last called.

(1) ByteArrayInputStream

ByteArrayInputStream internally contains a buf byte array buffer that can read the number of bytes from the stream, uses the pos pointer to point to the subscript where the next byte is read, and maintains a count attribute internally, which represents the ability to read count bytes.

Bytearrayinputstream

"you must ensure that pos is strictly less than count and count is strictly less than buf.length before you can read data from the buffer"

(2) FileInputStream

File input stream, read bytes from a file, usually copy, move and other operations of the file, you can use the input stream to read the bytes of the file into memory, and then use the output stream to output to the specified location.

(3) PipedInputStream

The pipeline input stream, which appears in pairs with PipedOutputStream, can realize pipeline communication in multithreading. The PipedOutputStream specifies a connection to a specific PipedInputStream, and the PipedInputStream also needs to specify a specific PipedOutputStream connection, and then the output stream continues to write data to the buffer buffer of the input stream, while the input stream can read data from the buffer.

(4) ObjectInputStream

Object input stream, which is used for object deserialization, deserializes the read byte data into an object, and realizes the persistent storage of the object.

(5) PushBackInputStream

It is a subclass of FilterInputStream, a processing flow, and maintains a buffer array buf internally.

In the process of reading bytes, the read byte data can be rolled back to the buffer for storage, and the byte data can be read out from the buffer again next time. So PushBackInputStream allows you to read the bytes of the input stream multiple times, as long as you put the read bytes back into the buffer.

It is important to note that if the byte is pushed back, if the buffer is full, an IOException exception is thrown.

Its application scenario: classify and organize the data.

If two types of data, numbers and letters, are stored in a file, we need to leave them to two threads to collect the data they are responsible for. if we adopt the traditional method, all the data is read into memory, and then the data is separated. in the case of large files, such as 1G or 2G, the traditional input stream can only discard the data because there is no buffer after it is read into the array. This requires each thread to read the file once.

Using PushBackInputStream, you can let a specialized thread read files, awakening different threads to read characters:

Read the data from the buffer for the first time to determine which threads read the data

Roll back the data and wake up the corresponding thread to read the data

Repeat the first two steps

Close the input stream

At this point, do you think of AQS's Condition waiting queue, where multiple threads can wait to be awakened under different conditions?

(6) BufferedInputStream

Buffer flow, which is a processing flow that encapsulates and enhances the node flow, has an internal buffer buffer, which is used to cache all read bytes. When the buffer is full, all bytes will be sent to the client to read instead of sending only part of the data each time, which improves efficiency.

(7) DataInputStream

The data input stream, which is also a processing stream, encapsulates the node stream and can internally convert the read bytes into the corresponding Java basic data type.

(8) SequenceInputStream

Two or more input streams are read sequentially as one input stream. The existence of this class does not affect the whole IO ecology, and this effect can also be achieved in the program.

(9) StringBufferInputStream

Converts the lower 8 bits of each character in a string to bytes and reads them into a byte array, which is currently out of date

InputStream Summary:

InputStream is the abstract base class for all input byte streams

ByteArrayInputStream and FileInputStream are two basic node streams that read data from a byte array and a local file, respectively.

DataInputStream, BufferedInputStream, and PushBackInputStream are all processing flows that encapsulate and enhance the basic node flow

PipiedInputStream is used for multithreaded communication and can share a pipeline with other threads to read data in the pipeline.

ObjectInputStream is used for deserialization of objects. Byte data of objects is read into memory. Byte data can be converted into corresponding objects through this stream object.

OutputStream

OutputStream is the abstract base class of the byte output stream, which provides a general writing method for inherited subclasses to rewrite and reuse.

The method function public abstract void write (int b) writes the specified bytes to the output stream, which is the lower 8-bit public void write of parameter b (byte b []) writes all bytes in the specified byte array to the output stream public void write (byte b [], int off, int len) specifies the starting position of the write offer, and the byte array with len writes to the output stream public void flush () to refresh the output stream And force all buffered output bytes to be written to the specified location. Each time you finish writing, call public void close () to close the output stream and release all system resources associated with the stream.

Most of the classes in OutputStream correspond to InputStream, except that the data flows differently. As can be seen from the picture above:

OutputStream is the abstract base class for all output byte streams

ByteArrayOutputStream and FileOutputStream are two basic node streams that write data to byte arrays and local files, respectively.

DataOutputStream and BufferedOutputStream are processing streams, the former can convert byte data into basic data types to write out to the file; the latter is a buffered byte array, only when the buffer is full, all bytes will be written out to the destination, reducing the number of IO.

PipedOutputStream is used for multithreaded communication. It can share a pipe with other threads and write data to the pipe.

ObjectOutputStream is used for object serialization. After converting the object into a byte array, all bytes are written to the specified location.

PrintStream provides an enhanced function on the basis of OutputStream, that is, it can easily output formatted representations of various types of data (not limited to byte), and PrintStream's method never throws IOEception. Its principle is to uniformly convert the data of each data type to String type when writing out, which I will explain at the end of the explanation.

Character stream object

Character stream objects also have correspondence, and most classes can be thought of as operating data from byte arrays to characters, and the function of the class is similar to that of byte stream objects.

"the composition of the character input stream is very similar to that of the byte input stream. The character input stream is a layer of conversion to the byte input stream. All files are stored in bytes, and the characters are not retained on disk. Instead, the characters are encoded into bytes and then saved to the file. When reading a file, it also reads a byte sequence, while the Java virtual machine realizes byte-to-character mapping by converting the byte sequence into Unicode characters in units of 2 bytes. "

Reader

Reader is the abstract base class of the character input stream, and the important methods within it are as follows.

Important method function public int read (java.nio.CharBuffer target) saves the read character in the specified character buffer public int read () reads a character public int read (char cbuf []) reads the character into the entire character array abstract public int read (char cbuf [], int off, int len) reads the character into the specified range in the character array

There are other additional methods that are the same as those provided by the byte input stream base class, except that the object is no longer a byte, but a character.

Reader is the abstract base class for all character input streams

CharArrayReader and StringReader are two basic node streams. They read character array and string data respectively. Inside StringReader, there is a String variable value. By traversing the characters of the variable, the string is read. In essence, it is also reading the character array.

PipedReader is used for communication in multithreading to read character data from a common-use pipeline.

BufferedReader is a character input buffer stream, which puts the read data into the character buffer to read characters efficiently.

InputStreamReader is a conversion stream that converts byte data into character streams and converts byte data into characters.

Writer

Reader is the abstract base class of the character output stream, and the important methods within it are as follows.

The important method function public void write (char cbuf []) writes the cbuf character array to the output stream abstract public void write (char cbuf [], int off, int len) writes the specified range of cbuf character array to the output stream public void write (String str), writes the string str to the output stream, and inside str also writes the character array public void write (String str, int off, int len) to the output stream abstract public void flush () to refresh some part of the string str. If the data is saved in the buffer, calling this method will actually write out to the specified location abstract public void close () to close the stream object. After each IO execution, you need to close the stream object and release system resources.

Writer is the abstract base class for all output character streams

CharArrayWriter and StringWriter are two basic node streams that write data to Char arrays and strings, respectively. The StringBuffer object is saved inside StringWriter, which can realize the dynamic growth of string.

PipedWriter can write character data to a shared pipeline and read it to other threads.

BufferedWriter is a buffered output stream, which can cache the written data, and then call flush () to write out the data when the buffer is full, reducing the number of IO.

PrintWriter is similar to PrintStream, but also very similar in function and use, except that the data written out is characters rather than bytes.

OutputStreamWriter converts a character stream to a byte stream, writing out characters to a specified location

Conversion between byte stream and character stream

Data from anywhere to memory is first read in the form of a byte stream, even if the character stream is used to read the data, it is still true, because the data always exists in the form of bytes in the Internet and hardware devices. the character stream can convert bytes into characters only through the mapping of character sets.

So Java provides two kinds of transformation flows:

InputStreamReader: converts byte data into character data and reads them into memory from byte stream to character stream

OutputStreamWriter: converts character data into byte data and writes out to a specified location from character stream to byte stream

"

Once you understand the main members of character streams and byte streams in Java's traditional BIO, you should master at least two key points:

(1) the traditional BIO processes data with stream as the basic unit, which is imagined as water flow, transmitting byte data little by little, and the process of IO stream transmission is always in the form of bytes.

(2) the difference between byte stream and character stream is that the data unit of operation is different. Character stream maps byte data to corresponding characters through character set, and character stream is essentially a byte stream.

"

Then we will continue to learn the knowledge of NIO, NIO is a very hot way of working in IO, it can solve the pain point of traditional BIO: blocking.

If BIO encounters IO blocking, the thread will be suspended and the thread will not be awakened until the IO is complete, and thread switching brings additional overhead.

Each IO in BIO needs a corresponding thread to specifically process the IO request, which will quickly increase the pressure on the server.

What we want to do is to do something else while the thread is waiting for the IO to finish, and when the IO is complete, the thread can come back to work on the IO-related operations without having to wait for the IO to finish. During IO processing, there can be a special thread that listens to these IO operations and tells the server what to do. So, when we talk about IO, we have to touch the hard bone of NIO.

Trendy NIO

Let's take a look at the difference between BIO and NIO. BIO is a stream-oriented IO, and the channels it establishes are unidirectional, so the channels of input and output streams are different, so two channels must be established, and all the bytes of data in the channels are transmitted = = 0101001 =.

In NIO, it is no longer stream-oriented IO, but buffer-oriented, it will establish a channel (Channel), which we can understand as a railway, the railway can transport all kinds of goods, and there will be a buffer (Buffer) on the channel to store real data, the buffer we can understand as a train.

The channel (railway) is only used as a connection resource for transport data, while the real data storage is the buffer zone (train). That is, the channel is responsible for transmission and the buffer is responsible for storage.

After understanding the figure above, the main differences between BIO and NIO can be briefly summarized in the following table.

BIONIO flow oriented (Stream) buffer oriented (Buffer) unidirectional channel bi-directional channel blocking IO nonblocking IO selector (Selectors)

Buffer (Buffer)

A buffer is an area where data is stored. In Java, a buffer is an array. In order to manipulate data of different data types, Java provides many different types of buffers. Except for Boolean types, other basic data types have corresponding buffer array objects.

"Why is there no Boolean buffer?

In Java, boolean type data only takes up 1 bit, while in the process of IO transmission, the transmission is carried out in bytes, so the 1 bit of boolean can be represented by a bit of byte type or a bit of int type. There is no need to provide an extra buffer for this 1 bit. "

Buffer interpretation ByteBuffer storage byte data buffer CharBuffer storage character data buffer ShortBuffer storage short integer data buffer IntBuffer storage integer data buffer LongBuffer storage long integer data buffer FloatBuffer storage single precision floating point data buffer DoubleBuffer storage double precision floating point data buffer

A buffer is allocated in a highly consistent way: using the allocate (int capacity) method.

For example, you need to allocate a 1024-size byte array, and the code looks like this.

ByteBuffer byteBuffer = ByteBuffer.allocate (1024)

There are two core ways for buffers to read and write data:

Put (): writes data to a buffer

Get (): read data from the buffer

Important properties of the buffer:

Capacity: the maximum capacity of data stored in the buffer, which cannot be changed once declared

Limit: indicates the size of the data that can be manipulated in the buffer, and the data after limit cannot be read or written. Must satisfy the limit kernel mode operating system kernel to return data to the user process kernel state-> user mode user process request to write data to hard disk user state-> kernel mode operating system returns operation results to user process kernel state-> user state

"the operation between the user space and the kernel space will involve context switching, where the intervention of CPU is required, and the data is copied back and forth between the two spaces, which also requires the intervention of CPU, which will undoubtedly increase the pressure on CPU. How does NIO reduce the pressure of CPU? use the zero copy technology of the operating system."

Zero copy of the operating system

Therefore, a new concept has emerged in the operating system, which solves the bottleneck of IO: zero copy. Zero copy refers to the zero copy between kernel space and user space.

Zero copy can be said to be a great savior of IO. There are many zero copy mechanisms at the bottom of the operating system. I will only explain one of the zero copy mechanisms used in Java NIO.

In Java NIO, zero copy is achieved by sharing a piece of physical memory between user space and kernel space buffers, which means that the above diagram can evolve like this.

At this time, whether the user space or the kernel space operates its own buffer, it essentially operates the buffer data in this piece of shared memory, eliminating the data copy operation between the user space and the kernel space.

Now that we copy the file again, it becomes the following step:

The user process requests to read the file to the user space buffer through the system call read () (the first context switch), the user mode-> kernel mentality, the data is read from the hard disk to the kernel space buffer (the first data copy)

The system call returns to the user process (the second context switch), where the user space shares this memory (buffer) with the kernel space, so there is no need to copy from the kernel buffer to the user buffer

When the user process issues a write () system call request to write data to the hard disk (the third context switch), the data in the kernel space buffer needs to be copied to the kernel Socket buffer (the second data copy).

DMA writes the contents of the Socket buffer to the hard disk (the third data copy), and the write () system call returns (the fourth context switch).

The whole process is shown in the picture below.

In the figure, only the ③ step is required for CPU to participate in the work. Compared with the traditional IO,CPU, which needs to participate in the copy work between user space and kernel space, it needs to meaninglessly occupy CPU resources twice, resulting in a waste of CPU resources.

Here is a summary of the advantages of zero copy in the operating system:

Reduce the pressure on CPU: avoiding CPU requires participation in data copying between kernel space and user space

Reduce unnecessary copies: avoid the need to copy data between user space and kernel space

The above illustration may not be rigorous, it will be helpful for you to understand zero copy. You can consult more information about zero copy. This is a great knowledge.

"after introducing the channel, we know that it is a medium for transmitting data, and it can be read and written in both directions, so if you put it in the network IO, how does the server discover and process these channels when the data is ready? next, let's learn the last important point in NIO: Selector."

Selector (Selectors)

Selector is one of the souls to improve the performance of IO. It makes use of the multiplexing IO mechanism at the bottom, so that the selector can listen to multiple IO connections and process it to the server according to the state response of IO. In popular terms: selectors can listen for multiple IO connections, while traditional BIO requires a thread for each IO connection to listen and process.

The figure clearly shows that in BIO, each Socket needs a special thread to process each request, while in NIO, only one Selector is needed to listen to each Socket request, and the Selector is not blocked, so there is no overhead of context switching due to switching between multiple threads.

In Java NIO, the selector uses the Selector class to indicate that the Selector can receive various IO connections, and when the IO state is ready, the registered Selector,Selector of the channel will find that the IO connection is ready on the next poll, and then process the connection.

Selector selector is mainly used in network IO. Here I will compare traditional BIO Socket programming with Socket programming after using NIO to analyze why NIO is more popular. Let's start with a look at the basic structure of Selector.

Important method parsing open () opens a Selector selector int select () blocking waiting for ready channels int select (long timeout) blocks timeout milliseconds at most, if it is 0, it keeps blocking waiting, if it is 1, it represents blocking 1 milliseconds int selectNow () non-blocking polling ready channels

Here, you will see that select () and its overloaded methods are blocked. If the user process polls and finds that there is no ready channel, the operating system has two ways:

Wait until a channel is ready, and then return to the user process

Immediately return an error status code to the user process to keep the user process running without blocking

These two methods correspond to synchronous blocking IO and synchronous non-blocking IO. Here is a small point of view of readers, please read critically.

"NIO in Java cannot really be called Non-Blocking IO. Through the call to API, we can find that the select () method will still be blocked. The behavior of the operating system will be different according to the passed parameters, the difference is blocking or non-blocking, so I prefer to call NIO New IO, because it not only provides Non-Blocking IO, but also retains the original Blocking IO functions."

Once you understand the selector, its purpose is to listen to multiple IO channels, and when a channel is ready, the selector will poll to find the channel and process it accordingly. So there are many kinds of IO states, how can we tell which state the ready channel is in? The selection key (SelectionKey) is provided in Java.

Select key (SelectionKey)

Four selection keys are available in Java:

SelectionKey.OP_READ: socket channel ready for read operation

SelectionKey.OP_WRITE: the socket channel is ready for writing

SelectionKey.OP_ACCEPT: server socket channels accept other channels

SelectionKey.OP_CONNECT: socket channel ready to complete the connection

Many attributes are included in SelectionKey

Channel: the channel bound by the selection key

Selector: the selector that polled the selection key

ReadyOps: the value of the current ready selection key

InteresOps: all the selection keys that the selector is interested in the channel

The function of the selection key is that when the selector polls for ready channels, it returns the ready selection keys (SelectionKey) of those channels, through which the channels can be obtained for operation.

After a brief understanding of the selector, we can combine buffers, channels and selectors to complete a simple chat room application.

Example: simple client-server communication

"first of all, the code here is very smelly and long, it is not recommended to take a closer look, just look at the code near the comments."

We will open two threads on the server side.

Thread1: specifically listens for client connections and registers channels with client selectors

Thread2: specifically listens for other IO states (read state) of the client. When the IO state of the client is ready, the selector polls for discovery and handles it accordingly.

Public class NIOServer {Selector serverSelector = Selector.open (); Selector clientSelector = Selector.open (); public static void main (String [] args) throws IOException {NIOServer server = nwe NIOServer (); new Thread ()-> {try {/ / the server starts ServerSocketChannel listenerChannel = ServerSocketChannel.open () in IO programming ListenerChannel.socket () .bind (new InetSocketAddress (3333)); listenerChannel.configureBlocking (false); listenerChannel.register (serverSelector, SelectionKey.OP_ACCEPT); server.acceptListener ();} catch (IOException ignored) {}}) .start () New Thread (()-> {try {server.clientListener ();} catch (IOException ignored) {}}) .start () }} / / listening for client connection public void acceptListener () {while (true) {if (serverSelector.select (1) > 0) {Set set = serverSelector.selectedKeys (); Iterator keyIterator = set.iterator (); while (keyIterator.hasNext ()) {SelectionKey key = keyIterator.next () If (key.isAcceptable ()) {try {/ / (1) for each new connection, register with clientSelector SocketChannel clientChannel = ((ServerSocketChannel) key.channel ()) .accept (); clientChannel.configureBlocking (false) ClientChannel.register (clientSelector, SelectionKey.OP_READ);} finally {/ / remove this key keyIterator.remove () from the ready list }} / / IO state ready public void clientListener () {while (true) {/ / batch polling for connections with readable if (clientSelector.select (1) > 0) {Set set = clientSelector.selectedKeys () Iterator keyIterator = set.iterator (); while (keyIterator.hasNext ()) {SelectionKey key = keyIterator.next () / / determine whether the channel is read ready state if (key.isReadable ()) {try {/ / get client channel read data SocketChannel clientChannel = (SocketChannel) key.channel (); ByteBuffer byteBuffer = ByteBuffer.allocate (1024) ClientChannel.read (byteBuffer); byteBuffer.flip (); System.out.println (LocalDateTime.now (). ToString () + "the Server side received a message from the Client side:" + Charset.defaultCharset () .decode (byteBuffer) .toString ()) } finally {/ / remove this key keyIterator.remove (); key.interestOps (SelectionKey.OP_READ);}} from the ready list

On the client side, we can simply enter some text and send it to the server.

Public class NIOClient {public static final int CAPACITY = 1024; public static void main (String [] args) throws Exception {ByteBuffer dsts = ByteBuffer.allocate (CAPACITY); SocketChannel socketChannel = SocketChannel.open (new InetSocketAddress ("127.0.0.1", 3333)); socketChannel.configureBlocking (false); Scanner sc = new Scanner (System.in); while (true) {String msg = sc.next () Dsts.put (msg.getBytes ()); dsts.flip (); socketChannel.write (dsts); dsts.clear ();}

As can be seen in the following figure, the client sends a message to the server, and after the server receives the message, it can distribute the message to other clients to implement a simple group chat system. We can also label these clients, such as user name and chat level, to identify each client. I didn't write all the features here because of the space, because it's not very convenient to use the native NIO.

I'm sure you all slipped down to see this, and I was very painful when I wrote this code, and I even got a little tired of Java's native NIO programming. In fact, we seldom use NIO to program directly in our daily development, and we usually use Netty,Mina as a server framework. They are all good NIO technologies that encapsulate and optimize the native NIO of Java in the upper layer to simplify the difficulty of development. But before we learn the framework, we need to understand its underlying native technology, such as the dynamic proxy of Spring AOP, the Map container storage objects of the Spring IOC container, and the basic NIO of the underlying Netty.

This is the end of the content of "what is IO flow"? thank you for reading. If you want to know more about the industry, you can follow the website, the editor will output more high-quality practical articles for you!

Welcome to subscribe "Shulou Technology Information " to get latest news, interesting things and hot topics in the IT industry, and controls the hottest and latest Internet news, technology news and IT industry trends.

Views: 0

*The comments in the above article only represent the author's personal views and do not represent the views and positions of this website. If you have more insights, please feel free to contribute and share.

Share To

Development

Wechat

© 2024 shulou.com SLNews company. All rights reserved.

12
Report