多线程套接字通信客户/服务器

时间:2012-09-25 17:53:45

标签: java multithreading concurrency client-server

我写完了一个工作正常的Client / Server Socket通信程序。现在我正在试图弄清楚如何制作它,以便我可以同时与服务器建立多个客户端连接。我环顾四周,似乎有不止两种不同的方法来做到这一点。所以我来这里向你们寻求帮助/建议。

我的服务器:

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()或者是我的run方法?

4 个答案:

答案 0 :(得分:11)

您应该使用ExecutorService。您的客户端请求处理将是run()的{​​{1}},并且在每次接受后,您可以调用Runnable以异步方式为客户端提供服务。

使用ExecutorService的示例。

ExecutorService.submit(runnableTask)

答案 1 :(得分:4)

  

如何制作它以便我可以同时与服务器建立多个客户端连接

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

clientSocket = serverSocket.accept();

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

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

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

  1. 在构造函数中设置服务器套接字。
  2. 创建一个acceptClients()方法,该方法将循环等待客户端被接受。这可以分叉一个线程来接受后台自己的线程中的客户端。
  3. 对于每个客户端,分叉线程来处理连接,将线程传递给客户端套接字。如@basiljames所示,更好的方法是使用ExecutorService为您管理线程。
  4. 以下是一些示例代码:

    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实现,建议使用Thread线程池。但是,如果由于某种原因您仍然坚持使用原始Thread,则可以使用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();
            }
        }
    

答案 2 :(得分: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;

那会让你到那儿。或者至少非常接近。

答案 3 :(得分: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 */
    }
}