我重構了一個單執行緒服務器,使其能夠同時處理多執行緒和接受多個客戶端。為此,我ClientHandler為每個新客戶端生成一個新執行緒并將其提交給ExecutorService. 我想通過輸入新行來啟動服務器關閉System.In。
但是,我無法從內部關閉服務器(使用 Oracle 的 ExecutorService 檔案中建議的關閉方法) - 有人可以向我解釋為什么嗎?我Server是一個Runnable,我把它和ThreadPool我的單個客戶端執行緒放在一起 - 這可能是問題嗎?
PS:這是一個大學專案。我故意省略了實作的介面和請求處理方法的名稱,并重命名了類,以防止這成為將來每個懶惰學生的首選解決方案。
服務器
public class Server extends Runnable {
private final List<ClientHandler> activeHandlers = new ArrayList<>();
private int port;
private volatile boolean terminated = false;
private ExecutorService service;
@Override
public void start(int port) throws ServerException {
this.port = port;
service = Executors.newCachedThreadPool();
service.submit(this);
}
@Override
public void shutdown() throws ServerException {
System.out.println("Shutdown initiated.");
this.terminated = true;
PoolUtil.safeShutdown(service);
}
@Override
public void run() {
try (ServerSocket serverSocket = new ServerSocket(port)) {
while (!terminated) {
try {
Socket client = serverSocket.accept();
ClientHandler clientSocket = connect(client);
service.submit(clientSocket);
} catch (IOException e) {
System.err.println("ERROR: Connection to client failed.");
}
}
} catch (IOException e) {
System.err.println("ERROR: Could not create a socket on port " port);
} finally {
PoolUtil.safeShutdown(service);
}
}
@Override
public ClientHandler connect(Socket client) {
ClientHandler clientHandler = new ClientHandler(client, this);
activeHandlers.add(clientHandler);
System.out.println("Registered new ClientHandler for " client.getInetAddress().toString());
return clientHandler;
}
@Override
public void disconnect(ClientHandler clientHandler) {
activeHandlers.remove(clientHandler);
System.out.println("Client successfully disconnected.");
}
}
客戶端處理程式
ublic class ClientHandler extends Runnable {
private final Socket client;
private final DirectoryServer server;
private boolean terminated;
private final Result result = new Result();
public ClientHandler(Socket client, DirectoryServer server) {
this.client = client;
this.server = server;
terminated = false;
}
@Override
public void run() {
try (client;
ObjectOutputStream oos = new ObjectOutputStream(client.getOutputStream());
ObjectInputStream ois = new ObjectInputStream(client.getInputStream())) {
while (!terminated) {
Object message = ois.readObject();
if (message instanceof SomeRequest) {
// dostuff...
} else if (message instanceof TerminateConnection) {
TerminateConnection termination = (TerminateConnection) message;
process(termination);
} else {
System.err.println(
"ERROR: the received object of class "
message.getClass().toString()
"can not be processed."
);
}
}
} catch (IOException e) {
// FIXME: Error handling
System.err.println("ERROR concerning client " client.getInetAddress() " -> " e.getMessage());
} catch (ClassNotFoundException e) {
// FIXME: Error handling
System.err.println("ERROR: the class of the received object unknown to server --> " e.getMessage());
}
}
@Override
public void process(TerminateConnection terminateConnection) {
this.terminated = true;
server.disconnect(this);
}
}
主服務器
public class ServerMain {
public static void main(String[] args) throws ServerException, IOException {
Server server = new Server();
server.start(1337);
System.out.println("Server started. Press enter to terminate.");
System.in.read();
server.shutdown();
System.out.println("Server is shut down...");
}
}
PoolUtil.shutdown()
public static void safeShutdown(ExecutorService threadPool){
threadPool.shutdown();
try {
// Waits a minute for all tasks to terminate
if (!threadPool.awaitTermination(60, TimeUnit.SECONDS)) {
// Cancel all tasks that are still running after a minute
threadPool.shutdownNow();
// Waits another minute for all tasks to be cancelled
if (!threadPool.awaitTermination(60, TimeUnit.SECONDS)) {
System.err.println("Service did not terminate!");
}
}
} catch (InterruptedException e) {
threadPool.shutdownNow();
Thread.currentThread().interrupt();
}
}
uj5u.com熱心網友回復:
ExecutorService.shutdown()方法的 JavaDoc宣告這意味著“之前提交的任務已執行,但不會接受新任務”。但是,在所有任務完成之前不會終止。您的任務的 Runnables 執行阻塞操作,
serverSocket.accept()因此您不應期望 awaitTermination 方法在關閉后回傳,直到關閉后有足夠的請求進入以用完所有阻塞的任務。您可以嘗試使用 shutdownNow() 而不是 shutdown() 以便它嘗試立即取消/中斷所有正在運行的任務,這有??望解除它們的阻塞。
uj5u.com熱心網友回復:
感謝@njr,我意識到這serverSocket.accept()是我問題的根源。
該方法正在阻塞并等待傳入??連接。為了能夠終止它,我創建了serverSocket一個實體變數,我可以通過serverSocket.close()在我的shutdown()方法中呼叫來關閉它。
這將導致serverSocket.accept()拋出IOException- 所以我抓住它并呼叫Thread.currentThread().interrupt()以關閉正在運行的執行緒。
下面是相關代碼:
public class Server extends Runnable {
private final List<ClientHandler> activeHandlers = new ArrayList<>();
private ServerSocket newConnections;
private volatile boolean terminated = false;
private ExecutorService service;
@Override
public void start(int port) throws ServerException {
try {
this.newConnections = new ServerSocket(port);
service = Executors.newCachedThreadPool();
service.submit(this);
} catch (IOException e) {
throw new ServerException("Server can not be created at port " port);
}
}
@Override
public void shutdown() throws ServerException {
try {
this.terminated = true;
newConnections.close();
} catch (IOException e) {
throw new ServerException("Shut down failed - server socket can not be closed");
} finally {
PoolUtil.safeShutdown(service);
}
}
@Override
public void run() {
try {
while (!terminated) {
try {
Socket client = newConnections.accept();
ClientHandler clientSocket = connect(client);
service.submit(clientSocket);
} catch (IOException e) {
System.out.println("ServerSocket terminated");
Thread.currentThread().interrupt();
}
}
} finally {
PoolUtil.safeShutdown(service);
}
}
// left out irrelevant methods
}
轉載請註明出處,本文鏈接:https://www.uj5u.com/net/422757.html
標籤:
下一篇:同時執行一個異步函式
