1

私は問題があります。

私のコードには、サーバーへの「イン」と「アウト」を持つすべてのプレーヤーの配列があります。

サーバーからクライアントへ私は問題なくうまく通信でき、すべてのクライアントはサーバーから送信された変更をGUIに表示します。

問題は、クライアントからサーバーへの通信です。サーバーとして、私はすべてのプレイヤーからいくつかの仕事をするための請願を受け取りたいと思っています。問題は、どのインデックスから請願書を受け取ったかわからplayers[index].in.readUTF()ず、プログラムをブロックするすべての原因を確認できないことです。

手助け?in.avaiable()私にはうまくいきません。

それをより明確に示すために:

プレイヤーがサーバーにログインするとき、私はサーバー側でこれを行います。

socket = serverSocket.accept();
out = new DataOutputStream(menuPrincipal.socket.getOutputStream());
in = new DataInputStream(menuPrincipal.socket.getInputStream());
players[i] = new player(i,out,in);

次に、サーバー側にメッセージを待機しているスレッドがあります。

問題は、どのプレーヤーの配列のインデックスから(どのプレーヤーから)メッセージが来るのかわからないことです。サーバーに何も送信していないプレーヤーのインデックスを確認できません。サーバーがプログラムをブロックするためです。

それがどのように機能するかです:

サーバーからクライアントへ私はただします:

  1. player[i].out.writeUTF(message)サーバー側から
  2. クライアントはそれを実行しin.readUTF()、すべてが正常に動作します

クライアントからサーバーへ私はします:

  1. out.writeUTF(message)クライアントから
  2. しかし、サーバー側では、player[i].in どのプレーヤーかわからないものを読み取るために1つを選択する必要があります。
4

2 に答える 2

2

つまり、サーバーからクライアントにデータを送信できたということですが、正常に機能しています。クライアントからサーバーにデータを送信する場合にも同じ原則が適用されます。

サーバー側にリスニングスレッドを用意します。パケット内に、データを送信するユーザーの名前またはIPアドレスを含めることができます。IE:

Data Recieved by Server: 'message, hello chris, sender, reciever'

サーバーは、文字列トークナイザーを使用して、パケットを意味のあるセクションに分割できます。

Action: 'message'
Message: 'hello chris'
Sender: 'sender'
Reciever: 'reciever'.

次に、続行するために必要なすべてのデータがあります。

この実装は、必要なすべてのデータが受信したパケットに含まれていることを意味し、データがどこから来たかを確認する必要はありません。余分な数バイトのデータは、パケットを受信するたびに検索を実行する必要があるほど遅くなることはありません。

OP編集に応じて編集

または、配列を実装する代わりに、を実装することもできますHashMap<String,Player>。そうすれば、上記の説明を使用して、送信されたパケットからユーザー名を取得し、それをハッシュマップに渡すだけです。ハッシュマップはクライアントを表すUserオブジェクトを返すことができ、ここでもインデックスルックアップは必要ありません。

ここでJavaハッシュマップのドキュメントを見つけることができます。

最後に、あなたがplayerクラスを持っていることに気づきました。Javaの規則では、すべてのクラスは大文字で始まります。あれは:

playerである必要がありますPlayer

コメントに応じた最終編集

あなたの問題は悪いデザインの問題です、私は恐れています。このコンテキストでは、クライアントはすべて同じチャネルを介してサーバーと通信している必要があります。そうすれば、サーバーがしなければならないのは、メッセージをリッスンし、それらを解析して、次に進むことだけです。クライアントがすべて同じストリームを介してサーバーと通信するように実装を調整します。これにより、問題が解消されます。

于 2013-03-06T12:49:05.990 に答える
2

さて、最初に、ソリューションを提供する前に、ソケットがどのように機能するかについての簡単な背景を説明しましょう。何が起こっているのかを理解することが重要です。

ソケット説明リファレンス

ソケットは、サーバープログラムと1つ以上のクライアントプログラム間の双方向通信を確立するソフトウェアエンドポイントです。双方向という言葉に注意してください。これは、クライアントとサーバー間の接続ごとに、入力ストリームと出力ストリームの2つのストリームがあることを意味します。これらには、サーバー側とクライアント側の両方で設定されたソケットオブジェクトからアクセスできます。

ここに画像の説明を入力してください

あなたの問題

サーバーからクライアントへ私はplayer[i].out.writeUTF(message)を実行し、クライアントは彼の「in」をチェックしてin.readUTF()を実行し、すべて正常に動作します。クライアントからサーバーへ私はクライアントからout.writeUTF(message)を実行しますが、サーバー側では1人のplayer [i] .inを選択して、どちらのプレーヤーかわからないものを読み取る必要があります。

したがって、私が正しく理解していれば、どのクライアントがあなたにメッセージを送信したかを判断する方法が困難です。マルチスレッドを正しく使用すると、各クライアントが別々のスレッドで処理されるため、これは簡単に解決できます。

重要なステップは、クライアントが最初にどのように処理されるかです。次のコードを検討してください。短くするために、私は慣習にあまり焦点を合わせませんでした。

ArrayList<ClientWorker> clientWorkers = new ArrayList<ClientWorker>();
int idCounter = 0;

    public void listenSocket(){
      try{
        int port = 9090
        server = new ServerSocket();
      } catch (IOException e) {
        System.exit(-1);
      }
      while(true){
        ClientWorker w;
        try{
    //server.accept returns a client connection
          clientWorker = new ClientWorker(server.accept(), idCounter, );
          idCounter++;
          clientWorkers.add(clientWorker); 
          Thread t = new Thread(clientWorker);
          t.start();
        } catch (IOException e) {
          System.exit(-1);
        }
      }
    }

そのコードを通して、新しいクライアントを処理し、それらに一意のキーを割り当てています。サーバーに接続しているユーザーの数に応じてサイズを増減できるように、配列の代わりに配列リストを使用しています。クライアントを受け取ったら、clientWorkerインスタンスを作成します。さらに、接続されているユーザーごとに一意のキーが割り当てられ、アレイリストからオブジェクトをフェッチするために使用できます(必要な場合)。

ClientWorkerクラスは次のようになります。

    class ClientWorker implements Runnable {
      private Socket client;
      public int id

    //Constructor
      ClientWorker(Socket client, int id) {
        this.client = client;
        this.id = id;
      }

      public void run(){
        String line;
        BufferedReader in = null;
        PrintWriter out = null;
        try{
          in = new BufferedReader(new InputStreamReader(client.getInputStream()));
          out = new PrintWriter(client.getOutputStream(), true);
        } catch (IOException e) {
          System.out.println("in or out failed");
          System.exit(-1);
        }

        while(true){
          try{
//ANY DATA I GET FROM THIS STREAM IS FROM THIS PARTICULAR CLIENT ONLY!
            line = in.readLine();
            System.out.println("RECEIVED FROM CLIENT "+ id +" " + line);
           }catch (IOException e) {
            System.exit(-1);
           }
        }
      }
    }

このスレッドはrun()メソッドを呼び出します。各クライアントには個別のスレッドがあるため、特定のスレッドから処理されるデータは、他の誰かではなく、その特定のクライアントから送信されます。したがって、各クライアントのデータを簡単に区別できます。

長い答えでごめんなさい。私が自分のポイントに到達するまでに少し時間がかかりました。

参考文献

于 2013-03-06T14:18:26.883 に答える