In JDK, there is a very interesting Library: NiO (New I/O ). This library has three important classes: Selector and channel in Java. NiO. channels and buffer in Java. NiO.
This articleArticleFirst, let's take a look at why NiO is needed for network programming. Then, let's take a look at how to use NiO in network programming step by step.
Why NiO
Socket written in JavaProgramYou must know socket and socketserver. When a call is called, the call is blocked and waits for a response. This method is very convenient for small-scale programs, but it is a little powerless for large programs. When there are a large number of connections, we can create a thread for each connection to operate. However, the defects brought about by this practice are also obvious:
The hardware supports a large amount of concurrency.
There is always an upper limit on the number of concurrent requests.
The priority of each thread is difficult to control.
Interaction and synchronization between clients is difficult.
We can also use a thread to process all requests and use non-blocking Io to poll and query all clients. This method also has a defect: it cannot quickly respond to the client, and it consumes a lot of time for round-robin queries.
Therefore, we need a poll mode to deal with this situation and find out the client that really needs services from a large number of network connections. This is exactly why NiO was born: it provides a poll mode to find the client to be served among all the clients.
Return to the three most important classes we just mentioned: Selector and channel in Java. NiO. channels, and buffer in Java. NiO.
A channel represents an object that can be used for Poll operations (it can be a file stream or a network stream). A channel can be registered to a selector. By calling the select method of selector, you can find the instance (accept, read...) to be served from all channels ..). The buffer object provides the cache for reading and writing data. Compared with the stream object we are familiar with, buffer provides better performance and better programming transparency (artificially controlling the cache size and specific operations ).
Use channel with buffer
It is not used for programming in the traditional mode. The channel does not use stream, but buffer. Let's implement a simple non-blocking echo client:
Package Com. cnblogs. gpcuster;
Import Java.net. inetsocketaddress;
Import Java.net. socketexception;
Import Java. NiO. bytebuffer;
Import Java. NiO. channels. socketchannel;
Public Class Tcpechoclientnonblocking {
Public Static Void Main (string ARGs []) Throws Exception {
If (ARGs. Length <2) | (ARGs. length> 3 )) // Testforcorrect # ofargs Throw New Illegalargumentexception (
" Parameter (s): <Server> <word> [<port>] ");
String Server = ARGs [0]; // Servernameoripaddress // Convertinputstringtobytesusingthedefacharcharset Byte [] Argument = ARGs [1]. getbytes ();
Int Servport = (ARGs. Length = 3 )? Integer. parseint (ARGs [2]): 7;
// Createchannelandsettononblocking Socketchannel clntchan = socketchannel. open (); clntchan. configureblocking ( False );
// Initiateconnectiontoserverandrepeatedlypolluntilcomplete If (! Clntchan. Connect ( New Inetsocketaddress (server, servport ))){
While (! Clntchan. finishconnect ()){
System. Out. Print (" . "); // Dosomethingelse } Bytebuffer writebuf = bytebuffer. Wrap (argument); bytebuffer readbuf = bytebuffer. Allocate (argument. Length ); Int Totalbytesrcvd = 0; // Totalbytesreceivedsofar Int Bytesrcvd; // Bytesreceivedinlastread While (Totalbytesrcvd <argument. Length ){
If (Writebuf. hasremaining ()){
Clntchan. Write (writebuf );
}
If (Bytesrcvd = clntchan. Read (readbuf) =-1 ){
Throw New Socketexception (" Connection closed prematurely ");
}
Totalbytesrcvd + = bytesrcvd;
System. Out. Print (" . "); // Dosomethingelse } System. Out. println (" Received: "+// Converttostringperdefacharcharset New String (readbuf. Array (), 0, totalbytesrcvd ));
Clntchan. Close ();
}
}
This sectionCodeUse bytebuffer to save read and write data. Use clntchan. configureblocking (False); After the setting, the connect, read, and write operations are not blocked, but the result is immediately put back.
Use Selector
Selector can find the desired service instance from all registered channels.
Let's implement echo server.
First, define an interface:
PackageCom. cnblogs. gpcuster;
ImportJava. NiO. channels. selectionkey;
ImportJava. Io. ioexception;
Public InterfaceTcpprotocol {
VoidHandleaccept (selectionkey key)ThrowsIoexception;
VoidHandleread (selectionkey key)ThrowsIoexception;
VoidHandlewrite (selectionkey key)ThrowsIoexception;
}
Our echo server will use this interface. Then we implement echo server:
Import Java. Io. ioexception;
Import Java.net. inetsocketaddress;
Import Java. NiO. channels. selectionkey;
Import Java. NiO. channels. selector;
Import Java. NiO. channels. serversocketchannel;
Import Java. util. iterator;
Public Class Tcpserverselector {
Private Static Final Int Bufsize = 256;// Buffersize (bytes) Private Static Final Int Timeout = 3000; // Waittimeout (milliseconds) Public Static Void Main (string [] ARGs) Throws Ioexception {
If (ARGs. Length <1 ){ // Testforcorrect # ofargs Throw New Illegalargumentexception (" Parameter (s): <port>... ");
}
// Createaselectortomultiplexlisteningsocketsandconnections Selector selector = selector. open (); // Createlisteningsocketchannelforeachportandregisterselector For (String Arg: ARGs ){
Serversocketchannel listnchannel = serversocketchannel. open ();
Listnchannel. socket (). BIND (
New Inetsocketaddress (integer. parseint (ARG )));
Listnchannel. configureblocking ( False );// Mustbenonblockingtoregister // Registerselectorwithchannel. thereturnedkeyisignored Listnchannel. Register (selector, selectionkey. op_accept );} // Createahandlerthatwilliam mplementtheprotocol Tcpprotocol protocol = New Echoselectorprotocol (bufsize );
While ( True ){ // Runforever, processingavailablei/oOperations // Waitforsomechanic neltobeready (ortimeout) If (Selector. Select (timeout) = 0 ){// Returns # ofreadychans System. Out. Print (" . ");
Continue ;
}
// Getiteratoronsetofkeyswithi/otoprocess Iterator <selectionkey> keyiter = selector. selectedkeys (). iterator (); While (Keyiter. hasnext ()){
Selectionkey key = keyiter. Next (); // Keyisbitmask // Serversocketchannelhaspendingconnectionrequests? If (Key. isacceptable ()){
Protocol. handleaccept (key );
}
// Clientsocketchannelhaspendingdata? If (Key. isreadable ()){
Protocol. handleread (key );
}
// Clientsocketchannelisavailableforwritingand // Keyisvalid (I. e., channelnotclosed )? If (Key. isvalid () & Key. iswritable ()){
Protocol. handlewrite (key );
}
Keyiter. Remove (); // Removefromsetofselectedkeys }}}}
We use listnchannel. register (selector, selectionkey. op_accept); registers an event we are interested in, and then calls selector. select (timeout) waits for the subscription time to occur, and then takes corresponding measures.
Finally, we implement echoselectorprotocol
Package Com. cnblogs. gpcuster;
Import Java. NiO. channels. selectionkey;
Import Java. NiO. channels. socketchannel;
Import Java. NiO. channels. serversocketchannel;
Import Java. NiO. bytebuffer;
Import Java. Io. ioexception;
Public Class Echoselectorprotocol Implements Tcpprotocol {
Private Int Bufsize; // Sizeofi/obuffer Public Echoselectorprotocol ( Int Bufsize ){
This . Bufsize = bufsize;
}
Public Void Handleaccept (selectionkey key) Throws Ioexception {
Socketchannel clntchan = (serversocketchannel) Key. Channel (). Accept ();
Clntchan. configureblocking ( False ); // Mustbenonblockingtoregister // Registertheselectorwithnewchannelforreadandattachbytebuffer Clntchan. Register (key. selector (), selectionkey. op_read, bytebuffer. Allocate (bufsize ));} Public Void Handleread (selectionkey key) Throws Ioexception {
// Clientsocketchannelhaspendingdata Socketchannel clntchan = (socketchannel) Key. Channel (); bytebuffer Buf = (bytebuffer) Key. Attachment (); Long Bytesread = clntchan. Read (BUF );
If (Bytesread =-1 ){ // Didtheotherendclose? Clntchan. Close ();} Else If (Bytesread> 0 ){
// Indicateviakeythatreading/writingarebothofinterestnow. Key. interestops (selectionkey. op_read | selectionkey. op_write );}} Public Void Handlewrite (selectionkey key) Throws Ioexception {
/*
* Channelisavailableforwriting, andkeyisvalid (I. e., clientchannel
* Notclosed ).
*/ // Retrievedatareadearlier Bytebuffer Buf = (bytebuffer) Key. Attachment (); Buf. Flip (); // Preparebufferforwriting Socketchannel clntchan = (socketchannel) Key. Channel (); clntchan. Write (BUF ); If (! Buf. hasremaining ()){ // Buffercompletelywritten? // Nothingleft, sonolongerinterestedinwrites Key. interestops (selectionkey. op_read);} Buf. Compact (); // Makeroomformoredatatobereadin }}
Here, we further register the relevant event for selector: Key. interestops (selectionkey. op_read );
In this way, we have implemented the NiO-based echo system.
Reference
TCP/IP. sockets. In. java.2nd. Edition