Java USES the NIO package to implement instance code for Socket communication

  • 2020-06-03 06:36:30
  • OfStack

The previous articles covered Socket communication using the java.io and java.net class libraries. The following is Socket using java.nio class libraries.

The ES12en. nio package was added to Java after 1.4 to improve the efficiency of I/O operations. The nio package mainly includes the following classes or interfaces:

Buffer: Buffer for temporary storage of input or output data. Charset: Used to interconvert Unicode character encodings with other character encodings. Channel: A data transfer channel used to write or read data from Buffer to a data source. Selector: Used to support asynchronous I/O operations, also known as non-blocking I/O operations.

In the nio package, the I/O operation efficiency is improved mainly through the following two aspects:

Speed up I/O operations with Buffer and Channel. Non-blocking I/O operations are supported through Selector.

Let's look at how the Socket functionality is implemented in a program using these libraries.

Let's start with a few helper classes

The helper class SerializableUtil is used to serialize the java object into a byte array or deserialize the byte array into an java object.


package com.googlecode.garbagecan.test.socket; 
 
import java.io.ByteArrayInputStream; 
import java.io.ByteArrayOutputStream; 
import java.io.IOException; 
import java.io.ObjectInputStream; 
import java.io.ObjectOutputStream; 
 
public class SerializableUtil { 
   
  public static byte[] toBytes(Object object) { 
    ByteArrayOutputStream baos = new ByteArrayOutputStream(); 
    ObjectOutputStream oos = null; 
    try { 
      oos = new ObjectOutputStream(baos); 
      oos.writeObject(object); 
      byte[] bytes = baos.toByteArray(); 
      return bytes; 
    } catch(IOException ex) { 
      throw new RuntimeException(ex.getMessage(), ex); 
    } finally { 
      try { 
        oos.close(); 
      } catch (Exception e) {} 
    } 
  } 
   
  public static Object toObject(byte[] bytes) { 
    ByteArrayInputStream bais = new ByteArrayInputStream(bytes); 
    ObjectInputStream ois = null; 
    try { 
      ois = new ObjectInputStream(bais); 
      Object object = ois.readObject(); 
      return object; 
    } catch(IOException ex) { 
      throw new RuntimeException(ex.getMessage(), ex); 
    } catch(ClassNotFoundException ex) { 
      throw new RuntimeException(ex.getMessage(), ex); 
    } finally { 
      try { 
        ois.close(); 
      } catch (Exception e) {} 
    } 
  } 
} 

The helper classes MyRequestObject and MyResponseObject, which are common java objects, implement the Serializable interface. The MyRequestObject class is the request from Client, and MyResponseObject is the response from the Server side.


package com.googlecode.garbagecan.test.socket.nio; 
 
import java.io.Serializable; 
 
public class MyRequestObject implements Serializable { 
 
  private static final long serialVersionUID = 1L; 
 
  private String name; 
   
  private String value; 
 
  private byte[] bytes; 
   
  public MyRequestObject(String name, String value) { 
    this.name = name; 
    this.value = value; 
    this.bytes = new byte[1024]; 
  } 
   
  public String getName() { 
    return name; 
  } 
 
  public void setName(String name) { 
    this.name = name; 
  } 
 
  public String getValue() { 
    return value; 
  } 
 
  public void setValue(String value) { 
    this.value = value; 
  } 
   
  @Override 
  public String toString() { 
    StringBuffer sb = new StringBuffer(); 
    sb.append("Request [name: " + name + ", value: " + value + ", bytes: " + bytes.length+ "]"); 
    return sb.toString(); 
  } 
} 
 
package com.googlecode.garbagecan.test.socket.nio; 
 
import java.io.Serializable; 
 
public class MyResponseObject implements Serializable { 
 
  private static final long serialVersionUID = 1L; 
 
  private String name; 
   
  private String value; 
 
  private byte[] bytes; 
   
  public MyResponseObject(String name, String value) { 
    this.name = name; 
    this.value = value; 
    this.bytes = new byte[1024]; 
  } 
   
  public String getName() { 
    return name; 
  } 
 
  public void setName(String name) { 
    this.name = name; 
  } 
 
  public String getValue() { 
    return value; 
  } 
 
