NIO服务器

 

其实这个是我自己对NIO做服务器时的一点见解,要是不太对,望赐教,这个图和数据通信的时分复用图差不多吧!

 

 

package org.com.mayi;

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(long timeout)方法,
        //timeout - 如果为正,则在等待某个通道准备就绪时最多阻塞 timeout 毫秒;
        //如果为零,则无限期地阻塞;必须为非负数 
        if(readSelector.select(1) > 0)
        {
            keys = readSelector.selectedKeys(); //返回此选择器的已选择键集
            it = keys.iterator();
           // 遍历,为每一个客户服务 
            while(it.hasNext()) 
            {
               key = (SelectionKey)it.next();
               if(key.isReadable())
               { // 测试此键的通道是否已准备好进行读取。
                  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();   //得到与通道相关的ServerSocket对象
        socket.bind(new InetSocketAddress(port), 10);//将ServerSocket绑定在制定的端口上
        channel.configureBlocking(false); //配置通道使用非阻塞模式。
        
        try 
        {
            while(true) 
            {
            //与通常的程序不同,这里使用ServerSocketChannel.accpet()接受客户端连接请求,
            //而不是在ServerSocket对象上调用accept()。
                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(); //服务器开始监听端口,提供服务
   
    }

}


基本过程:  服务器启动并初始化(new SelectorServer),服务器开始监听,serversocketchannel接收socket连接,并注册到selector,然后提供serversocket服务。

 

 

其中selector 是 SelectableChannel 对象的多路复用器。很有必要了解selector。

serversocketchannel   和socketchannel是实现使用通道传输数据的基础条件

非阻塞模式的优势:

                            通道channel要么处于阻塞 模式,要么处于非阻塞模式。

                            在阻塞模式中,每一个 I/O 操作完成之前都会阻塞在这个通道上调用的其他 I/O 操作。

                            在非阻塞模式中,永远不会阻塞 I/O 操作,并且传输的字节可能少于请求的数量,或者可能根本不传输字节。

                            新创建的通道总是处于阻塞模式。在结合使用基于选择器selector的多路复用时,非阻塞模式是最有用的。向选择器

                            注册某个通道前,必须将该通道置于非阻塞模式,并且在注销之前可能无法返回到阻塞模式。

        

 

 


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值