Java Selector.select()不阻止

Java Selector.select()不阻止,java,selector,Java,Selector,对不起,在我发布这个问题之前,我搜索了两天。也有类似的问题,但没有一个对我有帮助 我试图创建一个简单的聊天应用程序,其中客户端使用非NIO套接字连接到使用NIO ServerSocketChannel侦听的服务器。服务器使用选择器。在第一个客户端连接之前,Selector.select方法将按预期被阻止。但在第一个客户端连接后,Selector.select不会阻塞并立即返回。这会导致while循环持续运行 抱歉,我已粘贴了整个代码,以便您可以复制粘贴并运行它。我刚刚开始学习Java,因此非常感

对不起,在我发布这个问题之前,我搜索了两天。也有类似的问题,但没有一个对我有帮助

我试图创建一个简单的聊天应用程序,其中客户端使用非NIO套接字连接到使用NIO ServerSocketChannel侦听的服务器。服务器使用选择器。在第一个客户端连接之前,Selector.select方法将按预期被阻止。但在第一个客户端连接后,Selector.select不会阻塞并立即返回。这会导致while循环持续运行

抱歉,我已粘贴了整个代码,以便您可以复制粘贴并运行它。我刚刚开始学习Java,因此非常感谢您的帮助/指点。多谢各位

注意:现在,客户端通过套接字连接发送序列化对象消息object,服务器读取它。由于连接是非阻塞的,所以序列化对象在发送到服务器之前会预先固定对象大小(以字节为单位)。这允许服务器读取下一个x字节并反序列化为消息对象。服务器代码是正在进行的工作

客户端代码-----

import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.DataOutputStream;
import java.io.ObjectOutputStream;
import java.io.ObjectInputStream;
import java.net.InetAddress;
import java.net.Socket;
import java.net.UnknownHostException;
import java.nio.ByteBuffer;

public class ChatClient {

    void go(){
        User u = new User();
        u.setName("UserA");
        try{
            u.setInet(InetAddress.getLocalHost());
        }catch (UnknownHostException ex){
            System.out.println(ex);
            return;
        }
        Message m = new Message();
        m.setType(3);
        m.setText("This is the 1st message.");
        m.setFromUser(u);
        try{
            Socket sock = new Socket (InetAddress.getLocalHost(), 5000);
            DataOutputStream dataOut = new DataOutputStream(sock.getOutputStream()); 
            ByteArrayOutputStream byteTemp = new ByteArrayOutputStream();
            ObjectOutputStream objOut = new ObjectOutputStream (byteTemp);
            objOut.writeObject(m);
            objOut.flush();
            objOut.close();

            byte[] byteMessage = byteTemp.toByteArray();
            ByteBuffer bb = ByteBuffer.allocate(4);
            bb.putInt(byteMessage.length);
            byte[] size = new byte[4];
            size = bb.array();
            System.out.println("Object size = "+byteMessage.length); //370

            ByteArrayOutputStream byteOut = new ByteArrayOutputStream();
            byteOut.write(size);
            byteOut.write(byteMessage);

            byte[] finalMessage = byteOut.toByteArray();
            dataOut.write(finalMessage,0,finalMessage.length);
            dataOut.flush();
            System.out.println("Flushed out");
        }catch (Exception ex){
            System.out.println(ex);
        }
    }