  public void setValue(String value) { 
    this.value = value; 
  } 
   
  @Override 
  public String toString() { 
    StringBuffer sb = new StringBuffer(); 
    sb.append("Response [name: " + name + ", value: " + value + ", bytes: " + bytes.length+ "]"); 
    return sb.toString(); 
  } 
} 

The following is mainly to look at the code of Server. There are some English comments which are very helpful for understanding the code. The comments are mainly from the documents and examples of jdk


package com.googlecode.garbagecan.test.socket.nio; 
 
import java.io.ByteArrayOutputStream; 
import java.io.IOException; 
import java.net.InetSocketAddress; 
import java.nio.ByteBuffer; 
import java.nio.channels.ClosedChannelException; 
import java.nio.channels.SelectionKey; 
import java.nio.channels.Selector; 
import java.nio.channels.ServerSocketChannel; 
import java.nio.channels.SocketChannel; 
import java.util.Iterator; 
import java.util.logging.Level; 
import java.util.logging.Logger; 
 
import com.googlecode.garbagecan.test.socket.SerializableUtil; 
 
public class MyServer3 { 
 
  private final static Logger logger = Logger.getLogger(MyServer3.class.getName()); 
   
  public static void main(String[] args) { 
    Selector selector = null; 
    ServerSocketChannel serverSocketChannel = null; 
     
    try { 
      // Selector for incoming time requests 
      selector = Selector.open(); 
 
      // Create a new server socket and set to non blocking mode 
      serverSocketChannel = ServerSocketChannel.open(); 
      serverSocketChannel.configureBlocking(false); 
       
      // Bind the server socket to the local host and port 
      serverSocketChannel.socket().setReuseAddress(true); 
      serverSocketChannel.socket().bind(new InetSocketAddress(10000)); 
       
      // Register accepts on the server socket with the selector. This 
      // step tells the selector that the socket wants to be put on the 
      // ready list when accept operations occur, so allowing multiplexed 
      // non-blocking I/O to take place. 
      serverSocketChannel.register(selector, SelectionKey.OP_ACCEPT); 
   
      // Here's where everything happens. The select method will 
      // return when any operations registered above have occurred, the 
      // thread has been interrupted, etc. 
      while (selector.select() > 0) { 
        // Someone is ready for I/O, get the ready keys 
        Iterator<SelectionKey> it = selector.selectedKeys().iterator(); 
   
        // Walk through the ready keys collection and process date requests. 
        while (it.hasNext()) { 
          SelectionKey readyKey = it.next(); 
          it.remove(); 
           
          // The key indexes into the selector so you 
          // can retrieve the socket that's ready for I/O 
          execute((ServerSocketChannel) readyKey.channel()); 
        } 
      } 
    } catch (ClosedChannelException ex) { 
      logger.log(Level.SEVERE, null, ex); 
    } catch (IOException ex) { 
      logger.log(Level.SEVERE, null, ex); 
    } finally { 
      try { 
        selector.close(); 
      } catch(Exception ex) {} 
      try { 
        serverSocketChannel.close(); 
      } catch(Exception ex) {} 
    } 
  } 
 
  private static void execute(ServerSocketChannel serverSocketChannel) throws IOException { 
    SocketChannel socketChannel = null; 
    try { 
      socketChannel = serverSocketChannel.accept(); 
      MyRequestObject myRequestObject = receiveData(socketChannel); 
      logger.log(Level.INFO, myRequestObject.toString()); 
       
      MyResponseObject myResponseObject = new MyResponseObject( 
          "response for " + myRequestObject.getName(),  
          "response for " + myRequestObject.getValue()); 
      sendData(socketChannel, myResponseObject); 
      logger.log(Level.INFO, myResponseObject.toString()); 
    } finally { 
      try { 
        socketChannel.close(); 
      } catch(Exception ex) {} 
    } 
  } 
   
