3

複数のクライアントを受け入れることができるセレクターベースのシステムをJavaで構築しました。OP_ACCEPTの下に登録されたServerSocketChannelがあり、着信接続をaccept()し、結果のSocketChannelをセレクターに再度登録します。これがそのビットです:

ServerSocketChannel insock = ServerSocketChannel.open();
    insock.configureBlocking(false);
    insock.socket().bind(new InetSocketAddress(6789));

    Selector sel = Selector.open();
    SelectionKey joinchannel = insock.register(sel, SelectionKey.OP_ACCEPT);

    System.out.println("Ready to accept incoming connections.");

    while (true) {
        int ready = sel.selectNow();
        if (ready == 0)
            continue;
        Set<SelectionKey> selectedKeys = sel.selectedKeys();
        Iterator<SelectionKey> keyIterator = selectedKeys.iterator();
        while (keyIterator.hasNext()) {
            SelectionKey key = keyIterator.next();
            if(key.isAcceptable()){
                SocketChannel newConnection = insock.accept();
                System.out.println("New client "+newConnection+" connected.");
                newConnection.configureBlocking(false);
                newConnection.register(sel, SelectionKey.OP_READ).attach(new DGPlayer());
            }

OP_READに新しいSocketChannelを登録すると、これは正常に機能します。isReadable()のチェックが成功し、データを読み取ります。これがそのビットです:

else if(key.isReadable()){
                ByteBuffer buf = ByteBuffer.allocate(1024);
                int trans = ((SocketChannel)key.channel()).read(buf); buf.flip();
                byte[] ba = new byte[buf.remaining()]; buf.get(ba);
                String msg = new String(ba, 0, ba.length, Charset.forName("UTF-8"));

                if(trans > 0){
                    DGPlayer client = (DGPlayer) key.attachment();
                    System.out.println(client.name+": "+msg.trim());
                }
            }
                //              else if(key.isWritable()){
//                  ByteBuffer buf = ByteBuffer.allocate(48);
//                  buf.clear();
//                  buf.put(((String)key.attachment()).getBytes());
//                  buf.flip();
//                  
//                  SocketChannel target = ((SocketChannel)key.channel());
//                  while(buf.hasRemaining()) {
//                      target.write(buf);
//                  }
//                  
//                  key.attach(null);
//              }

ただし、ソケットチャネルをOP_READ | OP_WRITEに登録すると、何も起こりません。私の知る限り、セレクターはチャネルが読み取り可能()または書き込み可能()であることを検出しません。唯一の変更は、書き込み許可のためにチャネルを登録することです。

なぜこれが起こるのか、何か考えはありますか?

編集-完成のために、ここにいくつかのクライアントコードがあります:

while (true) {
        int readyChannels = sel.select();
        if (readyChannels == 0)
            continue;

        Set<SelectionKey> selectedKeys = sel.selectedKeys();
        Iterator<SelectionKey> keyIterator = selectedKeys.iterator();

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

            if (key.isReadable()) {
                ByteBuffer buf = ByteBuffer.allocate(1024);
                int trans = ((SocketChannel)key.channel()).read(buf); buf.flip();
                byte[] ba = new byte[buf.remaining()]; buf.get(ba);
                String msg = new String(ba, 0, ba.length, Charset.forName("UTF-8"));

                if(msg.trim().length() != 0)
                    System.out.println("Response from server: "+msg.trim());
            }
            else if(key.isWritable() && messages.size() > 0){
                String message = messages.remove();
                ByteBuffer buf = ByteBuffer.allocate(48);
                buf.clear();
                buf.put(message.getBytes(Charset.forName("UTF-8")));

                buf.flip();

                int written = outsock.write(buf);
                while(buf.hasRemaining()){
                    outsock.write(buf);
                }

                System.out.println("Wrote '"+message+"' to server");
            }
            keyIterator.remove();
        }
    }

(クライアントはサーバーをOP_READ | OP_WRITEに登録します)

4

1 に答える 1

7

一般に、積極的に書き込むものがない場合は、チャンネルを登録するOP_WRITEことはお勧めできません。ほとんどのチャネルはほとんどの時間に書き込むことができるため、へのすべての呼び出しはSelector.selectブロックせずに戻り、リソースを消費します。SelectionKey.interestOpsチャネルに何かを書き込む準備ができたら書き込みフラグを追加し、完了したらフラグを削除することをお勧めします。

于 2012-06-07T22:46:28.750 に答える