Java NIO非阻塞服务器示例

简介:
以前一直用的是“ervery thread per connection”的服务器端模式,今天试了下NIO非阻塞模式的服务器。  不过java不能实现I/O完成端口模型,这点很遗憾
复制代码
package com.vista.Server;

import java.io.IOException;
import java.net.InetSocketAddress;
import java.net.ServerSocket;
import java.nio.ByteBuffer;
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.LinkedList;
import java.util.Set;

public class SelectorServer 
{
    private static int DEFAULT_SERVERPORT = 6018;//默认端口
    private static int DEFAULT_BUFFERSIZE = 1024;//默认缓冲区大小为1024字节
    private ServerSocketChannel channel;
    private LinkedList<SocketChannel> clients;
    private Selector readSelector;
    private ByteBuffer buffer;//字节缓冲区
    private int port;
    
    public SelectorServer(int port) throws IOException
    {
        this.port = port;
        this.clients = new LinkedList<SocketChannel>();
        this.channel = null;
        this.readSelector = Selector.open();//打开选择器
        this.buffer = ByteBuffer.allocate(DEFAULT_BUFFERSIZE);
    }
     // 服务器程序在服务循环中调用sericeClients()方法为已接受的客户服务
    public void serviceClients()throws IOException
    {
        Set keys;
        Iterator it;
        SelectionKey key;
        SocketChannel client;
        // 在readSelector上调用select()方法,参数1代表如果调用select的时候 那么阻塞最多1秒钟等待可用的客户端连接
        if(readSelector.select(1) > 0)
        {
            keys = readSelector.selectedKeys(); // 取得代表端通道的键集合
            it = keys.iterator();
           // 遍历,为每一个客户服务 
            while(it.hasNext()) 
            {
               key = (SelectionKey)it.next();
               if(key.isReadable())
               { // 如果通道可读,那么读此通道到buffer中
                  int bytes;
                  client = (SocketChannel)key.channel();// 取得键对应的通道
                  buffer.clear(); // 清空缓冲区中的内容,设置好position,limit,准备接受数据
                  bytes = client.read(buffer); // 从通道中读数据到缓冲中,返回读取得字节数
                  if(bytes >= 0) 
                  {
                     buffer.flip(); // 准备将缓冲中的数据写回到通道中
                     client.write(buffer);  // 数据写回到通道中
                  } 
                  else if(bytes < 0) 
                  { // 如果返回小于零的值代表读到了流的末尾
                     clients.remove(client);
                  // 通道关闭时,选择键也被取消
                     client.close();
                  }
               }
            }
         }
    }
    
    public void registerClient(SocketChannel client) throws IOException
    {// 配置和注册代表客户连接的通道对象
        client.configureBlocking(false);  // 设置此通道使用非阻塞模式    
        client.register(readSelector, SelectionKey.OP_READ); // 将这个通道注册到选择器上
        clients.add(client); //保存这个通道对象
    }
    public void listen() throws IOException
    { //服务器开始监听端口,提供服务
        ServerSocket socket;
        SocketChannel client;
        channel = ServerSocketChannel.open(); // 打开通道
        socket = channel.socket();   //得到与通到相关的socket对象
        socket.bind(new InetSocketAddress(port), 10);   //将scoket榜定在制定的端口上
        //配置通到使用非阻塞模式,在非阻塞模式下,可以编写多道程序同时避免使用复杂的多线程
        channel.configureBlocking(false);    
        try 
        {
            while(true) 
            {//     与通常的程序不同,这里使用channel.accpet()接受客户端连接请求,而不是在socket对象上调用accept(),这里在调用accept()方法时如果通道配置为非阻塞模式,那么accept()方法立即返回null,并不阻塞
                client = channel.accept();    
                if(client != null)
                {
                    registerClient(client); // 注册客户信息
                }
                serviceClients();  // 为以连接的客户服务
            }
        } 
        finally 
        {
            socket.close(); // 关闭socket,关闭socket会同时关闭与此socket关联的通道
        }
    }
    public static void main(String[] args) throws IOException 
    {
        System.out.println("服务器启动");
        SelectorServer server = new SelectorServer(SelectorServer.DEFAULT_SERVERPORT);
        server.listen(); //服务器开始监听端口,提供服务

        
    }

}

复制代码
 

修改版本:

 

复制代码
package com.vista.Server;

