문제
안녕하세요 저는 간단한 Java Nio 서버를 구현하려고합니다. Socketchannel을 선택기에 등록합니다. 따라서 고객의 말을 듣고 응답을 돌려 보내고 싶습니다. Socketchannel이 선택기에 등록 된 후 클라이언트 (NIO)가 일부 데이터를 보내더라도 서버는 읽을 수 없습니다. Howerver 생성 된 키가 여전히 반복되고 있습니다.
자세한보기 : 서버 측 :
**First thread**:
public void run () {while (true) {
ServerSocketChannel serverSocketChannel = ServerSocketChannel.open();
serverSocketChannel.configureBlocking(true);
serverSocketChannel.socket().bind(inetAdressOfServer);
SocketChannel clientChannel = serverSocketChannel.accept();
new Listener("").addSocketChannel(clientChannel);
}}
**Second Thread**:
static Selector selector = Selector.open();
public boolean addSocketChannel(SocketChannel clientChannel) {
SelectionKey key = clientSocketChannel.register(selector, selector.OP_READ|SelectionKey.OP_WRITE);
key.attach(new ChannelCallback(clientSocketChannel));
return key.isValid();
}
public void run() {
Set keysSet = selector.keys();
Iterator i = keysSet.iterator();
while (i.hasNext()) {
SelectionKey key = (SelectionKey) i.next();
}
if (key.isReadable()) {
//read and do something
}
}
Client Side:
Socket socket = new Socket(serverIP, serverPort);
DataOutputStream dos = new DataOutputStream(socket.getOutputStream());
dos.writeBytes(str + "\n");
NB : 단일 스레드에서 완료되면 동일한 프로그램이 작동하지만 위에서 언급 한 방식으로 구현하면 클라이언트의 말을 듣지 않습니다. 이 문제를 해결하도록 도와주세요.
해결책
당신이 그곳에서 한 일을 보는 것은 매우 어렵지만 "두 번째 스레드"로 표시 한 내용은 두 스레드에서 사용되는 것처럼 보입니다 (구현에 대한 일부 혼란 Runnable
/확장 Thread
그리고 실제 실?). 특히, 나는 추측하고있다 new Listener
스레드를 구성하고 시작합니다. 그런 다음 전화하고 있습니다 addSocketChannel
첫 번째 스레드에서. 따라서 인종 조건이 있습니다.
또한 만드는 것은 나쁜 생각입니다 selector
공전.
다른 팁
다른 스레드에서 읽는 작품을 읽으십시오. 코드의 명백한 문제는 다음과 같습니다.
public void run() {
Set keysSet = selector.keys();
여기에서 반복자에서 키 세트를 가져 오지만 선택기에서 select () 또는 selectNow ()를 수행하는 코드는 없으므로이 세트는 항상 비어 있습니다.
Iterator i = keysSet.iterator();
while (i.hasNext()) {
SelectionKey key = (SelectionKey) i.next();
}
if (key.isReadable()) {
//read and do something
}
}
이렇게하면 컴파일되지 않으며 키에서 '읽기'확인은 while 블록 내부에서 수행되어야합니다.
SelectionKey key = clientSocketChannel.register(selector,
SelectionKey.OP_READ |
SelectionKey.OP_WRITE);
두 가지 문제 :이 작업을 수행하기 전에 채널을 차단 모드로 설정해야하며 SelectKey.op_write가 선택을 실행할 때마다 키를 반환하려고하지 않으면 설정되지 않아야합니다.
실제로 쓰기를 계획하는 경우 selectionkey.op_write 만 설정해야합니다.
마지막으로, 여기에 두 개의 스레드를 사용하는 것은 매우 독창적입니다. 이를 수행하는 권장 방법은 serversocketchanchann을 OP_ACCEPT를 사용하여 선택기에 등록하고 읽기/쓰기와 동일한 스레드의 서버 소켓에서 수락을 실행하는 것입니다.