  private static MyRequestObject receiveData(SocketChannel socketChannel) throws IOException { 
    MyRequestObject myRequestObject = null; 
    ByteArrayOutputStream baos = new ByteArrayOutputStream(); 
    ByteBuffer buffer = ByteBuffer.allocate(1024); 
     
    try { 
      byte[] bytes; 
      int size = 0; 
      while ((size = socketChannel.read(buffer)) >= 0) { 
        buffer.flip(); 
        bytes = new byte[size]; 
        buffer.get(bytes); 
        baos.write(bytes); 
        buffer.clear(); 
      } 
      bytes = baos.toByteArray(); 
      Object obj = SerializableUtil.toObject(bytes); 
      myRequestObject = (MyRequestObject)obj; 
    } finally { 
      try { 
        baos.close(); 
      } catch(Exception ex) {} 
    } 
    return myRequestObject; 
  } 
 
  private static void sendData(SocketChannel socketChannel, MyResponseObject myResponseObject) throws IOException { 
    byte[] bytes = SerializableUtil.toBytes(myResponseObject); 
    ByteBuffer buffer = ByteBuffer.wrap(bytes); 
    socketChannel.write(buffer); 
  } 
} 

Here is the code for Client, which is as simple as starting 100 threads to access Server


package com.googlecode.garbagecan.test.socket.nio; 
 
import java.io.ByteArrayOutputStream; 
import java.io.IOException; 
import java.net.InetSocketAddress; 
import java.net.SocketAddress; 
import java.nio.ByteBuffer; 
import java.nio.channels.SocketChannel; 
import java.util.logging.Level; 
import java.util.logging.Logger; 
 
import com.googlecode.garbagecan.test.socket.SerializableUtil; 
 
public class MyClient3 { 
 
  private final static Logger logger = Logger.getLogger(MyClient3.class.getName()); 
   
  public static void main(String[] args) throws Exception { 
    for (int i = 0; i < 100; i++) { 
      final int idx = i; 
      new Thread(new MyRunnable(idx)).start(); 
    } 
  } 
   
  private static final class MyRunnable implements Runnable { 
     
    private final int idx; 
 
    private MyRunnable(int idx) { 
      this.idx = idx; 
    } 
 
    public void run() { 
      SocketChannel socketChannel = null; 
      try { 
        socketChannel = SocketChannel.open(); 
        SocketAddress socketAddress = new InetSocketAddress("localhost", 10000); 
        socketChannel.connect(socketAddress); 
 
        MyRequestObject myRequestObject = new MyRequestObject("request_" + idx, "request_" + idx); 
        logger.log(Level.INFO, myRequestObject.toString()); 
        sendData(socketChannel, myRequestObject); 
         
        MyResponseObject myResponseObject = receiveData(socketChannel); 
        logger.log(Level.INFO, myResponseObject.toString()); 
      } catch (Exception ex) { 
        logger.log(Level.SEVERE, null, ex); 
      } finally { 
        try { 
          socketChannel.close(); 
        } catch(Exception ex) {} 
      } 
    } 
 
    private void sendData(SocketChannel socketChannel, MyRequestObject myRequestObject) throws IOException { 
      byte[] bytes = SerializableUtil.toBytes(myRequestObject); 
      ByteBuffer buffer = ByteBuffer.wrap(bytes); 
      socketChannel.write(buffer); 
      socketChannel.socket().shutdownOutput(); 
    } 
 
    private MyResponseObject receiveData(SocketChannel socketChannel) throws IOException { 
      MyResponseObject myResponseObject = null; 
      ByteArrayOutputStream baos = new ByteArrayOutputStream(); 
       
      try { 
        ByteBuffer buffer = ByteBuffer.allocateDirect(1024); 
        byte[] bytes; 
        int count = 0; 
        while ((count = socketChannel.read(buffer)) >= 0) { 
          buffer.flip(); 
          bytes = new byte[count]; 
          buffer.get(bytes); 
          baos.write(bytes); 
          buffer.clear(); 
        } 
        bytes = baos.toByteArray(); 
        Object obj = SerializableUtil.toObject(bytes); 
        myResponseObject = (MyResponseObject) obj; 
        socketChannel.socket().shutdownInput(); 
      } finally { 
        try { 
          baos.close(); 
        } catch(Exception ex) {} 
      } 
      return myResponseObject; 
    } 
  } 
} 

Finally, to test the above code, run the Server class first, then the Client class, and you can see the sent or received MyRequestObject or MyResponseObject objects on the Server side and the Client side console, respectively.


Related articles: