Java NIO detailed _java

Source: Internet
Author: User
Tags compact readable readline rewind switches advantage

Java NiO provides a different way of doing IO than standard IO:

Channels and buffers (channels and buffers): Standard IO operates on byte streams and character streams, whereas NiO operates on channels (Channel) and buffers (buffer), which are always read from the channel to the buffer or written from the buffer to the channel.
Asynchronous IO (Asynchronous IO): Java NIO allows you to use IO asynchronously, for example, when a thread reads data from a channel to a buffer, the thread can do something else. When data is written to the buffer, the thread can continue to process it. Writing a channel from a buffer is similar.
Selectors (selector): Java NiO introduces the concept of selectors, which are used to monitor multiple channels of events (for example: Connection open, data arrival). As a result, a single thread can listen for multiple data channels.
The following is a detailed description of Java NIO related knowledge.

Java NIO Overview

Java NIO consists of the following core components:





Channels


Buffers


Selectors


Although there are many other classes and components in Java NIO, it seems to me that Channel,buffer and Selector constitute the core API. Other components, such as pipe and filelock, are just tool classes that are used in conjunction with three core components. Therefore, I will focus on these three components in the overview. Other components are described in a separate section.





Channel and Buffer





Basically, all IO starts with a channel in NiO. Channel a bit like a stream. Data can be read from channel to buffer or written to channel from buffer. Here's a diagram:

















There are several types of channel and buffer. The following are the implementations of some of the major channel in Java NIO:





FileChannel


Datagramchannel


Socketchannel


Serversocketchannel


As you can see, these channels cover both UDP and TCP network IO, as well as file IO.





There are some interesting interfaces with these classes, but for the sake of simplicity, I try not to mention them in the overview. I will explain the other chapters of this tutorial that relate to them.





The following are key buffer implementations in Java NIO:





Bytebuffer


Charbuffer


DoubleBuffer


Floatbuffer


Intbuffer


Longbuffer


Shortbuffer


These buffer covers the basic data types you can send via IO: Byte, short, int, long, float, double, and Char.





Java NIO also has a mappedyteuffer that represents the memory-mapped file, and I do not intend to explain it in the overview.





Selector





Selector allows multiple Channel to be processed in a single thread. If your application opens multiple connections (channels), but the traffic is low for each connection, it is convenient to use selector. For example, in a chat server.





This is a single thread that uses a selector to handle 3 channel diagrams:

















To use selector, you have to register the channel with selector, and then call its select () method. This method blocks to a registered channel until the event is ready. Once this method returns, the thread can handle these events, with examples of events like new connections coming in, data receiving, and so on.








Java NIO vs. IO











(This part of the original address, the author: Jakob Jenkov, Translator: Guo, proofreading: Side Take-off)


After learning the Java NIO and IO APIs, a problem flooded into my mind:





Reference


When should I use IO and when to use NIO? In this article, I'll try to clearly parse the differences between Java NiO and Io, their usage scenarios, and how they affect your code design.








The main differences between Java NiO and IO





The following table summarizes the main differences between Java NiO and Io, and I describe the differences in each part of the table in more detail.





IO NIO


Stream oriented Buffer oriented


Blocking io Non Blocking io


Selectors








Flow-oriented and buffer-oriented





The first major difference between Java NiO and Io is that IO is stream oriented and NIO is buffer-oriented. Java io-oriented streaming means that one or more bytes are read from the stream each time, until all bytes are read, and they are not slowed down anywhere. In addition, it cannot move data back and forth in the stream. If you need to move data that is read from the stream before and after, you need to first cache it to a buffer. Java NiO has a slightly different buffer-oriented approach. The data is read to a buffer it handles later, and can be moved back and forth in the buffer when needed. This increases the flexibility in the processing process. However, you also need to check whether the buffer contains all the data you need to work with. Also, make sure that when more data is read into the buffer, do not overwrite data that has not been processed in the buffer.





Blocking and non-blocking IO





The various streams of Java 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 fully written. The thread can no longer do anything in the meantime. Non-blocking mode of Java NiO, which allows a thread to send requests to read data from a channel, but it can only get the data that is currently available and will not get anything if no data is available at the moment. Instead of keeping the thread blocked, the thread can continue to do something else until the data becomes readable. The same is true for non-blocking writing. A thread requests to write some data to a channel, but does not need to wait for it to be fully written, and the thread can do something else at the same time. Threads typically use non-blocking IO idle time to perform IO operations on other channels, so a single thread can now manage multiple input and output channels (channel).





Selector (selectors)





The Java NiO selector allows a separate thread to monitor multiple input channels, you can register multiple channels using a selector, and then use a separate thread to "select" the channel: these channels already have the input that can be processed, or select the channel that is ready to be written. This selection mechanism makes it easy for a single thread to manage multiple channels.





How NiO and Io affect the design of an application





Whether you choose IO or the NIO toolbox, you may affect the following aspects of your application design:





API calls to NiO or IO classes.


Data processing.


The number of threads used to process the data.


API call





Of course, the API call using NIO may look different from using IO, but that's not surprising, since it's not just read from one inputstream to bytes, but the data must be read into the buffer before it is processed.





Data processing





Data processing is also affected by the use of pure NIO design compared to IO design.





In IO design, we read the data byte by bit from InputStream or reader. Let's say you're working on a line based text stream, such as:





Copy Code code as follows:

Name:anna
Age:25
Email:anna@mailserver.com
phone:1234567890






The flow of this line can be handled in this way:





