如何让我的多线程服务器/客户端聊天程序使用套接字向所有客户端回显消息? 客户端:服务器端:注意事项:编辑 1:完整的示例代码

如何解决如何让我的多线程服务器/客户端聊天程序使用套接字向所有客户端回显消息? 客户端:服务器端:注意事项:编辑 1:完整的示例代码

现在我有一个 Java 程序,它使用线程和套接字来像真正的聊天窗口一样回显文本响应。目前,我的程序通过运行服务器而不是我想要的尽可能多的客户端来工作。当客户端输入消息时,该消息会回显到服务器以及发送该消息的客户端。

我的问题是,我希望任何客户端输入的消息不仅发送到服务器和他们自己,还发送到所有其他客户端。

这是它目前的工作原理:

服务器:

收到客户端消息:test1

客户端 1:

输入消息:test1

测试 1

客户端 2:

输入消息:

客户端1进入test1,收到test1,服务器也收到test1。客户端 2 一无所获。我的目标是让输入到客户端的任何消息显示在发送消息的客户端以及其他客户端和服务器上。

工作示例:

服务器:

收到客户端消息:test1

收到客户端消息:你好

客户端 1:

输入消息:test1

测试 1

来自客户 2:你好

客户端 2:

输入消息:

来自客户端 1:test1

你好

格式不必完全如此,但这就是想法。到目前为止,我的代码如下。我读过我需要将我的客户添加到列表中,然后遍历它们并将所有消息发送给他们,但我不确定。任何帮助都会很棒。

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.net.Socket;
import java.net.SocketTimeoutException;
import java.util.Scanner;

public class EchoMultiThreadClient {

    public static void main(String[] args) {
        try (Socket socket = new Socket("localhost",4000)) {
            
            //socket.setSoTimeout(5000);
            BufferedReader br = new BufferedReader(
                    new InputStreamReader(socket.getInputStream()));
            PrintWriter pw = new PrintWriter(socket.getOutputStream(),true);

            Scanner scanner = new Scanner(System.in);
            String echoString;
            String response;

            do {
                System.out.println("Enter string to be echoed: ");
                echoString = scanner.nextLine();

                pw.println(echoString);
                if(!echoString.equals("exit")) {
                    response = br.readLine();
                    System.out.println(response);
                }                          
                
                
            } while(!echoString.equals("exit"));
            
       // }catch(SocketTimeoutException e) {
        //  System.out.println("The Socket has been timed out");

        } catch (IOException e) {
            System.out.println("Client Error: " + e.getMessage());

        }
    }
    
}   

服务器代码

    import java.io.BufferedReader;
    import java.io.IOException;
    import java.io.InputStreamReader;
    import java.io.PrintWriter;
    import java.net.ServerSocket;
    import java.net.Socket;
import java.util.Vector;

    public class EchoMultiThreadServer {
        private static Vector<Echoer> clients = new Vector<Echoer>();
        public static void main(String [] args) {
            try(ServerSocket serverSocket = new ServerSocket(4000)){
                while(true) {                                           
                        
                         Socket socket = serverSocket.accept();
                         Echoer echoer = new Echoer(socket);
                         echoer.start(); 
                         clients.add(echoer);

                        } 
                    }catch(IOException e) {
                        System.out.println("Server Exception"+e.getMessage());
                }
                
        }
}

线程代码

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

public class Echoer extends Thread{
    
    private Socket socket;
    public Echoer(Socket socket) {
        this.socket = socket;
    }
    
    @Override
    public void run() {
        try {
            BufferedReader in = new BufferedReader(new InputStreamReader(socket.getInputStream()));
            PrintWriter wr = new PrintWriter(socket.getOutputStream(),true);
            
            while(true) {
                
                String echoString = in.readLine();
                System.out.println("Received Client Input: " + echoString);
                if(echoString.equals("exit")) {
                    break;
                }

                wr.println(echoString);
            }
        }catch(IOException e) {
            System.out.println("Oooops " + e.getMessage());
        }finally {
            try {
                socket.close();
            }catch(IOException e) {
                // later
            }
            
        }
        
    }

}

解决方法

我发现您当前的逻辑存在两个问题:

  1. 在客户端,您实际上是在读取用户输入,然后发送到服务器并获得(单个)响应。所以这里的问题是你只能得到一个响应,而你应该为每个用户输入行获取多个响应:即用户的输入加上其他用户的输入。由于您不知道其他用户的输入何时以及有多少,因此您需要异步。我的意思是你需要 2 个线程:一个用于读取用户输入,另一个用于读取服务器输入/响应(注意:我们仍然在客户端)。由于您已经拥有 2 个线程之一,即运行 main 方法的线程,因此您可以使用它而不是创建一个新线程。
  2. 在服务器端,您的 Echoer 正在读取用户输入,但仅将其发送回同一个客户端。例如,您还需要一个循环来将客户端的输入发送给所有其他客户端。