import java.io.BufferedWriter;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.OutputStreamWriter;
import java.io.PrintWriter;
import java.net.InetSocketAddress;
import java.net.ServerSocket;
import java.nio.ByteBuffer;
import java.nio.CharBuffer;
import java.nio.channels.FileChannel;
import java.nio.channels.SelectionKey;
import java.nio.channels.Selector;
import java.nio.channels.ServerSocketChannel;
import java.nio.channels.SocketChannel;
import java.nio.charset.Charset;
import java.nio.charset.CharsetDecoder;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.Set;

public class SelectorServer 
{
    private static int DEFAULT_SERVERPORT = 6018;//默认端口
    private static int DEFAULT_BUFFERSIZE = 1024;//默认缓冲区大小为1024字节
    private static String DEFAULT_CHARSET = "GB2312";//默认码集
    private static String DEFAULT_FILENAME = "bigfile.dat";
    private ServerSocketChannel channel;
    private LinkedList<SocketChannel> clients;
    private Selector selector;//选择器
    private ByteBuffer buffer;//字节缓冲区
    private int port;
    private Charset charset;//字符集
    private CharsetDecoder decoder;//解码器
    
    
    public SelectorServer(int port) throws IOException
    {
        this.port = port;
        this.clients = new LinkedList<SocketChannel>();
        this.channel = null;
        this.selector = Selector.open();//打开选择器
        this.buffer = ByteBuffer.allocate(DEFAULT_BUFFERSIZE);
        this.charset = Charset.forName(DEFAULT_CHARSET);
        this.decoder = this.charset.newDecoder();
        
    }
    
     private class HandleClient 
     {
         private String strGreeting = "welcome to VistaQQ";
         public HandleClient() throws IOException 
         {
         }
         public String readBlock() 
         {//读块数据
             return this.strGreeting;
         }
         public void close() 
         {
             
         }
    }

    protected void handleKey(SelectionKey key) throws IOException
    {//处理事件
          if (key.isAcceptable()) 
          { // 接收请求
              ServerSocketChannel server = (ServerSocketChannel) key.channel();//取出对应的服务器通道
              SocketChannel channel = server.accept();
              channel.configureBlocking(false);
              channel.register(selector, SelectionKey.OP_READ);//客户socket通道注册读操作
          }
          else if (key.isReadable()) 
          { // 读信息
              SocketChannel channel = (SocketChannel) key.channel();
              int count = channel.read(this.buffer);
              if (count > 0) 
              {
                this.buffer.flip();
                CharBuffer charBuffer = decoder.decode(this.buffer);
                System.out.println("Client >>" + charBuffer.toString());
                SelectionKey wKey = channel.register(selector,
                    SelectionKey.OP_WRITE);//为客户sockt通道注册写操作
                wKey.attach(new HandleClient());
              } 
              else
              {//客户已经断开
                channel.close();
              }
              this.buffer.clear();//清空缓冲区
         }
         else if (key.isWritable()) 
         { // 写事件
              SocketChannel channel = (SocketChannel) key.channel();
              HandleClient handle = (HandleClient) key.attachment();//取出处理者
              ByteBuffer block = ByteBuffer.wrap(handle.readBlock().getBytes());
              channel.write(block);
             // channel.socket().getInputStream().(block);
//              PrintWriter out = new PrintWriter(new BufferedWriter(new OutputStreamWriter(
//                        channel.socket().getOutputStream())), true);
//              out.write(block.toString());

        }

    }
    public void listen() throws IOException
    { //服务器开始监听端口,提供服务
        ServerSocket socket;
        channel = ServerSocketChannel.open(); // 打开通道
        socket = channel.socket();   //得到与通到相关的socket对象
        socket.bind(new InetSocketAddress(port));   //将scoket榜定在制定的端口上
        //配置通到使用非阻塞模式,在非阻塞模式下,可以编写多道程序同时避免使用复杂的多线程
        channel.configureBlocking(false);    
        channel.register(selector, SelectionKey.OP_ACCEPT);
        try 
        {
            while(true) 
            {//     与通常的程序不同,这里使用channel.accpet()接受客户端连接请求,而不是在socket对象上调用accept(),这里在调用accept()方法时如果通道配置为非阻塞模式,那么accept()方法立即返回null,并不阻塞
                this.selector.select();
                Iterator iter = this.selector.selectedKeys().iterator();
                while(iter.hasNext())
                {
                    SelectionKey key = (SelectionKey)iter.next();
                    iter.remove();
                    this.handleKey(key);
                    
                }
            }
        } 
        catch(IOException ex)
        {
            ex.printStackTrace();
        }
    }
    public static void main(String[] args) throws IOException 
    {
        System.out.println("服务器启动");
        SelectorServer server = new SelectorServer(SelectorServer.DEFAULT_SERVERPORT);
        server.listen(); //服务器开始监听端口,提供服务
    }

}