Copy Code code as follows:

InputStream input = ...; Get the InputStream from the client socket
BufferedReader reader = new BufferedReader (new InputStreamReader (input));

String nameline = Reader.readline ();
String ageline = Reader.readline ();
String emailline= reader.readline ();
String phoneline= reader.readline ();






Please note how long the processing status is determined by the program execution. In other words, once the Reader.readline () method returns, you will know that the line of text has been read, ReadLine () blocked until the whole line is read, that's why. You also know that this row contains a name; Similarly, the second readline () call returns when you know that the line contains age and so on. As you can see, the handler runs only when new data is read and knows what the data for each step is. Once a running thread has processed some of the data that is being read, the thread does not rollback the data (mostly). The following figure also illustrates this principle:








Reading data from a blocked stream





The implementation of a NIO is different, and here's a simple example:





Copy Code code as follows:

Bytebuffer buffer = bytebuffer.allocate (48);

int bytesread = inchannel.read (buffer);






Note that the second line reads bytes from the channel to Bytebuffer. When this method call returns, you don't know if all the data you need is in the buffer. What you know is that the buffer contains some bytes, which makes handling a bit difficult.


Assuming that after the first read (buffer) call, the data read into the buffer is only half a row, for example, "Name:an", can you handle the data? Obviously not, you need to wait until the entire row of data is read into the cache, before any processing of the data is meaningless.





So, how do you know if the buffer contains enough data to handle it? Well, you don't know. The discovered method can only view the data in the buffer. The result is that you have to check the buffer data several times before you know that all the data is in the buffer. This is not only inefficient, but also can make the program design scheme messy. For example:





Copy Code code as follows:

Bytebuffer buffer = bytebuffer.allocate (48);
int bytesread = inchannel.read (buffer);
while (! bufferfull (bytesread)) {
Bytesread = inchannel.read (buffer);
}






The Bufferfull () method must track how much data is read into the buffer and return TRUE or false, depending on whether the buffer is full. In other words, if the buffer is ready to be processed, it means that the buffer is full.





The Bufferfull () method scans the buffer, but must remain the same state before the Bufferfull () method is invoked. If not, the next data read into the buffer may not be able to read to the correct location. This is not possible, but it is another issue that needs to be noted.





If the buffer is full, it can be processed. If it is dissatisfied and meaningful in your actual case, you may be able to process some of the data. But in many cases this is not the case. The following figure shows the "Buffer data Loop Ready":








Read the data from one channel until all the data is read into the buffer.





Summarize





NIO allows you to manage multiple channels (network connections or files) using only one single thread, but at the cost of parsing the data can be more complex than reading from a blocking stream.





If you need to manage thousands of connections that are open at the same time, and these connections only send a small amount of data, such as a chat server, the server that implements NIO may be an advantage. Similarly, if you need to maintain many open connections to other computers, such as Peer-to-peer networks, using a separate thread to manage all your outbound connections may be an advantage. The design of multiple connections for one thread is shown in the following illustration:








Single-threaded management of multiple connections





If you have a small number of connections that use very high bandwidth and send a large amount of data at one time, perhaps a typical IO server implementation might be very much in agreement. The following figure illustrates a typical IO server design:





A typical IO server design:











A connection is handled by a thread





Channel (Channel)





The channels of Java NiO are similar in flow, but somewhat different:





Both the data can be read from the channel and the data can be written to the channel. However, the flow of read and write is usually one-way.


Channels can read and write asynchronously.


The data in the channel is always read first to a buffer, or always to be written from a buffer.


As mentioned above, reads data from the channel to the buffer, writes data from the buffer to the channel. As shown in the following illustration:

















The realization of Channel





These are the implementations of the most important channels in Java NIO:





FileChannel: Reads and writes data from the file.


Datagramchannel: Can read and write data in the network via UDP.


Socketchannel: Can read and write data in the network through TCP.


Serversocketchannel: A new incoming TCP connection can be monitored, like a Web server. A socketchannel will be created for each new incoming connection.


The basic Channel sample





The following is an example of using FileChannel to read data to buffer:





Copy Code code as follows:

Randomaccessfile afile = new Randomaccessfile ("Data/nio-data.txt", "RW");
FileChannel Inchannel = Afile.getchannel ();

Bytebuffer buf = Bytebuffer.allocate (48);

int bytesread = Inchannel.read (BUF);
while (Bytesread!=-1) {

System.out.println ("Read" + bytesread);
Buf.flip ();

while (Buf.hasremaining ()) {
System.out.print ((char) buf.get ());
}

Buf.clear ();
Bytesread = Inchannel.read (BUF);
}
Afile.close ();






Notice the call to Buf.flip (), first read the data to the buffer, then reverse the buffer, and then read the data from the buffer. The next section will delve into more details about buffer.





Buffers (buffer)





The buffer in Java NiO is used to interact with the NIO channel. As you know, the data is read from the channel into the buffer and written to the channel from the buffer.





A buffer is essentially a piece of memory that can be written to data and then read from. This memory is packaged as a NiO buffer object and provides a set of methods to facilitate access to the block memory.





Basic usage of buffer





The following four steps are generally used to read and write data using buffer:





Write data to Buffer


Call the Flip () method


reading data from buffer


Call the Clear () method or the compact () method





When the data is written to the buffer, the buffer records how much data is written. Once you want to read the data, you need to switch the buffer from write mode to read mode through the flip () method. In read mode, you can read all the data that was written to the buffer before.





