1

Javaを使用してマルチスレッドチャットクライアントサーバーを作成しようとしています。私はこのチュートリアルを開始点として使用しています:http://pguides.net/java-tutorial/java-tcp-clientserver-chat/

文字列「quit」を入力したときにクライアントを停止させたいのですが、どうすればよいかわかりません。また、クライアントが切断されたときに、接続されたニックネームリストからクライアントを削除する必要があります。

サーバ

/* ChatServer.java */
import java.net.ServerSocket;
import java.net.Socket;

import java.io.IOException;
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.io.PrintWriter;

import java.util.Hashtable;

public class ChatServer {
    private static int port = 1001; 

    public static void main (String[] args) throws IOException {

        ServerSocket server = null;
        try {
            server = new ServerSocket(port);
        } catch (IOException e) {
            System.err.println("Could not listen on port: " + port);
            System.err.println(e);
            System.exit(1);
        }

        Socket client = null;
        while(true) {
            try {
                client = server.accept();
            } catch (IOException e) {
                System.err.println("Accept failed.");
                System.err.println(e);
                System.exit(1);
            }
            /* start a new thread to handle this client */
            Thread t = new Thread(new ClientConn(client));
            t.start();
        }
       }
}

class ChatServerProtocol {
    private String nick;
    private ClientConn conn;

    private static Hashtable<String, ClientConn> nicks = new Hashtable<String, ClientConn>();

    private static final String msg_OK = "OK";
    private static final String msg_INVALID = "INVALID COMMAND";
    private static final String msg_SEND_FAILED = "FAILED TO SEND";

    private static boolean add_nick(String nick, ClientConn c) {
        if (nicks.containsKey(nick)) {
            return false;
        } else {
            nicks.put(nick, c);
            return true;
        }
    }

    private static boolean remove_nick(String nick, ClientConn c) {
        if (!(nicks.containsKey(nick))) {
            return false;
        } else {
            nicks.remove(nick);
            return true;
        }
    }

    public ChatServerProtocol(ClientConn c) throws IOException {
        nick = null;
        conn = c;
    }


    private boolean sendMsg(String recipient, String msg) {
        if (nicks.containsKey(recipient)) {
            ClientConn c = nicks.get(recipient);
            c.sendMsg(nick + ": " + msg);
            return true;
        } else {
            return false;
        }
    }


    public String process(String msg) {
        if (msg.startswith("Nick"){
        String output = "";
        if(add_nick(tryauthor, this.conn)) {
            this.nick = tryauthor;
            output = "Welcome "+tryauthor;
            } else {
                    output = "Nick already in";
                }
            }

        }
        else if (msg.startsWith("msg")) {
            String[] msg_parts = msg.split(":");
            for(int i=0; i<msg_parts.length; i++){
                System.out.println(msg_parts[i]);
            }
            String msg_type = msg_parts[0];
            if(msg_type.equals("msg")) {
                if(msg_parts.length < 3) output = msg_INVALID;
                if(sendMsg(msg_parts[1], msg_parts[2])) output = msg_OK;
                else output = msg_SEND_FAILED;
            } else {
                output = msg_INVALID;
            }
        }

        return output;
     }
    }

class ClientConn implements Runnable {
    private Socket client;
    private BufferedReader in = null;
    private PrintWriter out = null;

    public ClientConn(Socket client) {
        this.client = client;
        try {
            /* obtain an input stream to this client ... */
            in = new BufferedReader(new InputStreamReader(client.getInputStream()));
            /* ... and an output stream to the same client */
            out = new PrintWriter(client.getOutputStream(), true);
        } catch (IOException e) {
            System.err.println(e);
            return;
        }
    }

    public void run() {
        String msg, response;

        try {
             ChatServerProtocol protocol = new ChatServerProtocol(this);
            /* loop reading lines from the client which are processed 
             * according to our protocol and the resulting response is 
             * sent back to the client */
            while ((msg = in.readLine()) != "quit\r\n") {
                response = protocol.process(msg);
                out.println("SERVER: " + response);
            }
            this.close();
        } catch (IOException e) {
            System.err.println(e);
        }
    }

    public void sendMsg(String msg) {
        out.println(msg);
     }

    public void close() throws IOException {
        in.close();
        out.close();
        client.close();
    }

}

クライアント

/* ChatClient.java */
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.io.IOException;
import java.io.PrintWriter;
import java.net.Socket;
import java.net.UnknownHostException;

