我想问一下Selector 的select()和selectedKey()方法,有什么区别我好象感觉这两个方法其实就是一样的啊。。
还有就是,SelectionKey.OP_ATTEPT,SelectionKey.OP_CONNECT,SelectionKey.OP_READ,SelectionKey.OP_WRITE这4个来表示事件。。
SelectionKey.OP_ATTEPT,SelectionKey.OP_CONNECT都好理解。。
后面两个(SelectionKey.OP_READ,SelectionKey.OP_WRITE)就不怎么好理解了:书上书,OP_READ 表示通道中已经存有了可读数据。。那么就执行操作。。通道应该指的是SocketChannel里面吧最难理解的是SelectionKey.OP_WRITE .这是说表示可以向通道里面写数据了。。我要问的是:在什么情况下表示可以象通道里写数据。。而且一般都是先if(SelectionKey.isWritable())为真的时候才写数据我就搞不懂了。。一个程序他是在什么情况下知道要写数据呢??是先写数据他才知道写数据?还是先知道写数据才写数据?下面是个程序import java.io.*;
import java.nio.*;
import java.nio.channels.*;
import java.nio.charset.*;
import java.net.*;
import java.util.*;
class EchoClient{
  private SocketChannel socketChannel = null;
  private ByteBuffer sendBuffer=ByteBuffer.allocate(1024);
  private ByteBuffer receiveBuffer=ByteBuffer.allocate(1024);
  private Charset charset=Charset.forName("GBK");
  private Selector selector;
  public EchoClient()throws IOException{
    socketChannel = SocketChannel.open();
    InetAddress ia = InetAddress.getLocalHost();
    InetSocketAddress isa = new InetSocketAddress(ia,8000);
    socketChannel.connect(isa);
    socketChannel.configureBlocking(false);
    System.out.println("与服务器的连接建立成功");
    selector=Selector.open();
  }
  public static void main(String args[])throws IOException{
    final EchoClient client=new EchoClient();
    Thread receiver=new Thread(){
      public void run(){
        client.receiveFromUser();
      }
    };    receiver.start();
    client.talk();
  }  public void receiveFromUser(){
    try{
      BufferedReader localReader=new BufferedReader(new InputStreamReader(System.in));
      String msg=null;
      while((msg=localReader.readLine())!=null){
        synchronized(sendBuffer){
            sendBuffer.put(encode(msg + "\r\n"));
         }
        if(msg.equals("bye"))
          break;
      }
    }catch(IOException e){
       e.printStackTrace();
    }
  }  public void talk()throws IOException {
     socketChannel.register(selector,
                          SelectionKey.OP_READ |
                          SelectionKey.OP_WRITE); 
   //System.out.println("selector.select()1 "+selector.select()); //为什么这里为 1.如果注释掉这句,那么下面一句打出的结果是0,如果执行这句,则下面打错的就是1下面一句指的是selector.selectedKeys().size()....    select()到底起到什么作用啊?
     System.out.println("selector.selectedKeys()1 "+selector.selectedKeys().size());
   //  System.out.println("selector.selectedKeys()2 "+selector.selectedKeys().size());
    // System.out.println("selector.select()2 "+selector.select());//同一个方法为什么这里为 0 呢?
     while (selector.select() > 0 ){
     
     
       Set readyKeys = selector.selectedKeys();
       Iterator it = readyKeys.iterator();
       while (it.hasNext()){
         SelectionKey key=null;
         try{
             key = (SelectionKey) it.next();
             it.remove();             if (key.isReadable()) {
              System.out.println("可以读了");
                 receive(key);
             }
             if (key.isWritable()) {
              //System.out.println("可以写了"); 客户端连接上之后无限打印.为什么啊?                 send(key);
             }
         }catch(IOException e){
            e.printStackTrace();
            try{
                if(key!=null){
                    key.cancel();
                    key.channel().close();
                }
            }catch(Exception ex){e.printStackTrace();}
         }
      }//#while
    }//#while
  }  public void send(SelectionKey key)throws IOException{
    SocketChannel socketChannel=(SocketChannel)key.channel();
    synchronized(sendBuffer){
        sendBuffer.flip(); //把极限设为位置
        socketChannel.write(sendBuffer);
        sendBuffer.compact();
     }
  }
  public void receive(SelectionKey key)throws IOException{
    SocketChannel socketChannel=(SocketChannel)key.channel();
    socketChannel.read(receiveBuffer);
    receiveBuffer.flip();
    String receiveData=decode(receiveBuffer);    if(receiveData.indexOf("\n")==-1)return;    String outputData=receiveData.substring(0,receiveData.indexOf("\n")+1);
    System.out.print(outputData);
    if(outputData.equals("echo:bye\r\n")){
        key.cancel();
        socketChannel.close();
        System.out.println("关闭与服务器的连接");
        selector.close();
        System.exit(0);
    }    ByteBuffer temp=encode(outputData);
    receiveBuffer.position(temp.limit());
    receiveBuffer.compact();
  }  public String decode(ByteBuffer buffer){  //解码
    CharBuffer charBuffer= charset.decode(buffer);
    return charBuffer.toString();
  }
  public ByteBuffer encode(String str){  //编码
    return charset.encode(str);
  }
}代码里面有些问题,大家也解释一下  。由于篇幅问题服务器程序没写出来