Once you have finished reading all the data, you need to empty the buffer so that it can be written again. There are two ways to empty a buffer: Call the Clear () or compact () method. The clear () method empties the entire buffer. The compact () method clears only the data that has been read. Any unread data is moved to the beginning of the buffer, and the newly written data is placed behind the data that is not read by the buffer.





Here is an example of using buffer:





Copy Code code as follows:

Randomaccessfile afile = new Randomaccessfile ("Data/nio-data.txt", "RW");
FileChannel Inchannel = Afile.getchannel ();

Create buffer with capacity of bytes
Bytebuffer buf = Bytebuffer.allocate (48);

int bytesread = Inchannel.read (BUF); Read into buffer.
while (Bytesread!=-1) {

Buf.flip ();//make buffer ready for Read

while (Buf.hasremaining ()) {
System.out.print ((char) buf.get ()); Read 1 byte at a time
}

Buf.clear (); Make buffer ready for writing
Bytesread = Inchannel.read (BUF);
}
Afile.close ();






The capacity,position and limit of the buffer





A buffer is essentially a piece of memory that can be written to data and then read from. This memory is packaged as a NiO buffer object and provides a set of methods to facilitate access to the block memory.





To understand how the buffer works, you need to familiarize yourself with its three properties:





Capacity


Position


Limit





The meaning of position and limit depends on whether the buffer is in read mode or write mode. No matter what mode the buffer is in, the meaning of capacity is always the same.





Here is a description of the capacity,position and limit in the read and write mode, explained in detail behind the illustrations.





Capacity





As a block of memory, the buffer has a fixed size value, also known as "capacity". You can only write capacity byte, Long,char and other types. Once the buffer is full, it needs to be emptied (by reading the data or clearing the data) in order to continue writing the data.





Position





When you write data into the buffer, position represents the current position. The initial position value is 0. When a byte, long, and other data is written to the buffer, the position moves forward to the next buffer unit that can be inserted into the data. Position can be the largest capacity–1.





When reading data, it is also read from a specific location. When the buffer is switched from write mode to read mode, the position is reset to 0. When the data is read from the position of the buffer, the position moves forward to the next readable position.





Limit





In write mode, the limit of the buffer indicates how much data you can write into the buffer. In write mode, the limit equals the capacity of the buffer.





When switching the buffer to read mode, limit indicates how much data you can read at most. Therefore, when you switch the buffer to read mode, limit is set to the position value in write mode. In other words, you can read all the data written before (limit is set to the number of written data, which is position in write mode)





Type of buffer





Java NIO has the following buffer types:





Bytebuffer


Mappedbytebuffer


Charbuffer


DoubleBuffer


Floatbuffer


Intbuffer


Longbuffer


Shortbuffer





As you can see, these buffer types represent different data types. In other words, you can manipulate the bytes in the buffer by using the char,short,int,long,float or double type.





The Mappedbytebuffer is somewhat special and is further addressed in the special chapters that deal with it.





Allocation of buffer





To get a buffer object, you first have to assign it. Each buffer class has a allocate method. Here is an example of a bytebuffer that allocates 48 bytes of capacity.





Copy Code code as follows:

Bytebuffer buf = Bytebuffer.allocate (48);






This is a charbuffer that can store 1024 characters:





Copy Code code as follows:

Charbuffer buf = charbuffer.allocate (1024);






Write data to Buffer





There are two ways of writing data to buffer:





Write from channel to buffer.


Write to the buffer by using the put () method of the buffer.





From channel to the example of buffer





Copy Code code as follows:

int bytesread = Inchannel.read (BUF); Read into buffer.






Example of using put method to write buffer:





Copy Code code as follows:

Buf.put (127);






There are many versions of the Put method that allow you to write data into the buffer in different ways. For example, write to a specified location, or write a byte array to buffer. More details of the implementation of the buffer reference Javadoc.





Flip () method





The flip method switches the buffer from write mode to read mode. Calling the flip () method sets the position back to 0, setting the limit to the previous position value.





In other words, position is now used to mark the location of the read, limit indicates how many bytes, char, etc. were written before--how many byte, char, etc. can now be read.





reading data from buffer





There are two ways to read data from buffer:





Read data from buffer to channel.


Use the Get () method to read data from the buffer.





Examples of reading data from buffer to channel:





Copy Code code as follows:

Read from buffer into channel.
int byteswritten = Inchannel.write (BUF);






Example of reading data from buffer using the Get () method





Copy Code code as follows:

byte Abyte = Buf.get ();






The Get method has many versions that allow you to read data from the buffer in different ways. For example, read from a specified position, or read data from buffer to an array of bytes. More details of the implementation of the buffer reference Javadoc.





Rewind () method





Buffer.rewind () sets the position back to 0, so you can reread all the data in the buffer. The limit remains unchanged, still indicating how many elements (byte, char, etc.) can be read from the buffer.





Clear () and compact () method





Once you have read the data in the buffer, you need to have the buffer ready to be written again. Can be done through the clear () or compact () method.





If the clear () method is invoked, position will be set back to the value 0,limit is set to capacity. In other words, the Buffer is emptied. The data in the buffer is not erased, but the tags tell us where to start writing data into the buffer.





If there are some unread data in the buffer, call the Clear () method, and the data will be "forgotten", meaning that no more tags will tell you what data has been read or not.





If you still have unread data in the buffer, and you need the data later, but you want to write some data first, then use the compact () method.





