基于Java NIO的Socket通信

Java NIO模式的Socket通信,是一种同步非阻塞IO设计模式,它为Reactor模式实现提供了基础。

下面看看,Java实现的一个服务端和客户端通信的例子。

NIO模式的基本原理描述如下:

服务端打开一个通道(ServerSocketChannel),并向通道中注册一个选择器(Selector),这个选择器是与一些感兴趣的操作的标识(SelectionKey,即通过这个标识可以定位到具体的操作,从而进行响应的处理)相关联的,然后基于选择器(Selector)轮询通道(ServerSocketChannel)上注册的事件,并进行相应的处理。

客户端在请求与服务端通信时,也可以向服务器端一样注册(比服务端少了一个SelectionKey.OP_ACCEPT操作集合),并通过轮询来处理指定的事件,而不必阻塞。

下面的例子,主要以服务端为例,而客户端只是简单地发送请求数据和读响应数据。

服务端实现,代码如下所示:

[java]  view plain  copy
  1. package org.shirdrn.java.communications.nio;  
  2.   
  3. import java.io.IOException;  
  4. import java.net.InetSocketAddress;  
  5. import java.nio.ByteBuffer;  
  6. import java.nio.channels.SelectionKey;  
  7. import java.nio.channels.Selector;  
  8. import java.nio.channels.ServerSocketChannel;  
  9. import java.nio.channels.SocketChannel;  
  10. import java.util.Iterator;  
  11. import java.util.Set;  
  12. import java.util.logging.Logger;  
  13.   
  14. /** 
  15.  * NIO服务端 
  16.  *  
  17.  * @author shirdrn 
  18.  */  
  19. public class NioTcpServer extends Thread {  
  20.   
  21.     private static final Logger log = Logger.getLogger(NioTcpServer.class.getName());  
  22.     private InetSocketAddress inetSocketAddress;  
  23.     private Handler handler = new ServerHandler();  
  24.       
  25.     public NioTcpServer(String hostname, int port) {  
  26.         inetSocketAddress = new InetSocketAddress(hostname, port);  
  27.     }  
  28.       
  29.     @Override  
  30.     public void run() {  
  31.         try {  
  32.             Selector selector = Selector.open(); // 打开选择器  
  33.             ServerSocketChannel serverSocketChannel = ServerSocketChannel.open(); // 打开通道  
  34.             serverSocketChannel.configureBlocking(false); // 非阻塞  
  35.             serverSocketChannel.socket().bind(inetSocketAddress);  
  36.             serverSocketChannel.register(selector, SelectionKey.OP_ACCEPT); // 向通道注册选择器和对应事件标识  
  37.             log.info("Server: socket server started.");  
  38.             while(true) { // 轮询  
  39.                 int nKeys = selector.select();  
  40.                 if(nKeys>0) {  
  41.                     Set<SelectionKey> selectedKeys = selector.selectedKeys();  
  42.                     Iterator<SelectionKey> it = selectedKeys.iterator();  
  43.                     while(it.hasNext()) {  
  44.                         SelectionKey key = it.next();  
  45.                         if(key.isAcceptable()) {  
  46.                             log.info("Server: SelectionKey is acceptable.");  
  47.                             handler.handleAccept(key);  
  48.                         } else if(key.isReadable()) {  
  49.                             log.info("Server: SelectionKey is readable.");  
  50.                             handler.handleRead(key);  
  51.                         } else if(key.isWritable()) {  
  52.                             log.info("Server: SelectionKey is writable.");  
  53.                             handler.handleWrite(key);  
  54.                         }  
  55.                         it.remove();  
  56.                     }  
  57.                 }  
  58.             }  
  59.         } catch (IOException e) {  
  60.             e.printStackTrace();  
  61.         }  
  62.     }  
  63.       
  64.     /** 
  65.      * 简单处理器接口 
  66.      *  
  67.      * @author shirdrn 
  68.      */  
  69.     interface Handler {  
  70.         /** 
  71.          * 处理{@link SelectionKey#OP_ACCEPT}事件 
  72.          * @param key  
  73.          * @throws IOException 
  74.          */  
  75.         void handleAccept(SelectionKey key) throws IOException;  
  76.         /** 
  77.          * 处理{@link SelectionKey#OP_READ}事件 
  78.          * @param key  
  79.          * @throws IOException 
  80.          */  
  81.         void handleRead(SelectionKey key) throws IOException;  
  82.         /** 
  83.          * 处理{@link SelectionKey#OP_WRITE}事件 
  84.          * @param key  
  85.          * @throws IOException 
  86.          */  
  87.         void handleWrite(SelectionKey key) throws IOException;  
  88.     }  
  89.       
  90.     /** 
  91.      * 服务端事件处理实现类 
  92.      *  
  93.      * @author shirdrn 
  94.      */  
  95.     class ServerHandler implements Handler {  
  96.   
  97.         @Override  
  98.         public void handleAccept(SelectionKey key) throws IOException {  
  99.             ServerSocketChannel serverSocketChannel = (ServerSocketChannel)key.channel();  
  100.             SocketChannel socketChannel = serverSocketChannel.accept();  
  101.             log.info("Server: accept client socket " + socketChannel);  
  102.             socketChannel.configureBlocking(false);  
  103.             socketChannel.register(key.selector(), SelectionKey.OP_READ);  
  104.         }  
  105.   
  106.         @Override  
  107.         public void handleRead(SelectionKey key) throws IOException {  
  108.             ByteBuffer byteBuffer = ByteBuffer.allocate(512);  
  109.             SocketChannel socketChannel = (SocketChannel)key.channel();  
  110.             while(true) {  
  111.                 int readBytes = socketChannel.read(byteBuffer);  
  112.                 if(readBytes>0) {  
  113.                     log.info("Server: readBytes = " + readBytes);  
  114.                     log.info("Server: data = " + new String(byteBuffer.array(), 0, readBytes));  
  115.                     byteBuffer.flip();  
  116.                     socketChannel.write(byteBuffer);  
  117.                     break;  
  118.                 }  
  119.             }  
  120.             socketChannel.close();  
  121.         }  
  122.   
  123.         @Override  
  124.         public void handleWrite(SelectionKey key) throws IOException {  
  125.             ByteBuffer byteBuffer = (ByteBuffer) key.attachment();  
  126.             byteBuffer.flip();  
  127.             SocketChannel socketChannel = (SocketChannel)key.channel();  
  128.             socketChannel.write(byteBuffer);  
  129.             if(byteBuffer.hasRemaining()) {  
  130.                 key.interestOps(SelectionKey.OP_READ);  
  131.             }  
  132.             byteBuffer.compact();  
  133.         }  
  134.     }  
  135.   
  136.     public static void main(String[] args) {  
  137.         NioTcpServer server = new NioTcpServer("localhost"1000);  
  138.         server.start();  
  139.     }  
  140. }  

