MulServer - 客户端通信:关闭后,连接的客户端仍可以进行交互[为什么?]

时间:2016-09-30 19:02:56

标签: java multithreading sockets server executorservice

更新:非常感谢Antoniossss和Peter Lawrey!

我创建了一个多线程服务器 - 客户端通信。

我有3个类:Server,Client,RequestHandler。

服务器打开ServerSocket,然后通过accept()开始侦听客户端,如果客户端连接,则将客户端的任务(某些String)引用到RequestHandler。

对我来说重要的命令是“关闭”。 如果RequestHandler找到此命令,则调用服务器中的方法来关闭。

此方法基于Executor Service的用法示例: https://docs.oracle.com/javase/8/docs/api/java/util/concurrent/ExecutorService.html(如果您不想点击该链接,请参阅该方法的 FAT 文字)

您不必阅读下面提供的代码,但如果有人对此感兴趣,我会提供

使用方法示例:

 void shutdownAndAwaitTermination(ExecutorService pool) {
   pool.shutdown(); // Disable new tasks from being submitted
   try {
     // Wait a while for existing tasks to terminate
     if (!pool.awaitTermination(60, TimeUnit.SECONDS)) {
       pool.shutdownNow(); // Cancel currently executing tasks
       // Wait a while for tasks to respond to being cancelled
       if (!pool.awaitTermination(60, TimeUnit.SECONDS))
           System.err.println("Pool did not terminate");
     }
   } catch (InterruptedException ie) {
     // (Re-)Cancel if current thread also interrupted
     pool.shutdownNow();
     // Preserve interrupt status
     Thread.currentThread().interrupt();
   }
 }
public class MulServer_v1 {

protected static int portNumber = 8540;
protected static int max_Clients = 3;
protected static boolean shutdownFlag = false;
private static ServerSocket serverSocket;
protected ExecutorService executor;
protected static ArrayList<Socket> socketList = new ArrayList<>();

public MulServer_v1(int portNumber, int poolSize) {
}

public void runServer() {

    try {
        serverSocket = new ServerSocket(portNumber);
        executor = Executors.newFixedThreadPool(max_Clients);
    } catch (IOException e) {
        System.out.println("Could not create server on specific port");
        e.printStackTrace();
    }

    while (!shutdownFlag) {
        try {
            Socket clientSocket = serverSocket.accept();
            socketList.add(clientSocket);
            executor.submit(new RequestHandler_v1(clientSocket));

        } catch (IOException e) {
            System.out.println("Couldn't accept on the Socket");
            executor.shutdown();
            e.printStackTrace();

        }

    }
    shutdownAndAwaitTermination();
}

public void shutdownAndAwaitTermination() {
    System.out.println("Shutting down..");
    executor.shutdown(); // Disable new tasks from being submitted
    try {
        // Wait a while for existing tasks to terminate
        if (!executor.awaitTermination(10, TimeUnit.SECONDS)) {
            executor.shutdownNow();
            // Cancel currently executing tasks
            System.out.println("komme ich hierhin?");

            // Wait a while for tasks to respond to being cancelled
            if (!executor.awaitTermination(10, TimeUnit.SECONDS))
                System.err.println("Pool did not terminate");
        }
    } catch (InterruptedException ie) {
        // (Re-)Cancel if current thread also interrupted
        executor.shutdownNow();
        // Preserve interrupt status
        Thread.currentThread().interrupt();
    }
    try {
        serverSocket.close();
    } catch (IOException e) {
        System.out.println("Serversocket konnte nicht geschlossen werden");
        e.printStackTrace();
    }
    System.out.println("I got here!");
    for (Socket s : socketList) {
        if (s != null) {
            try {
                s.close();
            } catch (IOException e) {
                System.out.println("Couldn't close the socket");
                e.printStackTrace();
            }
        }
    }
}

public static void main(String[] args) {
    MulServer_v1 server = new MulServer_v1(portNumber, max_Clients);
    server.runServer();
}

}
 public class Client_v1 {

    public static final String HOSTNAME = "localhost";
    public static final int PORTNUMBER = 8540;
    private static boolean clientClose = false;

    public static void main(String[] args) throws IOException {
        System.out.println("Client started");

        try (Socket socket = new Socket(HOSTNAME, PORTNUMBER);

                PrintWriter out = new PrintWriter(socket.getOutputStream(),
                        true);
                // InputStream test = echoSocket.getInputStream();
                BufferedReader in = new BufferedReader(new InputStreamReader(
                        socket.getInputStream()));
                BufferedReader stdIn = new BufferedReader(
                        new InputStreamReader(System.in))) {
            String userInput;

            while ((userInput = stdIn.readLine()) != null && !clientClose) {
                out.println(userInput);
                System.out.println("echo: " + in.readLine());
                // if (userInput.equals("BYE")) {
                // break;
                // }

            }
        } catch (UnknownHostException e) {
            System.err.println("Don't know about host " + HOSTNAME);
            System.exit(1);
        } catch (IOException e) {
            System.err.println("Couldn't get I/O for the connection to "
                    + HOSTNAME);
            System.exit(1);
        }

    }

    protected static void closeClient() {
        clientClose = true;
    }
}
public class RequestHandler_v1 implements Runnable {
    // private final String password = "passwort";
    private final Socket client;
    private boolean closeFlag = false;