复制代码




本文转自Phinecos(洞庭散人)博客园博客,原文链接:http://www.cnblogs.com/phinecos/archive/2008/07/17/1245428.html,如需转载请自行联系原作者
目录
相关文章
|
9天前
|
安全 Java 调度
Java编程时多线程操作单核服务器可以不加锁吗?
Java编程时多线程操作单核服务器可以不加锁吗?
26 2
|
7天前
|
Java Linux
java读取linux服务器下某文档的内容
java读取linux服务器下某文档的内容
20 3
java读取linux服务器下某文档的内容
|
9天前
|
Java
java小工具util系列5:java文件相关操作工具,包括读取服务器路径下文件,删除文件及子文件,删除文件夹等方法
java小工具util系列5:java文件相关操作工具,包括读取服务器路径下文件,删除文件及子文件,删除文件夹等方法
22 4
|
12天前
|
JavaScript 前端开发 Java
Java 8 新特性详解及应用示例
Java 8 新特性详解及应用示例
|
18天前
|
Kubernetes Java Maven
揭秘无服务器革命:Quarkus如何让Java应用在云端“零”负担起飞?
本文介绍如何使用Quarkus从零开始开发无服务器应用,通过示例代码和详细步骤引导读者掌握这一技术。无服务器架构让开发者无需管理服务器,具有自动扩展和成本效益等优势。Quarkus作为Kubernetes Native Java框架,优化了Java应用的启动速度和内存使用,适合无服务器环境。文章涵盖环境搭建、项目创建及部署全流程,并介绍了Quarkus的扩展性和监控工具,助力高效开发与应用性能提升。
25 9
|
1月前
|
弹性计算 运维 监控
阿里云ECS实例使用示例
使用阿里云ECS实例的流程简便快捷:登录阿里云控制台,在ECS实例列表中选择目标实例并进入详情页,点击“启动”按钮激活实例,确保预装系统和应用完成。运行后,通过控制台监控CPU、内存及磁盘使用情况,如需调整配置,可选择实例后点击“重启”应用新设置。阿里云ECS助力企业轻松上云、高效运维。[1][3] 相关链接:阿里云ECS使用流程 https://startup.aliyun.com/info/1078898.html
|
2月前
|
缓存 安全 Java
Java服务器端技术:Servlet与JSP的集成与扩展
Java服务器端技术:Servlet与JSP的集成与扩展
23 3
|
28天前
|
Cloud Native Java 编译器
将基于x86架构平台的应用迁移到阿里云倚天实例云服务器参考
随着云计算技术的不断发展,云服务商们不断推出高性能、高可用的云服务器实例,以满足企业日益增长的计算需求。阿里云推出的倚天实例,凭借其基于ARM架构的倚天710处理器,提供了卓越的计算能力和能效比,特别适用于云原生、高性能计算等场景。然而,有的用户需要将传统基于x86平台的应用迁移到倚天实例上,本文将介绍如何将基于x86架构平台的应用迁移到阿里云倚天实例的服务器上,帮助开发者和企业用户顺利完成迁移工作,享受更高效、更经济的云服务。
将基于x86架构平台的应用迁移到阿里云倚天实例云服务器参考
|
26天前
|
编解码 前端开发 安全
通过阿里云的活动购买云服务器时如何选择实例、带宽、云盘
在我们选购阿里云服务器的过程中,不管是新用户还是老用户通常都是通过阿里云的活动去买了,一是价格更加实惠,二是活动中的云服务器配置比较丰富,足可以满足大部分用户的需求,但是面对琳琅满目的云服务器实例、带宽和云盘选项,如何选择更适合自己,成为许多用户比较关注的问题。本文将介绍如何在阿里云的活动中选择合适的云服务器实例、带宽和云盘,以供参考和选择。
通过阿里云的活动购买云服务器时如何选择实例、带宽、云盘
|
24天前
|
弹性计算 运维 安全
阿里云轻量应用服务器和经济型e实例区别及选择参考
目前在阿里云的活动中,轻量应用服务器2核2G3M带宽价格为82元1年,2核2G3M带宽的经济型e实例云服务器价格99元1年,对于云服务器配置和性能要求不是很高的阿里云用户来说,这两款服务器配置和价格都差不多,阿里云轻量应用服务器和ECS云服务器让用户二选一,很多用户不清楚如何选择,本文来说说轻量应用服务器和经济型e实例的区别及选择参考。
阿里云轻量应用服务器和经济型e实例区别及选择参考
下一篇
无影云桌面