8

我完成了一个运行良好的客户端/服务器套接字通信程序。现在我正试图弄清楚如何做到这一点,以便我可以一次有多个客户端连接到服务器。我环顾四周,似乎有不止几种不同的方法可以做到这一点。所以我来这里向你们寻求帮助/建议。

我的服务器:

public class Server {
    private ServerSocket serverSocket = null;
    private Socket clientSocket = null;

    public Server() {
        try {
            serverSocket = new ServerSocket(7003);
        } catch (IOException e) {
            System.err.println("Could not listen on port: 7003");
            System.exit(1);
        }

        try {
            clientSocket = serverSocket.accept();
        } catch (IOException e) {
            System.err.println("Accept failed");
            System.exit(1);
        }
    }

    public void startServer() throws IOException {
        PrintWriter output = new PrintWriter(clientSocket.getOutputStream(), true);
        BufferedReader input = new BufferedReader(new InputStreamReader(clientSocket.getInputStream()));

        String inputLine, outputLine;

        outputLine = "Connected to Server";
        output.println(outputLine);

        while ((inputLine = input.readLine()) != null) {
            // This just determines users input and server ruturns output based on that

            outputLine = this.getServerOutput(inputLine);
            output.println(outputLine);

            if (outputLine.equals("Bye"))
                break;
        }

        output.close();
        input.close();
        clientSocket.close();
        serverSocket.close();
    }
}

我需要让我的构造函数创建线程startServer()还是我的运行方法?

4

4 回答 4

13

你应该使用ExecutorService. 您的客户端请求处理将是run()一个Runnable,并且在每次接受后,您可以调用ExecutorService.submit(runnableTask)异步服务客户端。

使用 ExecutorService 的示例。

public class MyServer {

    private static MyServer server; 
    private ServerSocket serverSocket;

    /**
     * This executor service has 10 threads. 
     * So it means your server can process max 10 concurrent requests.
     */
    private ExecutorService executorService = Executors.newFixedThreadPool(10);        

    public static void main(String[] args) throws IOException {
        server = new MyServer();
        server.runServer();
    }

    private void runServer() {        
        int serverPort = 8085;
        try {
            System.out.println("Starting Server");
            serverSocket = new ServerSocket(serverPort); 

            while(true) {
                System.out.println("Waiting for request");
                try {
                    Socket s = serverSocket.accept();
                    System.out.println("Processing request");
                    executorService.submit(new ServiceRequest(s));
                } catch(IOException ioe) {
                    System.out.println("Error accepting connection");
                    ioe.printStackTrace();
                }
            }
        }catch(IOException e) {
            System.out.println("Error starting Server on "+serverPort);
            e.printStackTrace();
        }
    }

    //Call the method when you want to stop your server
    private void stopServer() {
        //Stop the executor service.
        executorService.shutdownNow();
        try {
            //Stop accepting requests.
            serverSocket.close();
        } catch (IOException e) {
            System.out.println("Error in server shutdown");
            e.printStackTrace();
        }
        System.exit(0);
    }

    class ServiceRequest implements Runnable {

        private Socket socket;

        public ServiceRequest(Socket connection) {
            this.socket = connection;
        }

        public void run() {

            //Do your logic here. You have the `socket` available to read/write data.

            //Make sure to close
            try {
                socket.close();
            }catch(IOException ioe) {
                System.out.println("Error closing client connection");
            }
        }        
    }
}
于 2012-09-25T18:03:02.077 回答
5

如何做到这一点,以便我可以一次与服务器建立多个客户端连接

现在您正在启动服务器并立即等待单个客户端在构造函数中连接。

clientSocket = serverSocket.accept();

然后你在你的startServer()方法中处理那个单一的套接字连接。这意味着不会处理其他客户端。