    public static void main (String args[]){
        new CopyOfChatClient().go();
    }
}
服务器代码--------

    import java.io.ByteArrayInputStream;
    import java.io.IOException;
    import java.io.ObjectInputStream;
    import java.net.InetSocketAddress;
    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.Set;
    import java.util.concurrent.locks.ReentrantLock;

    public class CopyOfChatServer {
        Object a, b;//Dummy objects for synchronization
        SocketChannel clientSock=null;
        Selector selector;
        SelectionKey key;

        void go(){
            try{
                a=new Object();//Dummy objects for synchronization
                b=new Object();//Dummy objects for synchronization
                ServerSocketChannel serverSock = ServerSocketChannel.open();
                serverSock.socket().bind(new InetSocketAddress(5000));
                //Note: ServerSocketChannel is blocking, but each new connection returned by accept() will be made non-blocking (see below)
                selector = Selector.open();
                new Thread(new SelectorThread()).start(); //Start the SelectorThread
                int i=0;
                while (true){               
                    clientSock = serverSock.accept();
                    if (clientSock!=null){                  
                        clientSock.configureBlocking(false); //The default client socket returned by accept() is blocking. Set it to non-blocking.                          
                        synchronized (b){
                            selector.wakeup();
                            synchronized (a){
                                key = clientSock.register(selector, SelectionKey.OP_READ);  //register new client Socket with selector  
                                key.attach(clientSock);
                            }//sync(a)
                        }//sync(b)              
                        i++;
                    }
                    System.out.println("Here");
                }//while(true)

            }catch (Exception ex){
                System.out.println(ex);
            }
        }

        class SelectorThread implements Runnable{
            Set <SelectionKey> selectedKeys;
            int readyChannels;
            public void run(){
                while (true){               
                        try {
                            synchronized(a){
                                System.out.println("1. Selector trying to select");
                                readyChannels = selector.select();//Note: select() is blocking ?? Does not block. Behaves like non-blocking
                                System.out.println("2. Selector has selected");
                            }//sync a

                            synchronized (b){
                                //just wait till registration is done in main thread
                            }

                            if (readyChannels == 0) continue; //Even if select() is blocking, this check is to handle suprious wake-ups
                            System.out.println("readyChannels>0");
                            selectedKeys = selector.selectedKeys();
                            Iterator<SelectionKey> keyIterator = selectedKeys.iterator();
                            while (keyIterator.hasNext()){
                                SelectionKey key = keyIterator.next();
                    keyIterator.remove();//added after the first answer to my question
                                if (key.isReadable()){
                                    System.out.println("3. Got incoming data");
                                    SocketChannel tempSock = (SocketChannel)key.attachment();
                                    ByteBuffer bb=ByteBuffer.allocate(8000);
                                    int bytesRead=tempSock.read(bb);
                                    System.out.println("4. Bytes read = "+bytesRead);
                                    if (bytesRead>4){
                                        bb.flip();
                                        bb.rewind();

                                        int size = bb.getInt();
                                        System.out.println("5. Size of object = "+size);
                                        byte[] objIn = new byte[size];
                                        for (int i=0;i<size;i++){
                                            objIn[i]=bb.get();
                                        }
                                        bb.compact();

                                        ByteArrayInputStream bIn= new ByteArrayInputStream(objIn);
                                        ObjectInputStream objStream= new ObjectInputStream(bIn);
                                        Message temp1 = (Message) objStream.readObject();
                                        System.out.println("6. Read object back");
                                        System.out.println(temp1.getFromUser().getName());
                                    }
                                }
                            }
                            selectedKeys.clear();

                        } catch (IOException e) {
                            e.printStackTrace();
                        } catch (ClassNotFoundException e) {
                            // TODO Auto-generated catch block
                            e.printStackTrace();
                        } 

                }
            }
        }

        public static void main (String args[]){
            new CopyOfChatServer().go();
        }
    }
用户类----

import java.io.Serializable;
import java.net.InetAddress;

public class User implements Serializable{
    private String name;
    private InetAddress inet;


    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    public InetAddress getInet() {
        return inet;
    }
    public void setInet(InetAddress inet) {
        this.inet = inet;
    }



}
你必须把

keyIterator.remove()
之后

选择器不会从selectedKeys本身删除任何内容


注意:您不需要将频道作为附件附加到密钥。您可以从key.channel获得它。

我没有看到其他任何东西,但这是一个非常奇怪的代码。您已经为NIO选择了序列化中最难的案例之一,但是您没有正确地完成它。例如,您假设整个对象在一次读取中到达,如果它没有到达,或者长度字没有在一次读取中到达,您将完全失去同步。另外,最好在非阻塞模式下使用带有服务器套接字通道的单个线程:这样就可以摆脱所有同步。但我怀疑你是否应该使用NIO。在java.net.True中会容易得多。正如我提到的,我的服务器代码不完整。我仍然需要调整它。我想在我弄清楚是什么导致select立即返回后,我会继续。您关于是否使用NIO的问题是完全正确的。这更多是为了学习而不是为了实际应用程序。您还忽略了ObjectOutputStream编写的流头由ObjectInputStream读取。因此,如果每次读取都要构造一个新的ObjectInputStream,那么每次写入都需要构造一个新的ObjectOutputStream:换言之,还有另一种方法可以不同步。
keyIterator.remove()
keyIterator.next()