0

私は3つのメインクラスを含む動作中のJavaサーバーを持っています(端が少し荒いですが)。

最初のクラスはサーバーを実行し、ソケットを取得してポートをリッスンし、新しい接続をクライアント ハンドラーに渡します。

2 番目のクラスは、スレッド化されたクライアント ハンドラーです。

3 つ目は、クライアント ハンドラから呼び出されて情報を処理するプロトコル クラスです。情報が処理されると、プロトコル クラスは処理済みまたはフォーマット済みの応答をクライアント ハンドラーに返し、クライアントに渡します。

利点は、2 番目のクラスに、ソケットから受け入れ可能なデータをロードするだけでよいことです。データはプロトコル ハンドラに渡すことができ、プロトコル ハンドラには、サーバーがクライアントと通信するために使用する任意のプロトコルをロードできます。

この例では、telnet ベースのチャット クラスをロードしました。

たとえば、誰かがチャットを離れた場合、クライアント ハンドラ クラスは次のようなコードを実行する可能性があります。

for (i = 0; i < currentClientsConnected; i++) {
    if(threads[i] != null && threads[i] != this) {
        outputLine = threads[i].serverprotocol.processInput("** " + username + " has left the room **");
        threads[i].out.printf(outputLine);

    }
}

これはクラスに渡さ"** [username] has left the room **"れ、serverprotocolクラスはメッセージをクライアントに送信するための最適な方法でデータを返します。この場合、serverprotocolクラスはクライアントに画面を再描画し、新しいメッセージを追加し、バッファ内の既存の現在のメッセージを上にスクロールするように指示する telnet 制御コードを使用してメッセージをフォーマットします。

また、クライアント ハンドラー クラスは、たとえばユーザーが特定のチャット ルームにいるソケットにのみメッセージを送信する必要があるため、常にすべてのソケットに送信する必要はありません。

私のコードでは、これはクラス 1 です。ソケットを受け入れるサーバー クラスは次のとおりです。

while (true) {
    int i;
    // Try and accept the connection
    try {
        clientSocket = serverSocket.accept();
        // System.out.printf("Remote IP:");
        // System.out.printf(clientSocket.getRemoteSocketAddress().toString());

        // Find an unused socket if one is available
        for (i = 0; i < maxClientsAllowed; i++) {

            // If found create thread
            if (threads[i] == null) {
                (threads[i] = new clientThread(clientSocket, threads)).start();
                break;
            }
        }

        // If all sockets are taken
        if (i == maxClientsAllowed) {
            PrintWriter out = new PrintWriter(clientSocket.getOutputStream(), true);
            out.printf("Server too busy. Try later.\n");
            out.close();
            clientSocket.close();
        }
    } catch(IOException e) {
        System.out.println(e);}
}

クラス 2 は、スレッドを拡張するクラスです。

class clientThread extends Thread {
    private String clientName = null;
    private DataInputStream in;
    private PrintWriter out;
    private Socket clientSocket = null;
    private final clientThread[] threads;
    private int currentClientsConnected;
    private serverprotocol serverprotocol;

    public clientThread(Socket clientSocket, clientThread[] threads) {
        this.clientSocket = clientSocket;
        this.threads = threads;
        currentClientsConnected = threads.length;
    }

    public void run() {
    //stuff 
    }
}

代わりにこれを機能させることができるかどうかを必死に確認しようとしましたが、スレッドのインスタンス番号に基づいてスレッドの(またはおそらくそれを読み取る必要がある) メソッドをimplements Runnable呼び出すことができませんでした (ここのコードで単に呼び出されます)。 .processInputdataToBeProcessedi

私が見た中で最も近いもの:

https://github.com/ico77/chat-server-client/blob/master/src/main/java/hr/ivica/chat/server/ChatServer.java

これは、サーバーをスレッド化されたプール サーバーとして実行する際に利用できます。

ただし、この場合の関数は、 を介してソケットに関連付けられた にsendToAll直接書き込みます。サーバーでは、個々のプロトコル ハンドラー クラスに送信することはできません。また、個々のクラス インスタンスに送信することもできません。これは、たとえば、メッセージをソケット 1 と 3 のみに送信してから、別のメッセージをソケット 2 に送信できないことを意味します。PrintWriterHashMapChatServerWorker

を使用せずにソケットハンドラーのインスタンスを呼び出すことができるオンラインの例が1つも見つかりませんextends Thread

具体的には、次のような行を使用できるようにしたいと考えています。

threads[i].out.printf(outputLine);

また

if(threads[i].[class].[var] == 'something') {
// stuff
}

整数を使用して、スレッド インスタンス、またはそのスレッドで使用される任意のクラス変数またはメソッドを参照できます。

何か不足していますか?

4

2 に答える 2

0

あなたの質問を理解できたかどうかわかりません。簡単な答え: clientThread を Runnable にしたい場合は、それを実行してから行を変更するだけです

  (threads[i] = new clientThread(clientSocket, threads)).start();

の中へ

  (threads[i] = new Thread(new clientThread(clientSocket, threads))).start();

ドキュメントを見ると: http://docs.oracle.com/javase/7/docs/api/java/lang/Thread.html#Thread(java.lang.Runnable)

スレッドは、Runnable スーパータイプを持つオブジェクトを受け入れます。

長い答え: スレッドを直接格納するのではなく、サーバー側でクライアントを表す抽象化を作成する必要があります。この抽象化は、通信のための機能をカプセル化する必要があります。そうすれば、別の通信ライブラリを実装したい場合でも、簡単にサブクラス化でき、開閉の原則を破ることを避けることができます。

https://en.wikipedia.org/wiki/Open/closed_principle

幸運を。

于 2015-08-20T21:36:11.600 に答える