那么在我看来正确的逻辑是:

客户端:

读取服务器的响应线程逻辑:

forever,do:
    get server's message.
    print server's message to user.

main 方法:

connect to server.
start a "Reading server's responses thread".
get user input.
while the user's input it not "exit",do:
    send user's input to server.
    get user input.
disconnect from server.

服务器端:

Echoer 线程:

forever,do:
    read client's message.
    for every client,do:
        send the message to the client.

main 方法:

start server socket.
forever,do:
    accept incoming connection.
    start an Echoer thread for the accepted connection.

虽然有一些缺失的部分,例如如何维护所有客户端的列表,但我可以看到您已经在服务器端使用了 Vector<Echoer> clients。因此,只需将 Vector 传递给您创建的每个 Echoer,这样它们就可以广播每个传入的消息。 重要说明:在服务器端,您有多个线程:主线程和每个 Echoer,因此请确保在修改时在 Vector 上同步它在主线程以及在 Echoer 上广播时。


注意事项:

  1. 我假设在上述所有逻辑中,客户端发送消息没有特定的顺序。例如,如果总是客户端 A 先发送,然后客户端 B 等等,并且整个过程不断重复,那么您根本不需要使用多线程。
  2. 请慢慢来。首先实施它,然后告诉我您是否遇到任何问题。

编辑 1:完整的示例代码。

客户端代码:

import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.net.Socket;
import java.nio.charset.StandardCharsets;
import java.util.Scanner;

public class Client {
    
    //This is the "Reading server's responses thread" I am talking about in the answer.
    private static class ReadingRunnable implements Runnable {
        
        private final BufferedReader serverInput;
        
        public ReadingRunnable(final InputStream is) {
            serverInput = new BufferedReader(new InputStreamReader(is,StandardCharsets.UTF_8));
        }
        
        @Override
        public void run() {
            try {
                //While the server is not disconnected,we print each line to 'System.out':
                for (String line = serverInput.readLine(); line != null; line = serverInput.readLine())
                    System.out.println(line);
            }
            catch (final IOException iox) {
                iox.printStackTrace(System.out);
            }
            finally {
                System.out.println("Input from server stopped.");
            }
        }
    }
    
    public static void main(final String[] args) {
        try {
            System.out.print("Connecting... ");
            try (final Socket sck = new Socket("localhost",50505);
                 final OutputStream os = sck.getOutputStream();
                 final InputStream is = sck.getInputStream()) {
                System.out.println("Connected.");
                new Thread(new ReadingRunnable(is)).start();
                final BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(os,StandardCharsets.UTF_8));
                final Scanner scan = new Scanner(System.in);
                for (String userInput = scan.nextLine(); !"exit".equalsIgnoreCase(userInput); userInput = scan.nextLine()) {
                    bw.write(userInput);
                    bw.newLine();
                    bw.flush();
                }
            }
        }
        catch (final IOException iox) {
            iox.printStackTrace(System.out);
        }
        finally {
            System.out.println("Output from user stopped.");
        }
    }
}

服务器代码:

import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.net.ServerSocket;
import java.net.Socket;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.Objects;

public class Server {
    
    private static class Echoer implements Runnable {
        private final ArrayList<Echoer> all;
        private final BufferedWriter bw;
        private final BufferedReader br;
        
        public Echoer(final ArrayList<Echoer> all,final InputStream is,final OutputStream os) {
            this.all = Objects.requireNonNull(all);
            bw = new BufferedWriter(new OutputStreamWriter(os,StandardCharsets.UTF_8));
            br = new BufferedReader(new InputStreamReader(is,StandardCharsets.UTF_8));
        }
        
        //Instead of exposing 'bw' via a getter,I just built a helper method to send a message to the Echoer:
        public void send(final String msg) throws IOException {
            bw.write(msg);
            bw.newLine();
            bw.flush();
        }
        
        @Override
        public void run() {
            try {
                for (String line = br.readLine(); line != null; line = br.readLine()) {
                    System.out.println(line); //Print the received line at the server.
                    synchronized (all) { //We are reading from a collection which may be modified at the same time by another (the main) Thread,so we need to synchronize.
                        //Broadcast the received line:
                        for (int i = all.size() - 1; i >= 0; --i) {
                            try {
                                all.get(i).send(line);
                            }
                            catch (final IOException iox) {
                                all.remove(i); //In case we cannot send to the client,disconnect him,ie remove him from the list in this simple case.
                            }
                        }
                    }
                }
            }
            catch (final IOException iox) {
            }
            finally {
                synchronized (all) {
                    all.remove(this); //Disconnect him,ie remove him from the list in this simple case.
                }
                System.out.println("Client disconnected.");
            }
        }
    }
    