public class ChatClient {
private static int port = 1001; 
private static String host = "localhost"; 

private static BufferedReader stdIn;

public static void main (String[] args) throws IOException {

    Socket server = null;

    try {
        server = new Socket(host, port);
    } catch (UnknownHostException e) {
        System.err.println(e);
        System.exit(1);
    }

    stdIn = new BufferedReader(new InputStreamReader(System.in));

    PrintWriter out = new PrintWriter(server.getOutputStream(), true);
    BufferedReader in = new BufferedReader(new InputStreamReader(server.getInputStream()));

    System.out.print("Nick: ");
    String auth = stdIn.readLine();
    out.println("Nick: " + auth);
    String serverResponse = in.readLine();
    System.out.println(serverResponse);

    if (serverResponse.startsWith("SERVER: welcome")) {
         /* create a thread to asyncronously read messages from the server */
        ServerConn sc = new ServerConn(server);
        Thread t = new Thread(sc);
        t.start();

        String msg;
        /* loop reading messages from stdin and sending them to the server */
        while ((msg = stdIn.readLine()) != "quit\r\n") {
            out.println(msg);
        }
        sc.close();
        System.out.println("Exit.");
        System.out.println("---Client Error---");
    }
    else {
        System.out.println("Exit.");
        System.out.println("---Client Error---");
    }


  }
}

class ServerConn implements Runnable {
    private BufferedReader in = null;
    private Socket server;

    public ServerConn(Socket s) throws IOException {
        server = s;
        in = new BufferedReader(new InputStreamReader(s.getInputStream()));
    }

    public void run() {
        String msg;
        try {
            /* loop reading messages from the server and show them 
             * on stdout */
             while ((msg = in.readLine()) != "quit\r\n") {
                System.out.println(msg);
             }
             this.close();
        } catch (IOException e) {
            System.err.println(e);
        }
      }

    public void close() throws IOException {
        in.close();
        server.close();
    }

}

どこでどのように接続を閉じる必要がありますか?

4

2 に答える 2

1

文字列「quit」を入力したときにクライアントを停止させたいのですが、どうすればよいかわかりません。

現在のJavaプログラムは。ですぐに終了できますSystem.exit(status)。オペレーティングシステムは、プロセスが終了すると、プロセスによって取得されたすべてのリソース(tcpソケットなど)を自動的に解放するため、接続も閉じます。

また、クライアントが切断されたときに、接続されたニックネームリストからクライアントを削除する必要があります。

それは難しいです。ネットワークケーブルが抜かれているなど、クライアントが終了する以外の理由で切断が発生する可能性があることに注意してください。つまり、クライアントはサーバーにもう存在しないことを確実に通知できず、サーバーはこれを自分で検出する必要があります。サーバーオペレーティングシステムのtcp実装は、クライアントがもう存在しないことを検出し(おそらく実装に依存する速度)、そのソケットを使用してアプリケーションに例外をスローします。これにより、ニックネームをキャッチして削除できます。リスト。

于 2012-05-20T10:46:31.733 に答える
1

これで、クライアントの名前が以前に作成したグラフの一部である場合にのみ、クライアントを認証できます。

これはコードに書かれているようです。Process()メソッドでは、で作成者を探し、見つからauthorsgraphない場合はエラーを返します。これはあなたが意図したものではありませんか?著者が見つからない場合は、代わりに追加する必要がありますか?作成者を?に追加する方法とともに、それらが見つからない場合は、おそらくadd_nice()呼び出しが行われるはずです。elseauthorsgraph

デバッガーでの新しい接続の処理をウォークスルーすると、ここで役立つ場合があります。メッセージの自由な使用System.out.println()も役立つかもしれません。

文字列「quit」を入力したときにクライアントを停止させたいのですが、どうすればよいかわかりません。

このコードにはバグがあります。が等しい .equals()かどうかを確認するために使用する必要があります。同じ文字列の内容ではなく、同じ参照がないことをテストしているだけです:String!=msgString

// can't use != on a `String` to check contents
while ((msg = stdIn.readLine()) != "quit\r\n") {

そのはず:

while (true) {
   msg = stdIn.readLine();
   if (msg == null || msg.equals("quit")) {
      break;
   }
   ...

「\r\n」をチェックしていないことにも注意してください。このreadLine()メソッドは、「行末文字を含まない」文字列を(javadocsから引用するために)返します。nullまた、ソケットが閉じている場合に備えてテストする必要があります。また、同じバグを持つクライアントコードに2つの場所があります。

クライアントが切断されたら、接続されたニックネームリストからクライアントを削除する必要があります。

上記のwhileループは、クライアントソケットが閉じているか、クライアントソケットに入ると終了します"quit"。次に、を呼び出しますclose()。電話をかけた後、電話をかけて彼をループから削除するclose()ことができます。protocol.remove_nicks()しかし、あなたはそこにいません"nick" String

nicks 本当に、内部のリストを維持するChatServerProtocolことはそれのための適切な場所ではないかもしれません。接続ごとに1つずつあるので、自分でクラスにロールインしChatServerProtocolます。いずれにせよ、あるオブジェクトは、作成者がログインした文字列ClientConnを保存し、入力または接続が閉じた後にその文字列を使用して呼び出す必要があります。"nick"remove_nick()"quit"

最後に、HashTableJava 5以降、の使用は非推奨になりCollections.synchronizedMap(new HashMap<...>())ました。代わりに使用します。

お役に立てれば。

于 2012-05-20T14:13:51.533 に答える