The Compact () method copies all unread data to the beginning of the buffer. The position is then set to the end of the last unread element. The Limit property is still set to capacity, like the clear () method. Now that the buffer is ready to write the data, it will not overwrite the unread data.





Mark () and Reset () method





By calling the Buffer.mark () method, you can mark a specific position in the buffer. You can then revert to this position by calling the Buffer.reset () method. For example:





Copy Code code as follows:

Buffer.mark ();

Call Buffer.get () a couple the times, e.g. during parsing.

Buffer.reset ();//set position back to mark.






Equals () and CompareTo () method





You can use the Equals () and the CompareTo () method to two buffer.





Equals ()





Represents two buffer equality when the following conditions are true:





Have the same type (byte, char, int, and so on).


The number of byte, char, etc. remaining in the buffer is equal.


All the remaining byte, char, and so on in the buffer are the same.





As you can see, Equals is only a part of the buffer, not every element in it is compared. In fact, it only compares the remaining elements in the buffer.





CompareTo () method





The CompareTo () method compares the remaining elements of two buffer (byte, char, etc.) and considers a buffer "less than" another buffer if the following conditions are true:





The first element that is not equal is less than the corresponding element in the other buffer.


All elements are equal, but the first buffer is depleted first (the number of elements in the first buffer is less than the other).





(The remaining elements are elements from position to limit)





Disperse (scatter)/aggregation (Gather)





(This part of the original address, the author: Jakob Jenkov translator: Guo)





Java NiO begins to support Scatter/gather,scatter/gather used to describe the operation of reading or writing to channel from channel: Channel in Chinese often translated as channels.





Scatter (scatter) reads from channel refers to writing read data to multiple buffer during a read operation. Therefore, channel will "scatter (scatter)" The data read from channel to multiple buffer.





Aggregation (gather) write Channel refers to writing multiple buffer data to the same Channel during a write operation, so Channel sends the data in multiple buffer (gather) to Channel.





Scatter/gather is often used in situations where the data needed to be processed separately, such as a message that consists of a message header and a message body, you may be able to spread the message body and message headers into different buffer so that you can easily handle the message headers and the message body.





Scattering reads





Scattering reads means that data is read from one channel to multiple buffer. The following figure describes:





The code example is as follows:





Copy Code code as follows:

Bytebuffer Header = bytebuffer.allocate (128);
Bytebuffer BODY = bytebuffer.allocate (1024);

Bytebuffer[] Bufferarray = {header, body};

Channel.read (Bufferarray);






Note that buffer is first inserted into the array, and then the array is used as an input parameter to Channel.read (). The read () method writes the data read from the channel to the buffer in the order in which the buffer is in the array, and when a buffer is filled, the channel is immediately written to another buffer.





Scattering reads must fill the current buffer before moving the next buffer, which means it does not apply to dynamic messages: The message size is not fixed. In other words, if there is a message header and a message body, the header must complete the fill (for example, 128byte), scattering reads to work properly.





Gathering writes





Gathering writes means that data is written from multiple buffer to the same channel. The following figure describes:





The code example is as follows:





Copy Code code as follows:

Bytebuffer Header = bytebuffer.allocate (128);
Bytebuffer BODY = bytebuffer.allocate (1024);

Write data into buffers

Bytebuffer[] Bufferarray = {header, body};

Channel.write (Bufferarray);






The buffers array is the entry of the Write () method, and the write () method writes the data to the channel according to the order of the buffer in the array, noting that only the data between position and limit is written. Therefore, if the capacity of a buffer is 128byte, but only contains 58byte of data, then this 58byte of data will be written into the channel. So, contrary to scattering reads, gathering writes can handle dynamic messages better.





Data transfer between channels





(This part of the original address, the author: Jakob Jenkov, Translator: Guo, proofreading: Jacky Chow)


In Java NiO, if one of the two channels is FileChannel, you can transfer the data directly from a channel (translator note: Channel Chinese translation channel) to another channel.





Transferfrom ()





The FileChannel Transferfrom () method transmits data from the source channel to the FileChannel (translator Note: This method is interpreted in the JDK document as transferring bytes from a given readable byte channel to a file in this channel). The following is a simple example:





Copy Code code as follows:

Randomaccessfile fromfile = new Randomaccessfile ("FromFile.txt", "RW");
FileChannel Fromchannel = Fromfile.getchannel ();

Randomaccessfile tofile = new Randomaccessfile ("ToFile.txt", "RW");
FileChannel Tochannel = Tofile.getchannel ();

Long position = 0;
Long Count = Fromchannel.size ();

Tochannel.transferfrom (position, count, Fromchannel);






The input parameter position of the method indicates that data is written to the target file from the position, and count represents the maximum number of bytes transmitted. If the remaining space of the source channel is less than count bytes, the number of bytes transferred is less than the requested number of bytes.





Also note that in Soketchannel implementations, Socketchannel only transmits data that is ready at the moment (possibly less than count bytes). Therefore, Socketchannel may not transfer all of the requested data (count bytes) to FileChannel.





TransferTo ()





The TransferTo () method transmits data from FileChannel to other channel. The following is a simple example:





Copy Code code as follows:

Randomaccessfile fromfile = new Randomaccessfile ("FromFile.txt", "RW");
FileChannel Fromchannel = Fromfile.getchannel ();

Randomaccessfile tofile = new Randomaccessfile ("ToFile.txt", "RW");
FileChannel Tochannel = Tofile.getchannel ();

Long position = 0;
Long Count = Fromchannel.size ();

