2

Java を使用してサーバー/クライアント システムを実装しています。サーバーはクライアントからの着信接続をリッスンし、クライアントが接続した後、サーバーは新しいソケットを作成し、それをデータの受信にのみ使用される新しいスレッドに渡します。

while (true){

        clientSocket=serverSocket.accept();
        new ClientReceiver(clientSocket,this.clientsManager).start();
    }

clientReceiver クラスは次のとおりです。

public class ClientReceiver extends Thread {

private Socket clientSocket=null;
private Client client=null;
private ClientsManager clientsManager;

private ClientActionParser clientActionParser=new ClientActionParser();

ClientHandlerState clientHandlerState;

PrintWriter outputStream=null;
BufferedReader inputStream=null;

public ClientReceiver(Socket clientSocket, ClientsManager clientsManager){

    this.clientSocket=clientSocket;
    this.clientsManager=clientsManager;
    this.setClientHandlerState(ClientHandlerState.Connected);
}

public void run(){

    String actionString;
    try{
        //define output and input stream to client
        outputStream =new PrintWriter(clientSocket.getOutputStream(),true);
        inputStream = new BufferedReader(new InputStreamReader(clientSocket.getInputStream()));



        //while client is connected read input to actionString
        while((actionString=inputStream.readLine()) != null){

                AbstractClientAction clientAction= this.clientActionParser.parse(actionString);

                if(this.getClientHandlerState()==ClientHandlerState.Connected){

                    if(clientAction instanceof ClientLoginAction){

                        ClientLoginAction clientLoginAction=(ClientLoginAction) clientAction;
                        if(this.authenticate(clientLoginAction)){


                        }
                        else{
                                throw new AuthenticationException();
                        }

                    }
                    else{

                        throw new AuthenticationException();
                    }
                }

            }
            if(this.getClientHandlerState()==ClientHandlerState.Authorized){

                //receive other client actions: transfer barge ....
            }
            try {
                Thread.sleep(400);
            } catch (InterruptedException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
    }
    catch(IOException e){

    }
    catch (AuthenticationException e) {
        // TODO: handle exception
    }

    //clean up the resources
    try{
        outputStream.close();
        inputStream.close();
        clientSocket.close();
    }
    catch(Exception e){

    }
}

private boolean authenticate(ClientLoginAction clientLoginAction){
    //perform authentication. If authentication successful:
    this.client=this.clientsManager.authenticateClient(clientLoginAction.getUsername(), clientLoginAction.getPassword());
    if(this.client==null){
        return false;
    }
    else{
        ClientSender clientSender=new ClientSender(this.outputStream, this.client);
        this.clientsManager.addClientSender(clientSender);
        this.setClientHandlerState(ClientHandlerState.Authorized);
        clientSender.start();
        return true;
    }
}

public ClientHandlerState getClientHandlerState(){

    return this.clientHandlerState;
}

public void setClientHandlerState(ClientHandlerState clientHandlerState){

    this.clientHandlerState=clientHandlerState;
}

受信者スレッドで認証が成功すると、データをクライアントに送信するための新しいスレッドが作成され、ソケットの outputStream が新しいスレッドに渡されます。clientSender クラスには、クライアントに送信する必要があるデータを含むバッファーとしてキューが含まれています。クラスclientSenderは次のとおりです。

public class ClientSender extends Thread {

private Client client=null;
private final Log logger = LogFactory.getLog(getClass());
PrintWriter outputStream=null;
private Queue<String> clientEventsQueue= new LinkedList<String>();

public ClientSender(PrintWriter outputStream, Client client){
    this.outputStream=outputStream;
    this.client=client;
}

public void run(){

    //System.out.println("ClientSender run method called.");

    while(true){

        try {
            Thread.sleep(10);
        } catch (InterruptedException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }


        if(!this.clientEventsQueue.isEmpty()){

            this.outputStream.println(this.clientEventsQueue.remove());
        }
    }
}

public Client getClient(){

    return this.client;
}

public void insertClientEvent(String clientEvent){

    this.clientEventsQueue.add(clientEvent);
}

私が使用するクライアントに何かを送信したいときはいつでも:

clientSender.insertClientEvent("some text");

問題は、Thread.sleep(10) を削除すると、クライアント側で何も受信されないことです。TCP ソケットがブロックされているため、これは発生しないはずです。これは正常ですか、それとも何か間違っていますか?

編集: 送信者スレッドの「終了」はありません。サーバーは、別のシステムからイベントを受け取るたびに、すべてのクライアントに適切な情報を送信する必要があります。したがって、送信するデータがない場合はスレッドを停止し、送信するデータがある場合はいつでもスレッドを開始するのが最善のシナリオだと思います。だから私は clientSender クラスでこれを試しました:

public void run(){

    while(true){

        if(this.clientEventsQueue.isEmpty()){
            break;
        }
        else{
            try {
                this.outputStream.println(this.clientEventsQueue.take());
            } catch (InterruptedException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
        }
    }
}

しかし、問題はいつスレッドを開始するかです。データを送信したいときはいつでも開始しようとしましたが、期待どおりに正しく機能せず、最初のパッケージのみを送信します。

clientSender.insertClientEvent(clientEvent.getEventString());
clientSender.start();

EDIT2 このアイデアを思いつきました。これは非常にシンプルで、CPU 時間の消費がはるかに少ないと思います。

while(true){

        while(this.clientEventsQueue.isEmpty()){

            try {
                Thread.sleep(300);
            } catch (InterruptedException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
        }

        try {
            this.outputStream.println(this.clientEventsQueue.take());
        } catch (InterruptedException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }

    }

私がそれをテストした限り、それはうまくいきました。あなたはそれについてどう思いますか?

4

1 に答える 1