解决方案 »

  1.   

    服务器端的程序
    import java.io.*;
    import java.nio.*;
    import java.nio.channels.*;
    import java.nio.charset.*;
    import java.net.*;
    import java.util.*;public class EchoServer{
      private Selector selector = null;
      private ServerSocketChannel serverSocketChannel = null;
      private int port = 8000;
      private Charset charset=Charset.forName("GBK");  public EchoServer()throws IOException{
        selector = Selector.open();
        serverSocketChannel= ServerSocketChannel.open();
        serverSocketChannel.socket().setReuseAddress(true);
        serverSocketChannel.configureBlocking(false);
        serverSocketChannel.socket().bind(new InetSocketAddress(port));
        System.out.println("服务器启动");
      }  public void service() throws IOException{
        serverSocketChannel.register(selector, SelectionKey.OP_ACCEPT );
        while (selector.select() > 0 ){
          Set readyKeys = selector.selectedKeys();
          Iterator it = readyKeys.iterator();
          while (it.hasNext()){
             SelectionKey key=null;
             try{
                key = (SelectionKey) it.next();
                it.remove();            if (key.isAcceptable()) {
                  ServerSocketChannel ssc = (ServerSocketChannel) key.channel();
                  SocketChannel socketChannel = (SocketChannel) ssc.accept();
                  System.out.println("接收到客户连接,来自:" +
                                     socketChannel.socket().getInetAddress() +
                                     ":" + socketChannel.socket().getPort());
                  socketChannel.configureBlocking(false);
                  ByteBuffer buffer = ByteBuffer.allocate(1024);
                  socketChannel.register(selector,
                                         SelectionKey.OP_READ |
                                         SelectionKey.OP_WRITE, buffer);
                }
                if (key.isReadable()) {
                // System.out.println("可以读数据");客户端关闭无限打印为什么啊?
                    receive(key);
                }
                if (key.isWritable()) {
                // System.out.println("可以写数据");客户端连接上之后无限打印.为什么啊?
                    send(key);
                }
            }catch(IOException e){
               e.printStackTrace();
               try{
                   if(key!=null){
                       key.cancel();
                       key.channel().close();
                   }
               }catch(Exception ex){e.printStackTrace();}
            }
          }//#while
        }//#while
      }  public void send(SelectionKey key)throws IOException{
        ByteBuffer buffer=(ByteBuffer)key.attachment();
        SocketChannel socketChannel=(SocketChannel)key.channel();
        buffer.flip();  //把极限设为位置,把位置设为0
        String data=decode(buffer);
        if(data.indexOf("\r\n")==-1)return;
        String outputData=data.substring(0,data.indexOf("\n")+1);
        System.out.print(outputData);
        ByteBuffer outputBuffer=encode("echo:"+outputData);
        while(outputBuffer.hasRemaining())
          socketChannel.write(outputBuffer);    ByteBuffer temp=encode(outputData);
        buffer.position(temp.limit());
        buffer.compact();    if(outputData.equals("bye\r\n")){
          key.cancel();
          socketChannel.close();
          System.out.println("关闭与客户的连接");
        }
      }  public void receive(SelectionKey key)throws IOException{
        ByteBuffer buffer=(ByteBuffer)key.attachment();    SocketChannel socketChannel=(SocketChannel)key.channel();
        ByteBuffer readBuff= ByteBuffer.allocate(32);
        socketChannel.read(readBuff);
        readBuff.flip();    buffer.limit(buffer.capacity());
        buffer.put(readBuff);
      }  public String decode(ByteBuffer buffer){  //解码
        CharBuffer charBuffer= charset.decode(buffer);
        return charBuffer.toString();
      }
      public ByteBuffer encode(String str){  //编码
        return charset.encode(str);
      }  public static void main(String args[])throws Exception{
        EchoServer server = new EchoServer();
        server.service();
      }
    }
      

  2.   

    1.select()方法的作用是选择一组键,其相应的通道已为 I/O 操作准备就绪,也就是客户端的已经是可以读写了,可以进行操作了.selectionKeys()方法就是返回这些已经准备就绪的键,而每个键里面保存的就是对客户端I/0操作的包装,必须要先调用SELECT方法后才能通过SELECTIONKEYS得到这些键进行I/O读写操作.
    2.第二个问题你就必须要了解AIO和NIO的不同了,如果你用AIO来实现一个SOCKET SERVER 必须要用线程的方式来实现并且会需要很多线程,而采用了NIO技术之后在通道里面实现了选择器技术,而选择器就是用来模拟线程实现的,在一个系统里面也许客户端这时真是要向服务器发起写入数据,也可能要读取数据,也可能才连接上服务器,服务器怎么来区别了,就使用这些常量来判断当前键的状态来进行操作