    public static void main(final String[] args) throws IOException {
        System.out.print("Starting... ");
        try (final ServerSocket srv = new ServerSocket(50505)) {
            final ArrayList<Echoer> all = new ArrayList<>();
            System.out.println("Waiting for clients...");
            while (true) {
                final Socket sck = srv.accept();
                try {
                    final OutputStream os = sck.getOutputStream();
                    final InputStream is = sck.getInputStream();
                    final Echoer e = new Echoer(all,is,os); //Pass all the Echoers at the new one.
                    synchronized (all) { //We will write to a collection which may be accessed at the same time by another (an Echoer) Thread,so we need to synchronize.
                        all.add(e); //Update list of Echoers.
                    }
                    new Thread(e).start(); //Start serving Echoer.
                }
                catch (final IOException iox) {
                    System.out.println("Failed to open streams for a client.");
                }
            }
        }
    }
}

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。

相关推荐


依赖报错 idea导入项目后依赖报错,解决方案:https://blog.csdn.net/weixin_42420249/article/details/81191861 依赖版本报错:更换其他版本 无法下载依赖可参考:https://blog.csdn.net/weixin_42628809/a
错误1:代码生成器依赖和mybatis依赖冲突 启动项目时报错如下 2021-12-03 13:33:33.927 ERROR 7228 [ main] o.s.b.d.LoggingFailureAnalysisReporter : *************************** APPL
错误1:gradle项目控制台输出为乱码 # 解决方案:https://blog.csdn.net/weixin_43501566/article/details/112482302 # 在gradle-wrapper.properties 添加以下内容 org.gradle.jvmargs=-Df
错误还原:在查询的过程中,传入的workType为0时,该条件不起作用 &lt;select id=&quot;xxx&quot;&gt; SELECT di.id, di.name, di.work_type, di.updated... &lt;where&gt; &lt;if test=&qu
报错如下,gcc版本太低 ^ server.c:5346:31: 错误:‘struct redisServer’没有名为‘server_cpulist’的成员 redisSetCpuAffinity(server.server_cpulist); ^ server.c: 在函数‘hasActiveC
解决方案1 1、改项目中.idea/workspace.xml配置文件,增加dynamic.classpath参数 2、搜索PropertiesComponent,添加如下 &lt;property name=&quot;dynamic.classpath&quot; value=&quot;tru
删除根组件app.vue中的默认代码后报错:Module Error (from ./node_modules/eslint-loader/index.js): 解决方案:关闭ESlint代码检测,在项目根目录创建vue.config.js,在文件中添加 module.exports = { lin
查看spark默认的python版本 [root@master day27]# pyspark /home/software/spark-2.3.4-bin-hadoop2.7/conf/spark-env.sh: line 2: /usr/local/hadoop/bin/hadoop: No s
使用本地python环境可以成功执行 import pandas as pd import matplotlib.pyplot as plt # 设置字体 plt.rcParams[&#39;font.sans-serif&#39;] = [&#39;SimHei&#39;] # 能正确显示负号 p
错误1:Request method ‘DELETE‘ not supported 错误还原:controller层有一个接口,访问该接口时报错:Request method ‘DELETE‘ not supported 错误原因:没有接收到前端传入的参数,修改为如下 参考 错误2:cannot r
错误1:启动docker镜像时报错:Error response from daemon: driver failed programming external connectivity on endpoint quirky_allen 解决方法:重启docker -&gt; systemctl r
错误1:private field ‘xxx‘ is never assigned 按Altʾnter快捷键,选择第2项 参考:https://blog.csdn.net/shi_hong_fei_hei/article/details/88814070 错误2:启动时报错,不能找到主启动类 #
报错如下,通过源不能下载,最后警告pip需升级版本 Requirement already satisfied: pip in c:\users\ychen\appdata\local\programs\python\python310\lib\site-packages (22.0.4) Coll
错误1:maven打包报错 错误还原:使用maven打包项目时报错如下 [ERROR] Failed to execute goal org.apache.maven.plugins:maven-resources-plugin:3.2.0:resources (default-resources)
错误1:服务调用时报错 服务消费者模块assess通过openFeign调用服务提供者模块hires 如下为服务提供者模块hires的控制层接口 @RestController @RequestMapping(&quot;/hires&quot;) public class FeignControl
错误1:运行项目后报如下错误 解决方案 报错2:Failed to execute goal org.apache.maven.plugins:maven-compiler-plugin:3.8.1:compile (default-compile) on project sb 解决方案:在pom.
参考 错误原因 过滤器或拦截器在生效时,redisTemplate还没有注入 解决方案:在注入容器时就生效 @Component //项目运行时就注入Spring容器 public class RedisBean { @Resource private RedisTemplate&lt;String
使用vite构建项目报错 C:\Users\ychen\work&gt;npm init @vitejs/app @vitejs/create-app is deprecated, use npm init vite instead C:\Users\ychen\AppData\Local\npm-