网络编程Socket的阻塞和非阻塞IO

  网络应用程序一个很重要的工做是传输数据。传输数据的过程不同取决于使用哪一种“交通工具“,可是传输的方式都是同样的:都是以字节码传输。JAVA开发网络程序传输数据的过程和方式是被抽象了的,咱们不须要关注底层接口,只须要使用Java API 或其余网络框架就能达到数据传输的目的。发送数据和接收数据都是字节码。编程

  Socket网络编程我就很少啰嗦了,这里我经过两个简单的示例比较下阻塞式IO(OIO)和非阻塞式IO(NIO)。服务器

  OIO中,每一个线程只能处理一个channel,该线程和该channel绑定。也就是同步的,客户端在发送请求后,必须得在服务端有回应后才发送下一个请求。因此这个时候的全部请求将会在服务端获得同步。网络

  NIO中,每一个线程能够处理多个channel。也就是异步的,客户端在发送请求后,没必要等待服务端的回应就能够发送下一个请求,这样对于全部的请求动做来讲将会在服务端获得异步,这条请求的链路就象是一个请求队列,全部的动做在这里不会获得同步的。并发

  你可能使用过Java提供的网络接口工做过,遇到过想从阻塞传输切换到非阻塞传输的状况,这种状况是比较困难的,由于阻塞IO和非阻塞IO使用的API有很大的差别。当咱们想切换传输方式时要花很大的精力和时间来重构代码。app

  先看一个传统的阻塞IO传输实现的Socket服务端:
框架

 1 /**
 2  *    传统阻塞IO(OIO),原始socket
 3  *  
 4  *   <p>Title: PlainOioServer</p>
 5  *    @author wyx
 6  *    @date 2016-6-15 下午1:36:04
 7  */
 8 public class PlainOioServer {
 9     public void server(int port) throws Exception{
10         // bind server to port 
11         final ServerSocket socket = new ServerSocket(port);
12         while(true){
13             // accept connection
14             final Socket clientSocket = socket.accept();
15             System.out.println("Accepted connection form " + clientSocket);
16             // create new thread to handle connection
17             new Thread(new Runnable() {
18                 
19                 @Override
20                 public void run() {
21                     OutputStream out;
22                     try {
23                         out = clientSocket.getOutputStream();
24                         // write  message to connected client
25                         out.write("Hi!\r\n".getBytes(Charset.forName("UTF-8")));
26                         out.flush();
27                         // close connection once message written and flushed
28                         clientSocket.close();
29                     } catch (IOException e) {
30                         e.printStackTrace();
31                     }
32                 }
33             }).start(); // start thread to begin handling
34         }
35     }
36 }

 

  上面的方式很简洁,可是这种阻塞模式在大链接的状况就会有严重的问题,如:客户端链接超时,服务器响应严重延迟等。为了解决这一问题,咱们可使用异步网络处理全部的并发链接,但问题在于NIO和OIO的API是彻底不一样的,因此一个用OIO开发的网络应用程序想要使用NIO重构代码几乎是从新开发。异步

  下面代码是使用Java NIO实现的例子:socket

 1 /**
 2  *    传统非阻塞式IO(NIO),原始socket
 3  *  
 4  *    <p>Title: PlainNioServer</p>
 5  *    @author wyx
 6  *    @date 2016-6-15 下午1:46:09
 7  */
 8 public class PlainNioServer {
 9     public void server(int port) throws Exception{
10         System.out.println("Listening for connections on port " + port);
11         // open selector that handles channels
12         Selector selector = Selector.open();
13         // open ServerSocketChannel
14         ServerSocketChannel serverChannel = ServerSocketChannel.open();
15         // get ServerSocket
16         ServerSocket serverSocket = serverChannel.socket();
17         // bind server to port     
18         serverSocket.bind(new InetSocketAddress(port));
19         // set to non-blocking
20         serverChannel.configureBlocking(false);
21         // register ServerSocket to selector and specify than it is interested in new accepted clients
22         serverChannel.register(selector, SelectionKey.OP_ACCEPT);
23         final ByteBuffer msg = ByteBuffer.wrap("Hi!\r\n".getBytes());
24         while(true){
25             // Wait for new events that are ready for process. this will block until something happens
26             int n = selector.select();
27             if(n > 0){
28                 // Obtain all SelectionKey instances that received enents
29                 Iterator<SelectionKey> iter = selector.selectedKeys().iterator();
30                 while(iter.hasNext()){
31                     SelectionKey key = iter.next();
32                     iter.remove();
33                     //Check if event was because new client ready to get accepted
34                     if(key.isAcceptable()){
35                         ServerSocketChannel server = (ServerSocketChannel) key.channel();
36                         SocketChannel client = server.accept();
37                         System.out.println("Accepted connection from " + client);
38                         client.configureBlocking(false);
39                         // Accept client and register it to seletor 
40                         client.register(selector, SelectionKey.OP_WRITE, msg.duplicate());
41                     }
42                     
43                     // Check if event was because socket is ready to write data
44                     if(key.isWritable()){
45                         SocketChannel client = (SocketChannel) key.channel();
46                         ByteBuffer buff = (ByteBuffer) key.attachment();
47                         // Write date to connected client 
48                         while(buff.hasRemaining()){
49                             if(client.write(buff) == 0){
50                                 break;
51                             }
52                         }
53                         client.close();
54                     }
55                 }
56             }
57         }
58     }
59 }

  如你所见,即便它们实现的功能时候同样的,可是代码彻底不一样。根据不一样需求选用不一样的实现方式,固然,也能够直接选择流行的网络传输框架实现,如:Netty。以便于后期维护。ide

相关文章
相关标签/搜索