Java NIO非阻塞服务器示例

开发 后端
以前一直用的是“ervery thread per connection”的服务器端模式,今天试了下NIO非阻塞模式的服务器。

以前一直用的是“ervery thread per connection”的服务器端模式,今天试了下NIO非阻塞模式的服务器。 不过java不能实现I/O完成端口模型,这点很遗憾。

  1. package com.vista.Server; 
  2.  
  3. import java.io.IOException; 
  4. import java.net.InetSocketAddress; 
  5. import java.net.ServerSocket; 
  6. import java.nio.ByteBuffer; 
  7. import java.nio.channels.SelectionKey; 
  8. import java.nio.channels.Selector; 
  9. import java.nio.channels.ServerSocketChannel; 
  10. import java.nio.channels.SocketChannel; 
  11. import java.util.Iterator; 
  12. import java.util.LinkedList; 
  13. import java.util.Set; 
  14.  
  15. public class SelectorServer  
  16.     private static int DEFAULT_SERVERPORT = 6018;//默认端口 
  17.     private static int DEFAULT_BUFFERSIZE = 1024;//默认缓冲区大小为1024字节 
  18.     private ServerSocketChannel channel; 
  19.     private LinkedList<SocketChannel> clients; 
  20.     private Selector readSelector; 
  21.     private ByteBuffer buffer;//字节缓冲区 
  22.     private int port; 
  23.      
  24.     public SelectorServer(int port) throws IOException 
  25.     { 
  26.         this.port = port; 
  27.         this.clients = new LinkedList<SocketChannel>(); 
  28.         this.channel = null
  29.         this.readSelector = Selector.open();//打开选择器 
  30.         this.buffer = ByteBuffer.allocate(DEFAULT_BUFFERSIZE); 
  31.     } 
  32.      // 服务器程序在服务循环中调用sericeClients()方法为已接受的客户服务 
  33.     public void serviceClients()throws IOException 
  34.     { 
  35.         Set keys; 
  36.         Iterator it; 
  37.         SelectionKey key; 
  38.         SocketChannel client; 
  39.         // 在readSelector上调用select()方法,参数1代表如果调用select的时候 那么阻塞最多1秒钟等待可用的客户端连接 
  40.         if(readSelector.select(1) > 0
  41.         { 
  42.             keys = readSelector.selectedKeys(); // 取得代表端通道的键集合 
  43.             it = keys.iterator(); 
  44.            // 遍历,为每一个客户服务  
  45.             while(it.hasNext())  
  46.             { 
  47.                key = (SelectionKey)it.next(); 
  48.                if(key.isReadable()) 
  49.                { // 如果通道可读,那么读此通道到buffer中 
  50.                   int bytes; 
  51.                   client = (SocketChannel)key.channel();// 取得键对应的通道 
  52.                   buffer.clear(); // 清空缓冲区中的内容,设置好position,limit,准备接受数据 
  53.                   bytes = client.read(buffer); // 从通道中读数据到缓冲中,返回读取得字节数 
  54.                   if(bytes >= 0)  
  55.                   { 
  56.                      buffer.flip(); // 准备将缓冲中的数据写回到通道中 
  57.                      client.write(buffer);  // 数据写回到通道中 
  58.                   }  
  59.                   else if(bytes < 0)  
  60.                   { // 如果返回小于零的值代表读到了流的末尾 
  61.                      clients.remove(client); 
  62.                   // 通道关闭时,选择键也被取消 
  63.                      client.close(); 
  64.                   } 
  65.                } 
  66.             } 
  67.          } 
  68.     } 
  69.      
  70.     public void registerClient(SocketChannel client) throws IOException 
  71.     {// 配置和注册代表客户连接的通道对象 
  72.         client.configureBlocking(false);  // 设置此通道使用非阻塞模式     
  73.         client.register(readSelector, SelectionKey.OP_READ); // 将这个通道注册到选择器上 
  74.         clients.add(client); //保存这个通道对象 
  75.     } 
  76.     public void listen() throws IOException 
  77.     { //服务器开始监听端口,提供服务 
  78.         ServerSocket socket; 
  79.         SocketChannel client; 
  80.         channel = ServerSocketChannel.open(); // 打开通道 
  81.         socket = channel.socket();   //得到与通到相关的socket对象 
  82.         socket.bind(new InetSocketAddress(port), 10);   //将scoket榜定在制定的端口上 
  83.         //配置通到使用非阻塞模式,在非阻塞模式下,可以编写多道程序同时避免使用复杂的多线程 
  84.         channel.configureBlocking(false);     
  85.         try  
  86.         { 
  87.             while(true)  
  88.             {//     与通常的程序不同,这里使用channel.accpet()接受客户端连接请求,而不是在socket对象上调用accept(),这里在调用accept()方法时如果通道配置为非阻塞模式,那么accept()方法立即返回null,并不阻塞 
  89.                 client = channel.accept();     
  90.                 if(client != null
  91.                 { 
  92.                     registerClient(client); // 注册客户信息 
  93.                 } 
  94.                 serviceClients();  // 为以连接的客户服务 
  95.             } 
  96.         }  
  97.         finally  
  98.         { 
  99.             socket.close(); // 关闭socket,关闭socket会同时关闭与此socket关联的通道 
  100.         } 
  101.     } 
  102.     public static void main(String[] args) throws IOException  
  103.     { 
  104.         System.out.println("服务器启动"); 
  105.         SelectorServer server = new SelectorServer(SelectorServer.DEFAULT_SERVERPORT); 
  106.         server.listen(); //服务器开始监听端口,提供服务 
  107.  
  108.          
  109.     } 
  110.  

修改版本:

  1. package com.vista.Server; 
  2.  
  3. import java.io.BufferedWriter; 
  4. import java.io.FileInputStream; 
  5. import java.io.IOException; 
  6. import java.io.OutputStreamWriter; 
  7. import java.io.PrintWriter; 
  8. import java.net.InetSocketAddress; 
  9. import java.net.ServerSocket; 
  10. import java.nio.ByteBuffer; 
  11. import java.nio.CharBuffer; 
  12. import java.nio.channels.FileChannel; 
  13. import java.nio.channels.SelectionKey; 
  14. import java.nio.channels.Selector; 
  15. import java.nio.channels.ServerSocketChannel; 
  16. import java.nio.channels.SocketChannel; 
  17. import java.nio.charset.Charset; 
  18. import java.nio.charset.CharsetDecoder; 
  19. import java.util.Iterator; 
  20. import java.util.LinkedList; 
  21. import java.util.Set; 
  22.  
  23. public class SelectorServer  
  24.     private static int DEFAULT_SERVERPORT = 6018;//默认端口 
  25.     private static int DEFAULT_BUFFERSIZE = 1024;//默认缓冲区大小为1024字节 
  26.     private static String DEFAULT_CHARSET = "GB2312";//默认码集 
  27.     private static String DEFAULT_FILENAME = "bigfile.dat"
  28.     private ServerSocketChannel channel; 
  29.     private LinkedList<SocketChannel> clients; 
  30.     private Selector selector;//选择器 
  31.     private ByteBuffer buffer;//字节缓冲区 
  32.     private int port; 
  33.     private Charset charset;//字符集 
  34.     private CharsetDecoder decoder;//解码器 
  35.      
  36.      
  37.     public SelectorServer(int port) throws IOException 
  38.     { 
  39.         this.port = port; 
  40.         this.clients = new LinkedList<SocketChannel>(); 
  41.         this.channel = null
  42.         this.selector = Selector.open();//打开选择器 
  43.         this.buffer = ByteBuffer.allocate(DEFAULT_BUFFERSIZE); 
  44.         this.charset = Charset.forName(DEFAULT_CHARSET); 
  45.         this.decoder = this.charset.newDecoder(); 
  46.          
  47.     } 
  48.      
  49.      private class HandleClient  
  50.      { 
  51.          private String strGreeting = "welcome to VistaQQ"
  52.          public HandleClient() throws IOException  
  53.          { 
  54.          } 
  55.          public String readBlock()  
  56.          {//读块数据 
  57.              return this.strGreeting; 
  58.          } 
  59.          public void close()  
  60.          { 
  61.               
  62.          } 
  63.     } 
  64.  
  65.     protected void handleKey(SelectionKey key) throws IOException 
  66.     {//处理事件 
  67.           if (key.isAcceptable())  
  68.           { // 接收请求 
  69.               ServerSocketChannel server = (ServerSocketChannel) key.channel();//取出对应的服务器通道 
  70.               SocketChannel channel = server.accept(); 
  71.               channel.configureBlocking(false); 
  72.               channel.register(selector, SelectionKey.OP_READ);//客户socket通道注册读操作 
  73.           } 
  74.           else if (key.isReadable())  
  75.           { // 读信息 
  76.               SocketChannel channel = (SocketChannel) key.channel(); 
  77.               int count = channel.read(this.buffer); 
  78.               if (count > 0)  
  79.               { 
  80.                 this.buffer.flip(); 
  81.                 CharBuffer charBuffer = decoder.decode(this.buffer); 
  82.                 System.out.println("Client >>" + charBuffer.toString()); 
  83.                 SelectionKey wKey = channel.register(selector, 
  84.                     SelectionKey.OP_WRITE);//为客户sockt通道注册写操作 
  85.                 wKey.attach(new HandleClient()); 
  86.               }  
  87.               else 
  88.               {//客户已经断开 
  89.                 channel.close(); 
  90.               } 
  91.               this.buffer.clear();//清空缓冲区 
  92.          } 
  93.          else if (key.isWritable())  
  94.          { // 写事件 
  95.               SocketChannel channel = (SocketChannel) key.channel(); 
  96.               HandleClient handle = (HandleClient) key.attachment();//取出处理者 
  97.               ByteBuffer block = ByteBuffer.wrap(handle.readBlock().getBytes()); 
  98.               channel.write(block); 
  99.              // channel.socket().getInputStream().(block); 
  100. //              PrintWriter out = new PrintWriter(new BufferedWriter(new OutputStreamWriter( 
  101. //                        channel.socket().getOutputStream())), true); 
  102. //              out.write(block.toString()); 
  103.  
  104.         } 
  105.  
  106.     } 
  107.     public void listen() throws IOException 
  108.     { //服务器开始监听端口,提供服务 
  109.         ServerSocket socket; 
  110.         channel = ServerSocketChannel.open(); // 打开通道 
  111.         socket = channel.socket();   //得到与通到相关的socket对象 
  112.         socket.bind(new InetSocketAddress(port));   //将scoket榜定在制定的端口上 
  113.         //配置通到使用非阻塞模式,在非阻塞模式下,可以编写多道程序同时避免使用复杂的多线程 
  114.         channel.configureBlocking(false);     
  115.         channel.register(selector, SelectionKey.OP_ACCEPT); 
  116.         try  
  117.         { 
  118.             while(true)  
  119.             {//     与通常的程序不同,这里使用channel.accpet()接受客户端连接请求,而不是在socket对象上调用accept(),这里在调用accept()方法时如果通道配置为非阻塞模式,那么accept()方法立即返回null,并不阻塞 
  120.                 this.selector.select(); 
  121.                 Iterator iter = this.selector.selectedKeys().iterator(); 
  122.                 while(iter.hasNext()) 
  123.                 { 
  124.                     SelectionKey key = (SelectionKey)iter.next(); 
  125.                     iter.remove(); 
  126.                     this.handleKey(key); 
  127.                      
  128.                 } 
  129.             } 
  130.         }  
  131.         catch(IOException ex) 
  132.         { 
  133.             ex.printStackTrace(); 
  134.         } 
  135.     } 
  136.     public static void main(String[] args) throws IOException  
  137.     { 
  138.         System.out.println("服务器启动"); 
  139.         SelectorServer server = new SelectorServer(SelectorServer.DEFAULT_SERVERPORT); 
  140.         server.listen(); //服务器开始监听端口,提供服务 
  141.     } 
  142.  

原文链接:http://www.cnblogs.com/phinecos/archive/2008/07/17/1245428.html

【编辑推荐】

  1. 基于Java NIO的即时聊天服务器模型
  2. Java解读NIO Socket非阻塞模式
  3. 利用NIO建立Socket服务器
  4. 用Java.nio.* 进行网络编程
  5. Java NIO的wakeup剖析
责任编辑:林师授 来源: 洞庭散人的博客
相关推荐

2011-12-07 17:17:02

JavaNIO

2018-01-11 08:24:45

服务器模型详解

2023-07-31 08:55:01

Java NIO非阻塞阻塞

2011-12-07 17:05:45

JavaNIO

2011-12-08 09:37:36

JavaNIO

2011-03-11 09:51:47

Java NIO

2011-12-08 13:04:06

JavaNIO

2021-03-04 08:34:55

同步阻塞非阻塞

2010-09-26 14:21:43

sql跨服务器查询

2021-06-04 18:14:15

阻塞非阻塞tcp

2023-03-15 08:39:07

远程服务调用

2019-05-05 08:50:42

阻塞非阻塞BIO

2017-10-24 14:48:34

微服务器示例教程

2009-12-14 10:44:51

Java 7NIO2

2021-02-27 16:08:17

Java异步非阻塞

2019-07-23 11:01:57

Python同步异步

2017-03-01 16:40:12

Linux驱动技术设备阻塞

2011-12-15 11:19:08

JavaNIO

2009-08-06 14:42:54

ASP.NET服务器控

2016-11-28 09:08:43

java系统异步非阻塞
点赞
收藏

51CTO技术栈公众号