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()か、それとも run メソッドになりますか?

4

4 に答える 4

13

を使用する必要がありますExecutorService。クライアント リクエストの処理は でrun()あり、各承認後に、非同期的にクライアントにサービスを提供するために Runnable呼び出すことができます。ExecutorService を使用したサンプル。ExecutorService.submit(runnableTask)

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. クライアントごとに、スレッドを fork して接続を処理し、スレッドにクライアント ソケットを渡します。@basiljamesが示すように、を使用しExecutorServiceてスレッドを管理することをお勧めします。

サンプルコードは次のとおりです。

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スレッドプールを使用することをお勧めします。ただし、何らかの理由でThreadraw の使用に固執している場合は、メソッドで代わりに次のことを行うことができます。ThreadacceptClients()

    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 に答える