1

サーバーアプリがあります。Java NIO

着信メッセージを処理する Runnable クラス (EventHandler) があります。メッセージ == "Bye" の場合 -> EventHandler 関連の SocketServer と SelectorKey を閉じる

OP_ACCEPT イベントでアクティブ化される 1 つの Runnable オブジェクト (Acceptor) があります。このチャネルからのメッセージを処理するために、新しい SocketChannel と新しい EventHandler を作成します。

私は問題があります。最初のクライアント接続。メッセージを送る。切断します。全て大丈夫

最初のクライアントが切断された後、2 番目のクライアントが接続します。ここから問題が始まります - Acceptor オブジェクトが呼び出されないため、SocketChannel と EventHandler は新しいクライアント用に作成されません。

私のコードで何が間違っていますか? SocketChannel が不適切に閉じられましたか?


コメントで指摘されたエラーを修正するためにコードを変更しました。今では正常に動作します

原子炉。メインループのあるクラス

public class Reactor implements Runnable {

    final Selector selector;
    final ServerSocketChannel serverSocketChannel;

    Reactor(int port) throws IOException {
        //configure server socket channel
        this.selector = Selector.open();
        this.serverSocketChannel = ServerSocketChannel.open();
        this.serverSocketChannel.socket().bind(new InetSocketAddress(port));
        this.serverSocketChannel.configureBlocking(false);

        //start acceptor
        this.serverSocketChannel.register(this.selector, SelectionKey.OP_ACCEPT, new Acceptor(this.serverSocketChannel, this.selector));
    }

    public void run() {
        System.out.println("Server is listening to port: " + serverSocketChannel.socket().getLocalPort());
        try {
            while (!Thread.currentThread().isInterrupted()) {
                if (this.selector.select() > 0) {
                    Set<SelectionKey> selected = this.selector.selectedKeys();
                    for (SelectionKey selectionKey : selected) {
                        dispatch(selectionKey);
                    }
                    selected.clear(); //clear set (thanks to EJP for comment)
                }
            }
        } catch (IOException ex) {
            ex.printStackTrace();
        }
    }

    void dispatch(SelectionKey k) {
        Runnable r = (Runnable) (k.attachment());
        if (r != null) {
            r.run();
        }
    }
}

アクセプター

public class Acceptor implements Runnable {

    final ServerSocketChannel serverSocketChannel;
    final Selector selector;

    public Acceptor(ServerSocketChannel serverSocketChannel, Selector selector) {
        this.serverSocketChannel = serverSocketChannel;
        this.selector = selector;
    }

    public void run() {
        try {
            SocketChannel socketChannel = this.serverSocketChannel.accept();
            if (socketChannel != null) {
                new EventHandler(this.selector, socketChannel);
                System.out.println("Connection Accepted");
            }
        } catch (IOException ex) {
            ex.printStackTrace();
        }
    }
}

EventHandler

public class EventHandler implements Runnable {

    EventHandler(Selector selector, SocketChannel socketChannel) throws IOException {
        this.socketChannel = socketChannel;
        socketChannel.configureBlocking(false);
        this.selectionKey = this.socketChannel.register(selector, SelectionKey.OP_READ, this);
        //selector.wakeup();  //we don't need to wake up selector (thanks to EJP for comment)
    }

    @Override
    public void run() {
        try {
            if (this.state == EventHandlerStatus.READING) {
                read();
            } else if (this.state == EventHandlerStatus.SENDING) {
                send();
            }
        } catch (IOException ex) {
            ex.printStackTrace();
        }
    }

    /**
     * Reading client message
     *
     * @throws IOException
     */
    void read() throws IOException {
        int readCount = this.socketChannel.read(this.input);

        //check whether the result is equal to -1, and close the connection if it is (thanks to EJP for comment)
        if(readCount == -1){
            this.socketChannel.close();
            System.out.println("Stream is closed. Close connection.");
            return;
        }

        if (readCount > 0) {
            processMessage(readCount);
        }

        if(this.clientMessage.equalsIgnoreCase("Bye")){
            this.socketChannel.close();
            //this.selectionKey.cancel(); //we don't need to cancel selectionKey if socketChannel is just closed (thanks to EJP for comment)
            System.out.println("Client said Bye. Close connection.");
            return;
        }

        this.state = EventHandler.Status.SENDING;
        this.selectionKey.interestOps(SelectionKey.OP_WRITE); //mark that we interested in writing
    }

    /**
     * Processing of the read message.
     *
     * @param readCount Number of bytes to read
     */
    synchronized void processMessage(int readCount) {
        this.input.flip();
        StringBuilder sb = new StringBuilder();
        sb.append(new String(Arrays.copyOfRange(input.array(), 0, readCount))); // Assuming ASCII (bad assumption but simplifies the example)
        this.clientMessage = sb.toString().trim();
        this.input.clear();
        System.out.println("Client said: " + this.clientMessage);
    }

    /**
     * Sending response to client
     *
     * @throws IOException
     */
    void send() throws IOException {
        System.out.println("Answer to client: " + this.clientMessage);
        this.socketChannel.write(ByteBuffer.wrap((this.clientMessage + "\n").getBytes()));
        this.state = EventHandler.Status.READING;
        this.selectionKey.interestOps(SelectionKey.OP_READ); //mark that we interested in reading
    }

//----------------------------------------------------------------------------------------------------------------------
//  Fields
//----------------------------------------------------------------------------------------------------------------------

    final SocketChannel socketChannel;
    final SelectionKey selectionKey;

    ByteBuffer input = ByteBuffer.allocate(1024);
    EventHandlerStatus state = EventHandler.Status.READING;
    String clientMessage = "";

//----------------------------------------------------------------------------------------------------------------------
//  Enum to mark current status of EventHandler
//----------------------------------------------------------------------------------------------------------------------

    enum Status {
        READING, SENDING
    }

}
4

0 に答える 0