从IO迁移到NIO - 网络,IllegalBlockingModeException

时间:2014-11-28 23:07:47

标签: java networking io nio

我正在尝试将我的网络从标准IO转移到NIO,并按照几个教程来尝试解决这个问题,而我,我自己,我决定花掉我的一个好主意。第一周重写所有应用程序逻辑处理的核心,我从来没有想到我将无法建立基本的网络。

目前网络处于一个非常基本的阶段,一切都在一个循环中抛出,我不能老实说我已经做了任何尝试,使它看起来不错,考虑到我没有线索我正在实现的目标是先弄清楚如何做到这一点,然后再回过头来进行改造。

以下是我用来初始化服务器的代码:

    // Initializes the TCP Server and all of its components.
private void initTcpServer(int port) {
    try {
        // Create a new selector
        Selector socketSelector = SelectorProvider.provider()
                .openSelector();

        // Create a new non-blocking server socket channel;
        this.serverSocketChannel = ServerSocketChannel.open();
        this.serverSocketChannel.configureBlocking(false);

        // Bind the server socket to the specified address and port
        this.serverSocketChannel.socket().bind(
                new InetSocketAddress("127.0.0.1", port));

        // Register the server socket channel, indicating an interest in
        // accepting new connections
        this.serverSocketChannel.register(socketSelector,
                SelectionKey.OP_ACCEPT);

        // Set the selector for the server instance.
        this.selector = socketSelector;

    } catch (IOException e) {
        e.printStackTrace();
    }
}

然后这个类实现Runnable接口,在这个方法完成后直接启动一个新线程,在这个线程中我们包含以下代码:

public void run() {
    while (isRunning) {
        try {
            selector.selectNow();
        } catch (IOException io) {
            return;
        }

        Iterator<SelectionKey> it = selector.selectedKeys().iterator();

        while (it.hasNext()) {
            SelectionKey key = it.next();

            if (!key.isValid()) {
                it.remove();
                continue;
            }

            try {
                if (key.isAcceptable()) {
                    this.handleConnection(key);
                } else if (key.isReadable()) {
                    Connection connection = (Connection) key.attachment();
                    if (connection != null) {
                        try {
                            connection.getMasterProtocol()
                                    .decode(connection,
                                            connection.getInputStream());
                        } catch (IOException e) {
                            e.printStackTrace();
                        }
                    }
                }
            } finally {
                it.remove();
            }
        }
    }
}

根据我的理解,这是允许我们根据SelectionKey来处理我们的连接和数据的原因..并且是所有基于NIO的网络运行的,你会看到我正在呼叫两种不同的方法使这不是一团糟,第一种是#handleConnection,另一种是解码功能。

handle连接方法创建了我的Connection类的新实例,并将其附加到SelectionKey,如下所示:

    public Connection(SelectionKey key) {
    try {
        // For an accept to be pending the channel must be a server socket channel.
        ServerSocketChannel serverSocketChannel = (ServerSocketChannel)key.channel();

        // Accept the connection and make it non-blocking.
        this.socketChannel = serverSocketChannel.accept();
        this.socketChannel.configureBlocking(false);

        // Set up other user data.
        this.inputStream = new DataInputStream(socketChannel.socket().getInputStream());
        this.masterProtocol = new MasterProtocol();

        // Register the new SocketChannel with our Selector, indicating
        // we'd like to be notified when there's data waiting to be read.
        key = this.socketChannel.register(OGServer.getInstance().getSelector(), SelectionKey.OP_READ);
        key.attach(this);

        // Add the current <SelectorKey, Connection> to the current connections collection.
        connections.put(key, this);

        Log.debug(getClass(), "Connection constructed successfully.");
    } catch(IOException e) {
        e.printStackTrace();
    }
}

当我尝试调用MasterProtocol#decode方法时,会调用该错误,如下所示:

public Object decode(Connection connection, DataInputStream dataInputStream) throws IOException {
    if(connection.getState() == ConnectionState.CONNECTED) {
        byte[] bytes = ByteStreams.toByteArray(dataInputStream);
        if(bytes.length < 4) {
            System.out.println("Not enough bytes read.");
            return null;
        }

        int bufferSize = dataInputStream.readInt();

        System.out.println("Buffer Size: " + bufferSize);

        while(bytes.length < bufferSize) {
            return null;
        }

        int test = dataInputStream.readInt();

        System.out.println("Test: " + test);

        return null;
    }
    return null;
}

DataInputStream尝试从网络读取时,似乎会调用该错误,更具体地说是在这行代码中:

byte[] bytes = ByteStreams.toByteArray(dataInputStream);

错误:

Exception in thread "Thread-0" java.nio.channels.IllegalBlockingModeException
at sun.nio.ch.SocketAdaptor$SocketInputStream.read(SocketAdaptor.java:190)
at sun.nio.ch.ChannelInputStream.read(ChannelInputStream.java:103)
at java.io.DataInputStream.read(DataInputStream.java:100)
at com.google.common.io.ByteStreams.copy(ByteStreams.java:70)
at com.google.common.io.ByteStreams.toByteArray(ByteStreams.java:115)
at net.ogserver.framework.net.protocol.MasterProtocol.decode(MasterProtocol.java:29)
at net.ogserver.framework.net.OGServer.run(OGServer.java:146)
at java.lang.Thread.run(Thread.java:745)

'IllegalBlockingModeException'异常是让我失望的原因,因为我发现的所有信息都是用于设置非阻塞服务器,但DataInputStream实现是我自己的,所以我必须在某处做错了。 NIO与IO完全不同,但学习是学习,呃?

编辑:我想我知道如何从客户端发送数据是有帮助的,它只是一个非常基本的测试应用程序来实现这一点:

        socket = new Socket("127.0.0.1", 5055);
        DataOutputStream dos = new DataOutputStream(socket.getOutputStream());
        dos.writeBoolean(false);

1 个答案:

答案 0 :(得分:0)

如果您在非阻塞中转移到NIO,则无法继续使用流。如果你想使用流,那么根本不使用NIO。我现在就停止迁移项目。