public void startServer() throws IOException {
    PrintWriter output = new PrintWriter(clientSocket.getOutputStream(), true);
    ...

通常使用这样的服务器模式,您会执行以下操作:

  1. 在构造函数中设置您的服务器套接字。
  2. 创建一个acceptClients()循环等待客户端被接受的方法。这可以派生一个线程以在后台在其自己的线程中接受客户端。
  3. 对于每个客户端,要么派生一个线程来处理连接,将线程传递给客户端套接字。正如@basiljames 所示,最好使用 anExecutorService为您管理线程。

这是一些示例代码:

public class Server {
    private ServerSocket serverSocket = null;

    public Server(int portNumber) throws IOException {
        serverSocket = new ServerSocket(portNumber);
    }

    // this could be run in a thread in the background
    public void acceptClients() throws IOException {
        // create an open ended thread-pool
        ExecutorService threadPool = Executors.newCachedThreadPool();
        try {
            while (!Thread.currentThread().isInterrupted()) {
                // wait for a client to connect
                Socket clientSocket = serverSocket.accept();
                // create a new client handler object for that socket,
                // and fork it in a background thread
                threadPool.submit(new ClientHandler(clientSocket));
            }
        } finally {
            // we _have_ to shutdown the thread-pool when we are done
            threadPool.shutdown();
        }
    }

    // if server is running in background, you stop it by killing the socket
    public void stop() throws IOException {
        serverSocket.close();
    }

    // this class handles each client connection
    private static class ClientHandler implements Runnable {
        private final Socket clientSocket;
        public ClientHandler(Socket clientSocket) {
            this.clientSocket = clientSocket;
        }
        public void run() {
            // use the client socket to handle the client connection
            ...
        }
    }
}

几乎所有类似的实现都建议使用ExecutorService线程池ThreadThread但是,如果由于某种原因您坚持使用 raw ,则可以在您的acceptClients()方法中执行以下操作:

    public void acceptClients() throws IOException {
        while (!Thread.currentThread().isInterrupted()) {
            // wait for a client to connect
            Socket clientSocket = serverSocket.accept();
            // fork a background client thread
            new Thread(new ClientHandler(clientSocket)).start();
        }
    }
于 2012-09-25T19:33:16.997 回答
3

改变这个:public void startServer() throws IOException 到这个:public void startServer(Socket clientSocket) throws IOException

那么你需要做的就是:

public Server()
{
    try
    {
        serverSocket = new ServerSocket(7003);
    }
    catch (IOException e)
    {
        System.err.println("Could not listen on port: 7003");
        System.exit(1);
    }

    try
    {
        while(true) {
            final Socket socket = serverSocket.accept();
            new Thread(new Runnable() {
                public void run() {
                    try {
                        startServer(socket);
                    } catch(IOException e) {e.printStackTrace();}
                }
            }).start();
        }
    }
    catch(IOException e)
    {
        System.err.println("Accept failed");
        System.exit(1);
    }
}

最后,您可以删除private Socket clientSocket = null;

那应该能让你到达那里。或者至少非常接近。

于 2012-09-25T18:01:35.350 回答
0
private static final int SERVER_PORT = 35706;
private ServerSocket serverSocket;
private final ArrayList<ClientThread> activeClients = new ArrayList<>();

public void startServer() {

    try {
        serverSocket = new ServerSocket(SERVER_PORT);
        
        final ExecutorService clientPool = Executors.newCachedThreadPool();

        while (!serverSocket.isClosed()) {

            try {
                Future<Socket> future = clientPool.submit(() -> {
                       Socket socket = serverSocket.accept();
                       ClientThread clientThread= new ClientThread(socket);
                       return (socket);
                });

                activeClients.add(future.get());
            } catch (IOException e) {
                clientPool.shutdownNow();
                System.out.println(e.getMessage());
            } catch (InterruptedException | ExecutionException e) {
                System.out.println(e.getMessage());
            }
        }

    } catch (IOException e) {
        System.out.println(e.getMessage());
    }
}



public void stopServer() {  

   try {
        serverSocket.close();
        activeClients.forEach(socket -> {
            try {
                socket.close();
            } catch (IOException e) {
                System.out.println(e.getMessage());
            }
        });
            
   } catch (IOException ex) {
        System.out.println(e.getMessage());
   }

}



private static class ClientThread implements Runnable{
    private final Socket socket;

    public ClientThread(Socket socket) throws IOException {
       this.socket = socket;
    }
        
    @Override
    public void run() {
        /* Your implementation */
    }
}
于 2020-08-29T11:44:02.617 回答