Nio is short for java New IO. It is a New api provided in jdk1.4. The following is a small compilation to help you sort out a socket communication instance program for nio in java, let's take a look at it with me.
Although the Java platform has a lot of pretty good step-by-step socket frameworks, such as Netty and naga, we should be aware of it.
Nio is short for java New IO. The New api provided by jdk1.4 has the following features officially advertised by Sun:
Provide Buffer cache support for all original types
Character Set encoding and decoding solution
Channel: A new original I/O Abstraction
Supports file access interfaces for lock and memory ing files
Provides non-bloking (non-bloking) non-blocking high-scalability network I/O
Basic Principles
NIO has a main class Selector. This is similar to an observer. As long as we tell the Selector about the SocketChannel to be explored, we will continue to do other things. When an event occurs, it will notify us, return a group of selectionkeys. When we read these keys, we will obtain the SocketChannel we just registered. Then, we will read data from this Channel, so we can rest assured that the package will be able to read, then we can process the data.
The internal principle of Selector is actually to make a round-robin access to the registered channel and continuously round-robin (this algorithm currently). Once a channel is registered, for example, when the data comes, he will stand up and report, hand over a key, and let us read the contents of this channel through this key.
The code written based on nio is a bit cool, so it is better to write it with a framework. The functions implemented by this server are relatively simple, but only the data sent from the client is returned.
Server
Initialize Selector
| The Code is as follows: |
Copy code |
|
Private Selector initSelector () throws IOException {
Selector socketSelector = SelectorProvider. provider (). openSelector ();
This. serverChannel = ServerSocketChannel. open ();
ServerChannel. configureBlocking (false );
InetSocketAddress isa = new InetSocketAddress (this. hostAddress, this. port );
ServerChannel. socket (). bind (isa );
ServerChannel. register (socketSelector, SelectionKey. OP_ACCEPT );
Return socketSelector;
} |
Accept incoming connections
| The Code is as follows: |
Copy code |
|
Private void accept (SelectionKey key) throws IOException {
ServerSocketChannel serverSocketChannel = (ServerSocketChannel) key. channel ();
SocketChannel socketChannel = serverSocketChannel. accept ();
Socket socket = socketChannel. socket ();
SocketChannel. configureBlocking (false );
SocketChannel. register (this. selector, SelectionKey. OP_READ );
LOGGER.info ("ACCEPT:" + socket. getInetAddress (). toString ());
} |
Accept data
| The Code is as follows: |
Copy code |
|
Private void read (SelectionKey key) throws IOException {
SocketChannel socketChannel = (SocketChannel) key. channel ();
This. readBuffer. clear ();
Int numRead;
Try {
NumRead = socketChannel. read (this. readBuffer );
} Catch (IOException e ){
Key. cancel ();
SocketChannel. close ();
Return;
}
If (numRead =-1 ){
Key. channel (). close ();
Key. cancel ();
Return;
}
// Hand the data off to our worker thread
This. worker. processData (this, socketChannel, this. readBuffer. array (), numRead );
} |
Send data
| The Code is as follows: |
Copy code |
|
Private void write (SelectionKey key) throws IOException {
SocketChannel socketChannel = (SocketChannel) key. channel ();
List <ByteBuffer> queue = this. pendingData. get (socketChannel );
While (! Queue. isEmpty ()){
ByteBuffer buf = queue. get (0 );
SocketChannel. write (buf );
If (buf. remaining ()> 0 ){
Break;
}
Queue. remove (0 );
}
If (queue. isEmpty ()){
Key. interestOps (SelectionKey. OP_READ );
}
} |
Client
Client Initialization is quite simple
| The Code is as follows: |
Copy code |
|
Private Selector initSelector () throws IOException {
Return SelectorProvider. provider (). openSelector ();
} |
Accept data
| The Code is as follows: |
Copy code |
|
Private void read (SelectionKey key) throws IOException {
SocketChannel socketChannel = (SocketChannel) key. channel ();
This. readBuffer. clear ();
Int numRead;
Try {
NumRead = socketChannel. read (this. readBuffer );
} Catch (IOException e ){
Key. cancel ();
SocketChannel. close ();
Return;
}
If (numRead =-1 ){
Key. channel (). close ();
Key. cancel ();
Return;
}
// Handle the response
This. handleResponse (socketChannel, this. readBuffer. array (), numRead );
} |
Send data
| The Code is as follows: |
Copy code |
|
Private void write (SelectionKey key) throws IOException {
SocketChannel socketChannel = (SocketChannel) key. channel ();
List <ByteBuffer> queue = this. pendingData. get (socketChannel );
While (! Queue. isEmpty ()){
ByteBuffer buf = queue. get (0 );
SocketChannel. write (buf );
If (buf. remaining ()> 0 ){
Break;
}
Queue. remove (0 );
}
If (queue. isEmpty ()){
Key. interestOps (SelectionKey. OP_READ );
}
} |
Auxiliary class
EchoWorker is responsible for sending the received client data back. Your specific business logic can be achieved through a similar method.
| The Code is as follows: |
Copy code |
Import java. nio. channels. SocketChannel;
Import java. util. concurrent. BlockingQueue;
Import java. util. concurrent. Define blockingqueue;
Public class EchoWorker implements Runnable {
Private BlockingQueue <ServerDataEvent> queue = new LinkedBlockingQueue <ServerDataEvent> ();
Public void processData (NioServer, SocketChannel socket, byte [] data, int count ){
Byte [] dataCopy = new byte [count];
System. arraycopy (data, 0, dataCopy, 0, count );
Try {
Queue. put (new ServerDataEvent (server, socket, dataCopy ));
} Catch (InterruptedException e ){
E. printStackTrace ();
}
}
Public void run (){
While (true ){
Try {
ServerDataEvent dataEvent = queue. take ();
// Return to sender
DataEvent. server. send (dataEvent. socket, dataEvent. data );
} Catch (InterruptedException e ){
E. printStackTrace ();
}
}
}
}
|
ChangeRequest is responsible for processing the SocketChannel status
| The Code is as follows: |
Copy code |
|
Import java. nio. channels. SocketChannel;
Public class ChangeRequest {
Public static final int REGISTER = 1;
Public static final int CHANGEOPS = 2;
Public SocketChannel socket;
Public int type;
Public int ops;
Public ChangeRequest (SocketChannel socket, int type, int ops ){
This. socket = socket;
This. type = type;
This. ops = ops;
}
} |
RspHandler is responsible for processing client data. In actual cases, you can process your own business logic in this class.
| The Code is as follows: |
Copy code |
|
Public class RspHandler {
Private byte [] rsp = null;
Public synchronized boolean handleResponse (byte [] rsp ){
This. rsp = rsp;
This. Policy ();
Return true;
}
Public synchronized void waitForResponse (){
While (this. rsp = null ){
Try {
This. wait ();
} Catch (InterruptedException e ){
}
}
System. out. println ("process message:" + new String (this. rsp ));
}
} |
Start
| The Code is as follows: |
Copy code |
|
// Start the client
Public static void main (String [] args) throws UnknownHostException, IOException {
For (int I = 0; I <10; I ++ ){
Try {
NioClient client = new NioClient (InetAddress. getLocalHost (), 9090 );
Thread t = new Thread (client );
T. setDaemon (true );
T. start ();
RspHandler handler = new RspHandler ();
Client. send ("test message" + I + 10). getBytes (), handler );
Handler. waitForResponse ();
} Catch (Exception e ){
E. printStackTrace ();
}
}
}
// Start the server
Public static void main (String [] args ){
Try {
EchoWorker worker = new EchoWorker ();
New Thread (worker). start ();
New Thread (new NioServer (null, 9090, worker). start ();
} Catch (IOException e ){
E. printStackTrace ();
}
} |