在创建多客户端聊天服务器时未获得所需的输出?

Not getting desired output in Creating a Multiclient Chat Server?

我正在尝试创建一种多客户端聊天服务器,其中我们有多个客户端连接到服务器,并且客户端输入的任何消息都会显示给所有客户端(包括发送消息的客户端)。我没有得到这个输出,而是消息只在发件人客户端而不是其他客户端上回显。代码很长,因此我将展示我认为可以帮助您理解错误的任何代码片段。以防万一,这还不够,只需评论您需要的部分。提前致谢。大约一个半小时以来我一直坚持这个,所以我很感激我能得到的任何帮助。

服务器Class

public class Multiserver {

ServerSocket serversocket;
Socket socket;
ArrayList<Socket> al = new ArrayList<Socket>();
DataInputStream dis;
DataOutputStream dos;

Multiserver() throws IOException
{

     serversocket = new ServerSocket(1036);
     System.out.println("Server started on port 1036");

    while(true)
    {

        socket = serversocket.accept();
        System.out.println(socket);
        al.add(socket);
        Mythread  thread = new Mythread(socket, al);
        thread.start();
    } 
}

服务器中使用的线程class

public class Mythread extends Thread{

Socket socket;
ArrayList al;
DataInputStream dis;
DataOutputStream dos;

Mythread(Socket socket, ArrayList al) 
{
     this.socket = socket;
     this.al = al;}

     public void run()
     {
         try{

             String data ="";
             dis = new DataInputStream(socket.getInputStream());
             data = dis.readUTF();
             if(!data.equals("stop"))
             {
                 broadcast(data);
             }
             else
             {
                 dos = new DataOutputStream(socket.getOutputStream());
                 // data = dos.readUTF();
                 dos.writeUTF(data);
                 dos.flush();
                 //dos.close();
             }

         }
         catch(Exception e){
             System.out.println("Run "+e);
         }


   }
   public void broadcast(String data)
   {
       try{
           Iterator it = al.iterator();
           while(it.hasNext())
           {
              Socket socket1 = (Socket)it.next();
               dos = new DataOutputStream(socket1.getOutputStream());
               dos.writeUTF(data);
               dos.flush();


           }
       }
       catch(Exception e){
               System.out.println("Broadcast running "+ e);
       }
   }
}

客户端class

public class Multiclient {

Socket socket;
DataInputStream dis;
DataOutputStream dos;
Multiclient() throws IOException
{
    socket = new Socket("127.0.0.1", 1036);
    System.out.println(socket);
    Mythreadc my = new Mythreadc(socket);
    my.start();
}

客户端中使用的线程class

public class Mythreadc extends Thread{

DataInputStream dis;
DataOutputStream dos;
Socket socket;
Mythreadc(Socket socket)throws IOException
{
    this.socket = socket;}

public void run()
{
    BufferedReader br = null; 
    try{
       br = new BufferedReader(new InputStreamReader (System.in));
        dos = new DataOutputStream(socket.getOutputStream());
      String data = "";
      do{

          data = br.readLine();
          dos.writeUTF(data);
          System.out.println(data);
          dos.flush();
      }
      while(!data.equals("stop"));
    }
    catch(Exception e)
    {
        System.out.println("Client input "+e);
    }
    finally{
        try{
            br.close();
            dis.close();
            dos.close();
        }
        catch(Exception e)
        {
            System.out.println("Closing "+e);
        }
    }
}    
}

对不起,我放了这么长的代码,几乎所有的程序。但我觉得有必要了解问题 lies.I 尝试过的地方,我认为它出在我们在客户端线程中显示写入客户端套接字的数据的部分 class 但我不知道这是什么???

#编辑:忘了说了。客户端在发送消息"Stop"!

时停止

我认为您只是错过了将 当前连接到服务器的套接字用户数组列表 传递给线程

而不是发布你的服务器 Class 你刚刚发布了客户端程序 2 次,

您的服务器Class应该以这种方式构建:-

一旦 ServerClass 收到来自任何客户端的请求,Server Class 应该添加套接字到 ArrayList 并创建新线程并将两者都传递给 MyThread Class

编辑: 您似乎还没有编写用于显示将从服务器获取的数据的代码。

在客户端发送消息您可以简单地在您的客户端Class的Main Mehtod

下的主线程中编写

您实际上需要 Thread 在客户端不是为了发送消息而是为了从服务器收听消息, 因为你永远不知道什么时候 任何人 可以向你发送消息,但你总是知道什么时候 想向连接到此聊天应用程序的任何人发送消息

现在进入编码部分:

客户端Class

public class Multiclient {

Socket socket;
DataInputStream dis;
DataOutputStream dos;
Multiclient() throws IOException
{
    socket = new Socket("127.0.0.1", 1036);
    System.out.println(socket);
    Mythreadc my = new Mythreadc(socket);
    my.start();
    /**
     * Here write out the code for taking input from Standard Console
     */
    BufferedReader br = null; 

   try{
   br = new BufferedReader(new InputStreamReader (System.in));
    dos = new DataOutputStream(socket.getOutputStream());
  String data = "";
  do{

      data = br.readLine();
      dos.writeUTF(data);
      System.out.println(data);
      dos.flush();
  }
  while(!data.equals("stop"));
}
catch(Exception e)
{
    System.out.println("Client input "+e);
}
}

客户端线程