Fromchannel.transferto (position, count, Tochannel);






Is it found that this example is particularly similar to the previous example? Except for the FileChannel object that invokes the method, the other is the same.





The problems mentioned above about Socketchannel are also present in the Transferto () method. Socketchannel will continue to transmit data until the target buffer is filled.





Selector (Selector)





(This part of the original link, author: Jakob Jenkov, Translator: Roaming V, proofreading: ding)


The Selector (selector) is a component in Java NIO that detects one or more NIO channels and knows if the channel is ready for such things as read and write events. In this way, a single thread can manage multiple channel to manage multiple network connections.





(1) Why use selector?





The advantage of handling multiple channels with only a single thread is that fewer threads are needed to handle the channel. In fact, you can handle all the channels with just one thread. For the operating system, context switches between threads are expensive, and each thread consumes some of the system's resources (such as memory). As a result, the fewer threads you use, the better.





However, it should be remembered that modern operating systems and CPUs are performing more and more well in multitasking, so the overhead of multithreading is getting smaller over time. In fact, if a CPU has multiple cores, not multitasking may be a waste of CPU power. Anyway, the discussion of that design should be in a different article. Here, it's enough to know that using selector can handle multiple channels.





The following is a single-threaded use of a selector to handle 3 Channel sample graphs:





(2) Creation of Selector





Create a selector by calling the Selector.open () method, as follows:





Copy Code code as follows:

Selector Selector = Selector.open ();






(3) Register the channel to the selector





In order to use channel and selector together, the channel must be registered to selector. Implemented by the Selectablechannel.register () method, as follows:





Copy Code code as follows:

Channel.configureblocking (FALSE);
Selectionkey key = Channel.register (selector,
Selectionkey.op_read);






When used with selector, channel must be in non-blocking mode. This means that FileChannel cannot be used with selector because FileChannel cannot switch to non-blocking mode. and socket channels are OK.





Note the second parameter of the register () method. This is a "interest collection," which is meant to be interested in what happened when listening to channel through the selector. You can listen for four different types of events:





Connect


Accept


Read


Write


The channel triggers an event that means the event is ready. Therefore, a channel successful connection to another server is called "Connect ready." A server socket channel ready to receive new incoming connections is called "Receive ready." A data-readable channel can be said to be "read-ready." The channel waiting to write data can be said to be "write-ready."





These four events are represented by the four constants of Selectionkey:





Selectionkey.op_connect


Selectionkey.op_accept


Selectionkey.op_read


Selectionkey.op_write


If you are interested in more than one event, you can use the "bit or" operator to connect the constants as follows:





Copy Code code as follows:

int interestset = Selectionkey.op_read | Selectionkey.op_write;






The interest collection will continue to be mentioned below.





(4) Selectionkey





In the previous section, when registering channel with selector, the Register () method returns a Selectionkey object. This object contains some of the attributes you are interested in:





Interest Collection


Ready Collection


Channel


Selector


Additional objects (optional)


I'll describe these properties below.





Interest Collection





As described in the section on registering channels to selector, the interest collection is the collection of events of interest that you choose. You can read and write interest collections through Selectionkey, like this:





Copy Code code as follows:

int interestset = selectionkey.interess ();

Boolean isinterestedinaccept= (Interestset & selectionkey.op_accept) = = Selectionkey.op_accept;
Boolean isinterestedinconnect = Interestset & selectionkey.op_connect;
Boolean isinterestedinread = Interestset & selectionkey.op_read;
Boolean isinterestedinwrite = Interestset & selectionkey.op_write;






As you can see, you can determine whether a certain event is in the interest collection with the "bit and" action interest the collection and the given Selectionkey constant.





Ready Collection





The Ready collection is a collection of actions that are already ready for the channel. After one selection (Selection), you will first access the ready set. Selection will explain in the next section. You can access the Ready collection like this:





int readyset = Selectionkey.readyops ();





You can use methods like the interest collection to detect what events or operations are ready in the channel. However, you can also use the following four methods, all of which return a Boolean type:





Copy Code code as follows:

Selectionkey.isacceptable ();
Selectionkey.isconnectable ();
Selectionkey.isreadable ();
Selectionkey.iswritable ();






Channel + Selector





Access to channel and selector from Selectionkey is simple. As follows:





Copy Code code as follows:

Channelchannel= Selectionkey.channel ();
Selector Selector = Selectionkey.selector ();






Attached objects





You can attach an object or more information to the Selectionkey so that you can easily identify a given channel. For example, you can attach a buffer that is used with a channel, or an object that contains aggregated data. Use the following methods:





Copy Code code as follows:

Selectionkey.attach (theobject);
Object attachedobj = Selectionkey.attachment ();






You can also attach an object when registering channel with the Register () method with selector. Such as:





Copy Code code as follows:

Selectionkey key = Channel.register (selector, selectionkey.op_read, theobject);






(5) Select channel through selector





Once you have registered one or more channels to selector, you can call several overloaded select () methods. These methods return the channels that you are interested in, such as connecting, accepting, reading, or writing, that are already ready. In other words, if you are interested in a "read-ready" channel, the Select () method returns those channels where the read event is already ready.





The following is the Select () method:





int Select ()


int Select (Long timeout)


int Selectnow ()


Select () blocks to at least one channel that is ready for your registered event.





The Select (Long timeout) is the same as the select (), except that the maximum blocking timeout milliseconds (parameters).





