为什么客户端无法在这个 java nio 程序中连接到服务器?
Why client can't connect to server in this java nio program?
我正在阅读 Doug Lea 的 Scalable I/O in Java,我遵循了 基本反应器设计 示例代码。但是我启动服务器后,客户端无法连接到服务器。
这是 Reactor class:
class Reactor implements Runnable {
private static final Logger logger = LogManager.getLogger();
final Selector selector;
final ServerSocketChannel serverSocket;
public Reactor(int port) throws IOException {
selector = Selector.open();
serverSocket = ServerSocketChannel.open();
serverSocket.bind(new InetSocketAddress(port));
serverSocket.configureBlocking(false);
SelectionKey sk = serverSocket.register(selector, SelectionKey.OP_ACCEPT);
sk.attach(new Acceptor());
logger.info("server started.");
}
@Override
public void run() {
while (!Thread.interrupted()) {
for (final Iterator<SelectionKey> it = selector.selectedKeys().iterator(); it.hasNext(); it.remove()) {
dispatch(it.next());
}
}
}
private void dispatch(SelectionKey key) {
Runnable r = (Runnable) key.attachment();
if (r != null) {
r.run();
}
}
private final class Acceptor implements Runnable {
@Override
public void run() {
try {
SocketChannel c = serverSocket.accept();
if (c != null) {
new Handler(selector, c);
}
} catch (IOException ex) {
ex.getMessage();
}
}
}
public static void main(String[] args) throws IOException {
new Reactor(9000).run();
}
}
处理程序class
final class Handler implements Runnable {
private static final Logger logger = LogManager.getLogger();
final SocketChannel c;
final SelectionKey key;
ByteBuffer buffer = ByteBuffer.allocate(1024);
public Handler(Selector sel, SocketChannel c) throws IOException {
this.c = c;
c.configureBlocking(false);
key = c.register(sel, SelectionKey.OP_READ | SelectionKey.OP_WRITE);
logger.info("client connected: " + c);
}
void read() throws IOException {
if (!buffer.hasRemaining()) {
return;
}
c.read(buffer);
}
void process() {/* */}
void write() throws IOException {
buffer.flip();
c.write(buffer);
c.close();
}
@Override
public void run() {
try {
read();
process();
write();
} catch (IOException ex) {
ex.getMessage();
}
}
}
我在 idea 中启动服务器,然后 server started 在控制台打印
但是当我在终端输入 telnet localhost 9000
后, client connected: 没有出现。
我不得不稍微更改 Reactor
运行 方法。您必须致电 selector.select()
或 selector.selectNow()
:
@Override
public void run() {
while (!Thread.interrupted()) {
try {
int ready = selector.selectNow();
if (ready == 0){
continue;
}
Set<SelectionKey> selected = selector.selectedKeys();
Iterator<SelectionKey> it = selected.iterator();
while (it.hasNext()) {
SelectionKey key = it.next();
if(key.isAcceptable() || key.isReadable()) {
dispatch(key);
}
}
selected.clear();
} catch (IOException e) {
e.printStackTrace();
}
}
}
允许客户端连接。
为了启用来自 Handler
的回显服务,我实现了这个:
final class Handler implements Runnable {
private static final Logger logger = LogManager.getLogger();
final SocketChannel c;
final SelectionKey key;
ByteBuffer buffer = ByteBuffer.allocate(1024);
public Handler(Selector selector, SocketChannel c) throws IOException {
this.c = c;
c.configureBlocking(false);
logger.info("client connected: " + c);
key = c.register(selector, 0);
key.attach(this);
key.interestOps(SelectionKey.OP_READ);
selector.wakeup();
}
@Override
public void run() {
try {
SocketChannel client = (SocketChannel) key.channel();
client.read(buffer);
if (new String(buffer.array()).trim().equals("close")) {
client.close();
System.out.println("close connection");
}
buffer.flip();
client.write(buffer);
buffer.clear();
} catch (IOException ex) {
ex.getMessage();
}
}
}
注册 Handler
实例进行读取,然后根据可读选择键调用此实例的 运行 方法来处理读取。
我正在阅读 Doug Lea 的 Scalable I/O in Java,我遵循了 基本反应器设计 示例代码。但是我启动服务器后,客户端无法连接到服务器。
这是 Reactor class:
class Reactor implements Runnable {
private static final Logger logger = LogManager.getLogger();
final Selector selector;
final ServerSocketChannel serverSocket;
public Reactor(int port) throws IOException {
selector = Selector.open();
serverSocket = ServerSocketChannel.open();
serverSocket.bind(new InetSocketAddress(port));
serverSocket.configureBlocking(false);
SelectionKey sk = serverSocket.register(selector, SelectionKey.OP_ACCEPT);
sk.attach(new Acceptor());
logger.info("server started.");
}
@Override
public void run() {
while (!Thread.interrupted()) {
for (final Iterator<SelectionKey> it = selector.selectedKeys().iterator(); it.hasNext(); it.remove()) {
dispatch(it.next());
}
}
}
private void dispatch(SelectionKey key) {
Runnable r = (Runnable) key.attachment();
if (r != null) {
r.run();
}
}
private final class Acceptor implements Runnable {
@Override
public void run() {
try {
SocketChannel c = serverSocket.accept();
if (c != null) {
new Handler(selector, c);
}
} catch (IOException ex) {
ex.getMessage();
}
}
}
public static void main(String[] args) throws IOException {
new Reactor(9000).run();
}
}
处理程序class
final class Handler implements Runnable {
private static final Logger logger = LogManager.getLogger();
final SocketChannel c;
final SelectionKey key;
ByteBuffer buffer = ByteBuffer.allocate(1024);
public Handler(Selector sel, SocketChannel c) throws IOException {
this.c = c;
c.configureBlocking(false);
key = c.register(sel, SelectionKey.OP_READ | SelectionKey.OP_WRITE);
logger.info("client connected: " + c);
}
void read() throws IOException {
if (!buffer.hasRemaining()) {
return;
}
c.read(buffer);
}
void process() {/* */}
void write() throws IOException {
buffer.flip();
c.write(buffer);
c.close();
}
@Override
public void run() {
try {
read();
process();
write();
} catch (IOException ex) {
ex.getMessage();
}
}
}
我在 idea 中启动服务器,然后 server started 在控制台打印
但是当我在终端输入 telnet localhost 9000
后, client connected: 没有出现。
我不得不稍微更改 Reactor
运行 方法。您必须致电 selector.select()
或 selector.selectNow()
:
@Override
public void run() {
while (!Thread.interrupted()) {
try {
int ready = selector.selectNow();
if (ready == 0){
continue;
}
Set<SelectionKey> selected = selector.selectedKeys();
Iterator<SelectionKey> it = selected.iterator();
while (it.hasNext()) {
SelectionKey key = it.next();
if(key.isAcceptable() || key.isReadable()) {
dispatch(key);
}
}
selected.clear();
} catch (IOException e) {
e.printStackTrace();
}
}
}
允许客户端连接。
为了启用来自 Handler
的回显服务,我实现了这个:
final class Handler implements Runnable {
private static final Logger logger = LogManager.getLogger();
final SocketChannel c;
final SelectionKey key;
ByteBuffer buffer = ByteBuffer.allocate(1024);
public Handler(Selector selector, SocketChannel c) throws IOException {
this.c = c;
c.configureBlocking(false);
logger.info("client connected: " + c);
key = c.register(selector, 0);
key.attach(this);
key.interestOps(SelectionKey.OP_READ);
selector.wakeup();
}
@Override
public void run() {
try {
SocketChannel client = (SocketChannel) key.channel();
client.read(buffer);
if (new String(buffer.array()).trim().equals("close")) {
client.close();
System.out.println("close connection");
}
buffer.flip();
client.write(buffer);
buffer.clear();
} catch (IOException ex) {
ex.getMessage();
}
}
}
注册 Handler
实例进行读取,然后根据可读选择键调用此实例的 运行 方法来处理读取。