        try{

         String data ="";
         dis = new DataInputStream(socket.getInputStream());
        while(data.equalsIgnorCase("stop")){
         data = dis.readUTF();
         System.out.println("Server Message : "+data);
    }
     }
     catch(Exception e){
         System.out.println("Run "+e);
     }

客户端线程不完整,但我认为这些信息已经足够了。

希望它能帮到你,你的问题确实让我想起了大学时代:)

您的代码有两个问题导致客户端无法显示多条消息。

问题一:您的客户端代码实际上从未显示或打印出它从服务器接收到的消息。行

dos = new DataOutputStream(socket.getOutputStream());

创建一个 OutputStream,您可以使用它来将数据写入套接字,即将消息发送到服务器。但是您永远不会使用套接字的 InputStream,这是您从套接字读取数据所需要做的,即从服务器接收消息。当您看到客户端打印出一条消息时,您实际上只是看到了

的结果
System.out.println(data);

让您的客户端打印它刚刚发送的消息。

为了让客户端同时接受用户的输入和从服务器读取消息,您可能应该在客户端上使用两个线程。一个线程可以只是您已经编写的客户端线程,因为它负责接受来自用户的输入。另一个线程应该是这个样子:

public class ClientReaderThread extends Thread {

Socket socket;

ClientReaderThread(Socket socket) {
    this.socket = socket;
}

public void run() {
    try (BufferedReader serverReader = new BufferedReader(
            new InputStreamReader(socket.getInputStream()))){

        String fromServer = serverReader.readLine();;
        while(fromServer != null) {
             if (fromServer.equals("stop")) 
                break;
            System.out.println(fromServer);
            fromServer = serverReader.readLine();
        }
    } catch (IOException e) {
        System.out.println("Client error! Got exception: " + e);
    }
}

}

(请注意,我使用 try-with-resources 语句构造 reader,它负责在客户端停止时关闭它)。

然后在你的主客户端class,用同一个套接字启动两个线程:

Multiclient() throws IOException
{
    socket = new Socket("127.0.0.1", 1036);
    System.out.println(socket);
    Mythreadc my = new Mythreadc(socket);
    ClientReaderThread reader = new ClientReaderThread(socket);
    my.start();
    reader.start();
}

问题二:您的服务器只从每个客户端读取并回显一行,因为处理每个客户端的套接字线程 (Mythread) 不包含一个循环。通过为每个客户端创建单个线程的设置,run() 每个客户端只会调用一次,因此 run() 方法需要处理客户端发送的每条消息。

服务器线程中的 run() 方法应该如下所示:

public void run() {
    try (BufferedReader inStream = new BufferedReader(
        new InputStreamReader(socket.getInputStream()))){

        String data = inStream.readLine();
        while(data != null) {
            if(data.equals("stop"))
                break;
            broadcast(data);
            data = inStream.readLine();
        }
    }
    catch(Exception e){
        System.out.println("Run exception "+e);
    } finally {
        al.remove(socket); //This is important to do
    }

}

我在这里做了一个额外的重要更改:在 run() 方法的末尾,当客户端断开连接或发生异常时,线程 从 ArrayList 中删除其套接字。 这确保了所有引用同一个 ArrayList 的其他服务器线程不会尝试广播到已断开连接的客户端的套接字。如果您忽略了这一点,当一个客户端在另一个客户端断开连接后向服务器发送消息时,您将得到一个异常。

杂记

  • 正如我在评论中提到的,你应该在线程 class 中给 al 一种类型 ArrayList<Socket>,并使用 for-each 循环而不是 Iterator 来迭代在 broadcast() 中超过它。
  • 我正在使用 BufferedReader 而不是 DataInputStream 从套接字中读取。这是因为 DataInputStream.readUTF()writeUTF() 已被弃用,并已被 BufferedReader.readLine()PrintWriter.println() 取代。
  • disdos 这样的流不需要是线程中的实例变量 classes,因为它们只在 run() 中使用方法。它们可以是 run() 中的局部变量,就像我在新 run() 方法中对 inStream 所做的那样。