Selectnow () will not block, no matter what channel is ready to return immediately (Translator Note: This method performs a non-blocking selection operation.) This method returns zero directly if no channel has become selectable since the previous selection operation. )。





The int value returned by the Select () method indicates how many channels are ready. That is, how many channels have become ready state since the last time the Select () method was called. If you call the Select () method, because one channel becomes ready, it returns 1, and if the Select () method is called again, it returns 1 if the other channel is ready. If you don't do anything with the first-ready channel, you now have two ready channels, but between each select () method call, there is only one channel ready.





Selectedkeys ()





Once the Select () method is invoked, and the return value indicates that one or more channels are ready, you can then access the Ready channel in the selected keyset (selected key set) by calling the selector Selectedkeys () method. As shown below:





Copy Code code as follows:

Set Selectedkeys = Selector.selectedkeys ();






When you register Channel like selector, the Channel.register () method returns a Selectionkey object. This object represents the channel registered to the selector. These objects can be accessed through the Selectionkey Selectedkeyset () method.





You can iterate through the selected key collection to access the Ready channel. As follows:





Copy Code code as follows:

Set Selectedkeys = Selector.selectedkeys ();
Iterator Keyiterator = Selectedkeys.iterator ();
while (Keyiterator.hasnext ()) {
Selectionkey key = Keyiterator.next ();
if (key.isacceptable ()) {
A connection is accepted by a serversocketchannel.
else if (key.isconnectable ()) {
A connection is established with a remote server.
else if (key.isreadable ()) {
A channel is ready for reading
else if (key.iswritable ()) {
A channel is ready for writing
}
Keyiterator.<tuihighlight class= "Tuihighlight" ><a href= "javascript:; style=" Display:inline;float:none; Position:inherit;cursor:pointer;color: #7962D5; text-decoration:underline; " >remove</a></tuihighlight> ();
}






This loop iterates through each key in the selected key set and detects the ready event for the channel corresponding to each key.





Note the Keyiterator.remove () call at the end of each iteration. Selector does not remove Selectionkey instances from the selected key set itself. You must remove yourself when the channel is finished processing. The next time the channel becomes ready, selector it into the selected key set again.





The Selectionkey.channel () method returns a channel that needs to be transformed into the type you want to process, such as Serversocketchannel or Socketchannel.





(6) WakeUp ()





After a thread calls the Select () method, it blocks, and even if no channel is ready, there is a way to get it back from the Select () method. Just let the other thread call the Selector.wakeup () method on the object on which the first thread calls the Select () method. The line Cheng that blocks on the Select () method is immediately returned.





If there are other threads calling the wakeup () method, but no thread is currently blocking on the Select () method, the next thread that calls the Select () method will immediately "wake up" (Wake up).





(7) Close ()





Calling its close () method after the selector is finished closes the selector and invalidates all Selectionkey instances registered to the selector. The channel itself is not closed.





(8) The complete example





Here is a complete example, open a selector, register a channel to register on this selector (channel initialization process omitted), and then continuously monitor the selector four events (accept, connect, read, write) is ready.





Copy Code code as follows:



Selector Selector = Selector.open ();


Channel.configureblocking (FALSE);


Selectionkey key = Channel.register (selector, selectionkey.op_read);


while (true) {


int readychannels = Selector.select ();


if (Readychannels = = 0) continue;


Set Selectedkeys = Selector.selectedkeys ();


Iterator Keyiterator = Selectedkeys.iterator ();


while (Keyiterator.hasnext ()) {


Selectionkey key = Keyiterator.next ();


if (key.isacceptable ()) {


A connection is accepted by a serversocketchannel.


else if (key.isconnectable ()) {


A connection is established with a remote server.


else if (key.isreadable ()) {


A channel is ready for reading


else if (key.iswritable ()) {


A channel is ready for writing


}


Keyiterator.&lt;tuihighlight class= "Tuihighlight" &gt;&lt;a href= "javascript:; style=" Display:inline;float:none; Position:inherit;cursor:pointer;color: #7962D5; text-decoration:underline; " &gt;remove&lt;/a&gt;&lt;/tuihighlight&gt; ();


}


}








File Channel





(This part of the original link, the author: Jakob Jenkov, translator: Jacky Chow, proofreading: Ding)


The FileChannel in Java NiO is a channel connected to a file. You can read and write files through the file channel.





FileChannel cannot be set to non-blocking mode, it is always running in blocking mode.





Open FileChannel





Before you can use FileChannel, you must first open it. However, we cannot directly open a filechannel and need to get a FileChannel instance by using a InputStream, OutputStream, or Randomaccessfile. The following is an example of opening filechannel through Randomaccessfile:





Copy Code code as follows:

Randomaccessfile afile = new Randomaccessfile ("Data/nio-data.txt", "RW");
FileChannel Inchannel = Afile.getchannel ();






Reading data from FileChannel





Invokes one of several read () methods to read data from the FileChannel. Such as:





Copy Code code as follows:

Bytebuffer buf = Bytebuffer.allocate (48);
int bytesread = Inchannel.read (BUF);






First, allocate a buffer. Data read from the FileChannel will be read in buffer.





Then, call the Filechannel.read () method. This method reads the data from the FileChannel into the buffer. The int value returned by the Read () method indicates how many bytes have been read in the buffer. If 1 is returned, it is represented at the end of the file.





Write data to FileChannel





Writes the data to the FileChannel using the Filechannel.write () method, which has a parameter of buffer. Such as:





Copy Code code as follows:

String NewData = "New String to write to file ..." + System.currenttimemillis ();

Bytebuffer buf = Bytebuffer.allocate (48);
Buf.clear ();
Buf.put (Newdata.getbytes ());

Buf.flip ();

while (Buf.hasremaining ()) {
Channel.write (BUF);
}






Note that Filechannel.write () is invoked in the while loop. Because the Write () method cannot be guaranteed how many bytes can be written to filechannel at a time, the write () method needs to be called repeatedly until there are no bytes in the buffer that have not yet been written to the channel.





Close FileChannel





You must close the FileChannel after you have finished using it. Such as:





Copy Code code as follows:

Channel.close ();






The position method of FileChannel





Sometimes you may need to read/write data at a specific location in FileChannel. You can get the current position of the FileChannel by calling the position () method.





You can also set the current position of the FileChannel by calling the position (long Pos) method.





Here are two examples:





Copy Code code as follows:

Long pos = Channel.position ();
Channel.position (pos +123);






If you set the location after the file terminator and then try to read the data from the file channel, the Read method returns the -1--file end flag.





If you set the location after the end of the file and then write the data to the channel, the file will hold up to the current location and write the data. This can result in a "file hole" where there is a gap between the data written in the physical file on the disk.





Size method of FileChannel





The size () method of the FileChannel instance will return the sizes of the files associated with the instance. Such as:





Copy Code code as follows:

Long fileSize = Channel.size ();






The Truncate method of FileChannel





You can use the Filechannel.truncate () method to intercept a file. When you intercept a file, the portion of the file that follows the specified length will be deleted. Such as:





Copy Code code as follows:

Channel.truncate (1024);






This example intercepts the first 1024 bytes of the file.





The Force method of FileChannel





The Filechannel.force () method forces data that has not been written to disk in the channel to be written to disk. For performance reasons, the operating system caches data in memory, so it is not guaranteed that the data written to the FileChannel will be written to disk immediately. To ensure this, you need to invoke the force () method.





The Force () method has a Boolean parameter that indicates whether file metadata (permission information, and so on) is also written to disk.





The following example forces file data and metadata to be written to disk at the same time:





Copy Code code as follows:

Channel.force (TRUE);






Socket Channel





(This part of the original link, the author: Jakob Jenkov, translator: Zheng Yuting, proofreading: Ding)


The Socketchannel in Java NiO is a channel that is connected to a TCP network socket. You can create Socketchannel in the following 2 ways:





Open a socketchannel and connect to a server on the Internet.


When a new connection arrives at Serversocketchannel, a socketchannel is created.


Open Socketchannel





The following is how Socketchannel is opened:





Copy Code code as follows:

Socketchannel Socketchannel = Socketchannel.open ();
Socketchannel.connect (New Inetsocketaddress ("http://jenkov.com", 80));






Close Socketchannel





Call Socketchannel.close () Close Socketchannel after Socketchannel is exhausted:





Copy Code code as follows:

Socketchannel.close ();






Reading data from Socketchannel





To read data from Socketchannel, call one of the Read () methods. Here is an example:





Copy Code code as follows:

Bytebuffer buf = Bytebuffer.allocate (48);
int bytesread = Socketchannel.read (BUF);






First, allocate a buffer. The data read from the Socketchannel will be placed in this buffer.





Then, call Socketchannel.read (). This method reads the data from the Socketchannel into the buffer. The int value returned by the Read () method indicates how many bytes were read into the buffer. If-1 is returned, it means that the end of the stream has been read (the connection is closed).





Write to Socketchannel





The Socketchannel.write () method is used to write data to Socketchannel, which takes a buffer as an argument. Examples are as follows:





Copy Code code as follows:

String NewData = "New String to write to file ..." + System.currenttimemillis ();

Bytebuffer buf = Bytebuffer.allocate (48);
Buf.clear ();
Buf.put (Newdata.getbytes ());

Buf.flip ();

while (Buf.hasremaining ()) {
Channel.write (BUF);
}






Note that the call to the Socketchannel.write () method is in a while loop. The write () method cannot guarantee how many bytes can be written to Socketchannel. So, we repeatedly call write () until the buffer has no bytes to write.





Non-blocking mode





You can set Socketchannel as non-blocking (non-blocking mode). After setting, you can use Connect (), read () and write () in asynchronous mode.





Connect ()





If Socketchannel is calling connect () in non-blocking mode, the method may return before the connection is established. To determine whether a connection is established, you can call the Finishconnect () method. Like this:





Copy Code code as follows:

Socketchannel.configureblocking (FALSE);
Socketchannel.connect (New Inetsocketaddress ("http://jenkov.com", 80));

while (! Socketchannel.finishconnect ()) {
Wait, or do something else ...
}






Write ()





In non-blocking mode, the Write () method may return when no content has been written. So you need to call write () in the loop. There are already examples, and there are no more.





Read ()





In non-blocking mode, the Read () method may return when no data has been read. So you need to pay attention to its int return value and it tells you how many bytes you read.





Non-blocking mode and selector





Non-blocking mode works better with selectors, by registering one or more socketchannel to selector, you can ask the selector which channel is ready to read, write, etc. The use of selector and Socketchannel will be detailed later.





ServerSocket Channel





(This part of the original link, the author: Jakob Jenkov, translator: Zheng Yuting, proofreading: Ding)


The Serversocketchannel in Java NiO is a channel that listens to incoming TCP connections, just like the serversocket in standard IO. The Serversocketchannel class is in the Java.nio.channels package.





Here's an example:





Copy Code code as follows:

Serversocketchannel Serversocketchannel = Serversocketchannel.open ();

Serversocketchannel.socket (). bind (New Inetsocketaddress (9999));

while (true) {
Socketchannel Socketchannel =
Serversocketchannel.accept ();

Do something with Socketchannel ...
}






Open Serversocketchannel





Open Serversocketchannel by calling the Serversocketchannel.open () method.





Copy Code code as follows:

Serversocketchannel Serversocketchannel = Serversocketchannel.open ();






Close Serversocketchannel





Turn off Serversocketchannel by calling the Serversocketchannel.close () method. Such as:





Copy Code code as follows:

Serversocketchannel.close ();






Listening for new Incoming connections





Listen for new incoming connections through the Serversocketchannel.accept () method. When the Accept () method returns, it returns a Socketchannel containing the newly entered connection. Therefore, the accept () method blocks until a new connection arrives.





The Accept () method is typically not used to listen to only one connection, but to the while loop. As in the following example:





Copy Code code as follows:

while (true) {
Socketchannel Socketchannel =
Serversocketchannel.accept ();

Do something with Socketchannel ...
}






Of course, you can also use an exit criterion other than true in the while loop.





Non-blocking mode





Serversocketchannel can be set to non-blocking mode. In non-blocking mode, the Accept () method returns immediately, and if there is no new incoming connection, the return will be null. Therefore, you need to check whether the returned Socketchannel is null. Such as:





Copy Code code as follows:

Serversocketchannel Serversocketchannel = Serversocketchannel.open ();

Serversocketchannel.socket (). bind (New Inetsocketaddress (9999));
Serversocketchannel.configureblocking (FALSE);

while (true) {
Socketchannel Socketchannel =
Serversocketchannel.accept ();

if (Socketchannel!= null) {
Do something with Socketchannel ...
}
}






Datagram Channel





(This part of the original link, the author: Jakob Jenkov, translator: Zheng Yuting, proofreading: Ding)


The Datagramchannel in Java NiO is a channel that can send and receive UDP packets. Because UDP is a connectionless network protocol, it cannot be read and written like any other channel. It sends and receives packets.





Open Datagramchannel





The following is how Datagramchannel is opened:





Copy Code code as follows:

Datagramchannel channel = Datagramchannel.open ();
Channel.socket (). bind (New Inetsocketaddress (9999));






This example opens a Datagramchannel that can receive packets on UDP port 9999.





Receive data





Receive data from Datagramchannel through the receive () method, such as:





Copy Code code as follows:

Bytebuffer buf = Bytebuffer.allocate (48);
Buf.clear ();
Channel.receive (BUF);






The receive () method copies the received packet contents to the specified buffer. If the buffer does not fit the received data, the extra data will be discarded.





Send data





Send data from Datagramchannel through the Send () method, such as:





Copy Code code as follows:

String NewData = "New String to write to file ..." + System.currenttimemillis ();

Bytebuffer buf = Bytebuffer.allocate (48);
Buf.clear ();
Buf.put (Newdata.getbytes ());
Buf.flip ();

int bytessent = Channel.send (buf, New inetsocketaddress ("jenkov.com", 80));






This example sends a string of characters to the UDP port 80 on the "jenkov.com" server. Because the server does not monitor this port, nothing will happen. You will not be notified whether the packets sent are received because UDP has no guarantee of data transfer.





Connect to a specific address





Datagramchannel can be "connected" to a specific address on the network. Because UDP is not connected, connecting to a specific address does not create a true connection as the TCP channel does. Instead, it locks up the Datagramchannel to send and receive data only from a specific address.





Here's an example:





Copy Code code as follows:

Channel.connect (New Inetsocketaddress ("jenkov.com", 80));






When connected, you can also use the read () and write () methods as if you were using a traditional channel. There is no guarantee of data transfer. Here are a few examples:





Copy Code code as follows:

int bytesread = Channel.read (BUF);
int byteswritten = Channel.write (But);






Pipe (Pipe)





(This part of the original link, the author: Jakob Jenkov, translator: Jack, proofreading: Ding)


The Java NIO pipe is a one-way data connection between 2 threads. Pipe has a source channel and a sink channel. The data is written to the sink channel and is read from the source channel.





Here is the diagram of the pipe principle:





Creating pipelines





Open the pipe through the Pipe.open () method. For example:





Copy Code code as follows:

Pipe Pipe = Pipe.open ();






Write data to a pipe





To write data to a pipe, you need to access the sink channel. Like this:





Copy Code code as follows:

Pipe.sinkchannel Sinkchannel = Pipe.sink ();






Write the data to Sinkchannel, like this by calling the Sinkchannel write () method:





Copy Code code as follows:

String NewData = "New String to write to file ..." + System.currenttimemillis ();
Bytebuffer buf = Bytebuffer.allocate (48);
Buf.clear ();
Buf.put (Newdata.getbytes ());

Buf.flip ();

while (Buf.hasremaining ()) {
<b>sinkchannel.write (BUF);</b>
}
[Code]

Reading data from a pipe

From reading the pipeline's data, you need access to the source channel, like this:

[Code]
Pipe.sourcechannel Sourcechannel = Pipe.source ();







Call the Read () method of the source channel to read the data, like this:





Copy Code code as follows:

Bytebuffer buf = Bytebuffer.allocate (48);

int bytesread = Inchannel.read (BUF);






The int value returned by the Read () method tells us how many bytes were read into the buffer.


Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.