Java Socket - 如何检查 ServerSocket 是否已被另一个客户端使用

Java Socket - how to check if the ServerSocket is already in use by another client

我有这个客户端和服务器程序。

客户端从用户那里得到一条命令,并将其发送给服务器,服务器return将结果(命令的输出)发送给客户端。

我有 2 个 类 Command 和 CommandResult(代表命令和结果),我将它们作为 json 与 ObjectInput(/Output)Stream 传递。

这是代码:

客户端

    Socket socket = new Socket("localhost", 1111);
    ObjectOutputStream out = new ObjectOutputStream(socket.getOutputStream());
    ObjectInputStream in = new ObjectInputStream(socket.getInputStream());

    while (true) {
        //get command from user
        Scanner scanner = new Scanner(System.in);
        System.out.println("Enter command:");
        String cmd = scanner.nextLine();
        Command command = new Command(cmd);

        //send the command as json
        String jsonCommand = new Gson().toJson(command);
        out.writeObject(jsonCommand);
        out.flush();

        //get and print the result
        String jsonCommandResult = (String) in.readObject();
        CommandResult commandResult = new Gson().fromJson(jsonCommandResult, CommandResult.class);
        System.out.println(commandResult.getOutput());
    }

服务器

    ServerSocket serverSocket = new ServerSocket(1111);
    Socket socket = serverSocket.accept();
    ObjectOutputStream out = new ObjectOutputStream(socket.getOutputStream());
    ObjectInputStream in = new ObjectInputStream(socket.getInputStream());

    while (true) {
        //read the command
        String cmd = (String) in.readObject();
        Command command = new Gson().fromJson(cmd, Command.class);

        //run() method will run the command with java Process and return the result
        CommandResult commandResult = command.run();

        //sent the result back to the client as json
        String jsonCommandResult = new Gson().toJson(commandResult);
        out.writeObject(jsonCommandResult);
        out.flush();
    }

现在,当我有一个客户时,它工作正常。

但是如果我尝试 运行 客户端程序的第二个实例,而第一个实例仍在循环中,它会挂在 ObjectInputStream 构造函数中。 (第三行)

ObjectInputStream in = new ObjectInputStream(socket.getInputStream());

据我从文档中了解到,构造函数会阻塞,直到创建相应的 ObjectOutputStream。

Creates an ObjectInputStream that reads from the specified InputStream. A serialization stream header is read from the stream and verified. This constructor will block until the corresponding ObjectOutputStream has written and flushed the header.

在我的例子中,服务器尚未接受新客户端,因此尚未创建 ObjectOutputStream。

现在,我想要的是在新客户端尝试连接而其他客户端连接到同一端口时抛出异常。 但是我不知道如何在调用此构造函数之前检查端口现在是否正在使用。

正如@Kayaman 在评论中提出的那样,我创建了一个 Runnable class 处理当前客户端。

然后在服务器中,我在 serverSocket.accept() 上循环,通过为第一个客户端启动一个新线程,一次只允许一个客户端,并检查当前客户端是否完成了他的通信.

这是最终服务器class。

public class Server {
    private static final int SERVER_READY = 1;
    private static final int SERVER_BUSY = 0;

    public static void main(String[] args) throws IOException, InterruptedException, ClassNotFoundException {
        int port = Integer.valueOf(args[0]);
        System.out.println(String.format("Starting server on port %s", port));
        ServerSocket serverSocket = new ServerSocket(port);
        System.out.println("Server is ready");

        Thread clientThread = null;
        while (true) {
            Socket clientSocket = serverSocket.accept();
            OutputStream os = clientSocket.getOutputStream();
            if (clientThread != null && clientThread.isAlive()) {
                os.write(SERVER_BUSY);
                continue;
            }
            os.write(SERVER_READY);
            System.out.println(String.format("Client connected: %s", clientSocket.getInetAddress()));
            clientThread = new Thread(new ClientWorker(clientSocket));
            clientThread.start();
        }
    }

    public static class ClientWorker implements Runnable {
        private final Socket clientSocket;

        ClientWorker(Socket clientSocket) {
            this.clientSocket = clientSocket;
        }

        @Override
        public void run() {
            try {
                handleClient();
            } catch (Exception e) {
                e.printStackTrace();
            }
        }

        private void handleClient() throws IOException, ClassNotFoundException {
            try {
                ObjectOutputStream out = new ObjectOutputStream(clientSocket.getOutputStream());
                ObjectInputStream in = new ObjectInputStream(clientSocket.getInputStream());
                while (true) {
                    System.out.println("Waiting for command..");
                    String cmd = (String) in.readObject();
                    System.out.println(String.format("Command received:\n %s", cmd));
                    if (cmd.equals("exit"))
                        break;
                    Command command = new Gson().fromJson(cmd, Command.class);
                    CommandResult commandResult = command.run();
                    String jsonCommandResult = new Gson().toJson(commandResult);
                    System.out.println(String.format("Sending response:\n %s", jsonCommandResult));
                    out.writeObject(jsonCommandResult);
                    out.flush();
                }
                //in case the client connection has closed, we want to end the while loop and accept a new client
            } catch (EOFException | SocketException e) {
            }
            System.out.println("Connection has been closed");
            clientSocket.close();
            System.out.println("Server is ready");
        }
    }
}

在客户端class,我检查服务器是否就绪。

Socket socket = new Socket(HOST, PORT);
int status = socket.getInputStream().read();
if (status != SERVER_READY)
    throw new Exception(String.format("Failed to connect to server %s:%s, Server is busy", HOST, PORT));
ObjectOutputStream out = new ObjectOutputStream(socket.getOutputStream());
ObjectInputStream in = new ObjectInputStream(socket.getInputStream());
....
....