我正在实现一个传输服务器程序,它从客户端(通过控制台输入)接收消息,然后将其转发到某种邮箱。
为了允许不同客户端同时接收多个消息,我首先创建了一个实现了
根据这个Stackoverflow问题,我本来期望一旦调用
我在另一个Stackoverflow问题中看到说这可能与代码块中的其他代码行消耗了
编辑:事实证明,只要在客户端上键入“quit”手动退出循环,循环就会停止,然后会输出
为了允许不同客户端同时接收多个消息,我首先创建了一个实现了
Runnable
接口的类。这些类实例中的每一个都将处理与恰好一个客户端的通信:public class ClientConnection implements Runnable {
//...
//...
@Override
public void run() {
try {
// prepare the input reader and output writer
BufferedReader reader = new BufferedReader(new InputStreamReader(clientSocket.getInputStream()));
PrintWriter writer = new PrintWriter(clientSocket.getOutputStream(), true);
Message message = new Message();
String request = "";
// read client requests
while ((request = reader.readLine()) != null) {
System.out.println("Client sent the following request: " + request);
String response;
if (request.trim().equals("quit")) {
writer.println("ok bye");
return;
}
response = message.parseRequest(request);
if (message.isCompleted()) {
messagesQueue.put(message);
message = new Message();
}
writer.println(response);
}
} catch (SocketException e) {
System.out.println("ClientConnection: SocketException while handling socket: " + e.getMessage());
} catch (IOException e) {
throw new UncheckedIOException(e);
} catch (InterruptedException e) {
System.out.println("Client Connection was interrupted!");
e.printStackTrace();
} finally {
if (clientSocket != null && !clientSocket.isClosed()) {
try {
clientSocket.close();
} catch (IOException ignored) {}
}
}
}
}
我有一个父线程,负责启动和管理所有ClientConnection可运行实例:
@Override
public void run() {
clientConnectionExecutor = (ThreadPoolExecutor) Executors.newCachedThreadPool();
while (true) {
Socket clientSocket;
try {
// wait for a Client to connect
clientSocket = serverSocket.accept();
ClientConnection clientConnection = new ClientConnection(clientSocket, messagesQueue);
clientConnectionExecutor.execute(clientConnection);
} catch (IOException e) {
// when this exception occurs, it means that we want to shut down everything
clientConnectionExecutor.shutdownNow(); // force terminate all ClientConnections
return;
}
}
}
根据这个Stackoverflow问题,我本来期望一旦调用
shutdownNow();
方法,ClientConnection.run()
方法中就会抛出InterruptedException
并输出Client Connection was interrupted!
。但实际上没有发生这种情况,因此异常捕获块似乎从未被执行到,输入读取循环仍在进行。我在另一个Stackoverflow问题中看到说这可能与代码块中的其他代码行消耗了
InterruptedException
有关,但是没有具体说明哪行代码会这样做。所以我非常感谢任何提示。编辑:事实证明,只要在客户端上键入“quit”手动退出循环,循环就会停止,然后会输出
Client Connection was interrupted!
。因此,在循环运行时异常似乎被忽略,只有在之后才得到处理。
readline()
函数会阻塞所有操作。我之前并不知道Java中存在不能被打断的阻塞函数。 所以我按照您的方法,在while循环条件中也检查了Thread.interrupted
标志。但是这种方法有一个缺陷,就是似乎需要客户端再按一次回车键才能真正退出客户端。因此,循环条件会比实际情况晚一步判断为false。我将进一步研究SocketChannel
类,看看它是否符合我的需求。 - BenjyTecreadLine()
处被阻塞了一次。所以,正如我在答案中所说的,根据我的一点研究,没有办法通过Socket
来防止这种行为。此外,我建议您在读写Socket
或SocketChannel
时要小心,因为这不是一种有效的方式。问题在于您实际上需要一个线程来处理连接。IO 操作可能比业务逻辑操作慢得多。我的建议是在单独的线程中进行读写操作。 - RoggiSocketChannel
的一般性说明。我强烈建议您使用SocketChannel
,因为它可以在阻塞和非阻塞模式下同时运行。因此,在您的代码中,SocketChannel
和ServerSocketChannel
总是可以替代旧的Socket
和ServerSocket
。此外,您还可以使用ByteBuffer
,它允许您轻松实现任何内部缓冲区的功能。如果您不熟悉ByteBuffer
,请也看一下它。 - Roggi