    public RequestHandler_v1(Socket client) {
        this.client = client;
}

@Override
public void run() {
    try (BufferedReader in = new BufferedReader(new InputStreamReader(
            client.getInputStream()));
            BufferedWriter writer = new BufferedWriter(
                    new OutputStreamWriter(client.getOutputStream()));) {
        System.out.println("Thread started with name:"
                + Thread.currentThread().getName());
        String userInput;
        String serverResponse;

        while ((userInput = in.readLine()) != null) {
            serverResponse = processInput(userInput);
            System.out.println("Received message from "
                    + Thread.currentThread().getName() + " : " + userInput);
            writer.write("Sever Response : " + serverResponse);
            writer.newLine();
            writer.flush();
            if (closeFlag) {
                Client_v1.closeClient();
                MulServer_v1.socketList.remove(client);
                client.close();

            }
        }
    } catch (IOException e) {
        System.out.println("I/O exception: " + e);
    } catch (Exception ex) {


System.out.println("Exception in Thread Run. Exception : " + ex);
}
}
public String processInput(String input) {
    boolean commandFound = false;
    String output = "";
    try {
        if (input.getBytes("UTF-8").length > 255)
            output = "Max string length exceeded";
    } catch (UnsupportedEncodingException e) {
        // TODO Auto-generated catch block
        e.printStackTrace();
    }
    Pattern allPattern = Pattern
            .compile("(?<lower>^LOWERCASE\\s.+)|(?<upper>^UPPERCASE\\s.+)|(?<reverse>^REVERSE\\s.+)|(?<bye>^BYE)|(?<shutdown>^SHUTDOWN passwort)");

    Matcher allMatcher = allPattern.matcher(input);
    if (allMatcher.find()) {
        String lower = allMatcher.group("lower");
        String upper = allMatcher.group("upper");
        String reverse = allMatcher.group("reverse");
        String bye = allMatcher.group("bye");
        String shutdown = allMatcher.group("shutdown");
        commandFound = true;
        if (lower != null) {
            output = lower.substring(10).toLowerCase();
        }
        if (upper != null) {
            output = upper.substring(10).toUpperCase();
        }
        if (reverse != null) {
            output = new StringBuilder(reverse.substring(8)).reverse()
                    .toString();
        }
        if (bye != null) {
            output = "BYE";
            closeFlag = true;
        }
        if (shutdown != null) {
            output = "SHUTDOWN";
            MulServer_v1.shutdownFlag = true;
            closeFlag = true;
        }
    } else {
        commandFound = false;
        output = "UNKNOWN COMMAND";
    }

    if (commandFound) {
        output = "OK ".concat(output);
    } else {
        output = "ERROR ".concat(output);

    }
    return output;

}
}

现在关闭工作正常,但新客户端可以在关机后连接。怎么可能? 这是我曾经查过的Sysout:

关闭..

线程以name:pool-1-thread-3

开头

从pool-1-thread-3收到消息:。 //&lt; - 这个(发送消息)//应该不会发生,因为executor.shutdown();已被召唤。

2 个答案:

答案 0 :(得分:1)

问题是你的信号被破坏了:

   while (!shutdownFlag) {
            try {
                Socket clientSocket = serverSocket.accept();
                executor.execute(new RequestHandler_v1(clientSocket));

            } catch (IOException e) {

accept()阻塞操作 - 阻塞直到新连接正常?这是culrpit。发送“shutdown”命令后,当前线程将在accept()上取消阻止,提交,通过while条件再次阻止。在此之后,正确的执行程序将标志设置为false,但服务器仍为accepting,因此池永远不会关闭。 另一次连接尝试应该唤醒服务器并尊重shutdownFlag突破循环并导致所有处理程序在10秒后死亡。

此外:

while ((userInput = in.readLine()) != null) {

是一个阻止操作 - 它阻止你的任务完成,因此池会更新关闭。如果流将以自然或异常结束,将返回null。你没有在两边都没有结束流。所以它会阻止。

ExecutorsService#shutdownNow()并不意味着来自池的线程将被终止 - 它们被发出信号以终止,并且线程将像使用Thread.isTerminated()标志所提到的@PeterLawrey一样正常终止。

答案 1 :(得分:1)

关闭套接字的概念证明将从阻塞的IO操作中断: public class Buffers {     private static Socket client;

static class ServerThread extends Thread {
    @Override
    public void run() {
        try {
            ServerSocket serverS = new ServerSocket(1099);
            client = serverS.accept();
            client.getOutputStream().write('a');
            client.getOutputStream().flush();
            Thread.sleep(2000);
            client.close();
        } catch (IOException | InterruptedException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
    }
}

static class ClientThread extends Thread {
    @Override
    public void run() {
        try {
            Thread.sleep(500);
            Socket socket = new Socket("127.0.0.1", 1099);
            BufferedReader input = new BufferedReader(new InputStreamReader(socket.getInputStream()));
            System.out.println("Will try to read");
            String line=null;
            while ((line = input.readLine()) != null) { // block here
                System.out.println("Read " + line); // will never come here
            }
        } catch (Exception e) {
            System.out.println("Server closed the connection!");
        }
        super.run();
    }
}

public static void main(String[] args) throws InterruptedException {
    new ServerThread().start();
    ClientThread t = new ClientThread();
    t.start();
    t.join();

}

如果您发表评论client.close();应用将永远不会像您的情况那样结束。