サーバーアプリがあります。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
}
}