客户端实现,代码如下所示:

[java]  view plain  copy
  1. package org.shirdrn.java.communications.nio;  
  2.   
  3. import java.io.IOException;  
  4. import java.net.InetSocketAddress;  
  5. import java.nio.ByteBuffer;  
  6. import java.nio.channels.SocketChannel;  
  7. import java.util.logging.Logger;  
  8.   
  9. /** 
  10.  * NIO客户端 
  11.  *  
  12.  * @author shirdrn 
  13.  */  
  14. public class NioTcpClient {  
  15.   
  16.     private static final Logger log = Logger.getLogger(NioTcpClient.class.getName());  
  17.     private InetSocketAddress inetSocketAddress;  
  18.       
  19.     public NioTcpClient(String hostname, int port) {  
  20.         inetSocketAddress = new InetSocketAddress(hostname, port);  
  21.     }  
  22.       
  23.     /** 
  24.      * 发送请求数据 
  25.      * @param requestData 
  26.      */  
  27.     public void send(String requestData) {  
  28.         try {  
  29.             SocketChannel socketChannel = SocketChannel.open(inetSocketAddress);  
  30.             socketChannel.configureBlocking(false);  
  31.             ByteBuffer byteBuffer = ByteBuffer.allocate(512);  
  32.             socketChannel.write(ByteBuffer.wrap(requestData.getBytes()));  
  33.             while (true) {  
  34.                 byteBuffer.clear();  
  35.                 int readBytes = socketChannel.read(byteBuffer);  
  36.                 if (readBytes > 0) {  
  37.                     byteBuffer.flip();  
  38.                     log.info("Client: readBytes = " + readBytes);  
  39.                     log.info("Client: data = " + new String(byteBuffer.array(), 0, readBytes));  
  40.                     socketChannel.close();  
  41.                     break;  
  42.                 }  
  43.             }  
  44.   
  45.         } catch (IOException e) {  
  46.             e.printStackTrace();  
  47.         }  
  48.     }  
  49.       
  50.     public static void main(String[] args) {  
  51.         String hostname = "localhost";  
  52.         String requestData = "Actions speak louder than words!";  
  53.         int port = 1000;  
  54.         new NioTcpClient(hostname, port).send(requestData);  
  55.     }  
  56. }  

上述实现,NioTcpServer服务线程启动后,监听指定端口,等待客户端请求的到来,然后NioTcpClient客户端进程启动并发送请求数据,服务端接收到请求数据后,响应客户端(将请求的数据作为响应数据写回到客户端通道SocketChannel,并等待客户端处理)。

实际上,客户端和服务端可以采用同样轮询的非阻塞模式来实现,为简单实现在这个例子中我们把客户端角色简化了,而实际上它可能在另一个系统通信中充当服务端角色。

另外,上面对于不同事件是采用非线程的方式来处理,只是简单地调用处理的方法。在实际中,如果存在大量连接、读写请求,可以考虑使用线程池来更大程度地并发处理,提高服务端处理的速度和吞吐量,提升系统性能。

你可能感兴趣的:(java,spring,Web,socket,server)