[英]How do I get my multithreaded server/client chat program to echo messages to all clients using sockets?
right now I have a java program that uses threads and sockets to echo text responses like a real chat window.现在我有一个 java 程序,它使用线程和 sockets 来回显文本响应,例如真实聊天 window。 Currently, my program works by running the server and than as many clients as I want.目前,我的程序通过运行服务器而不是我想要的任意数量的客户端来工作。 When a client enters a message, that message is echoed to the server and also to the client that sent the message.当客户端输入消息时,该消息会回显到服务器以及发送消息的客户端。
My problem is that I want the message any client enters to be sent not only to the server and to themselves, but to every other client as well.我的问题是我希望任何客户端输入的消息不仅发送到服务器和他们自己,而且发送到其他所有客户端。
Heres how it currently works:以下是它目前的工作方式:
Server:服务器:
Received client message: test1收到客户端消息:test1
Client 1:客户 1:
Enter message: test1输入消息:test1
test1测试1
Client 2:客户 2:
Enter message:输入消息:
Client 1 enters test1, receives test1 back and the server also receives test1.客户端 1 进入 test1,收到 test1,服务器也收到 test1。 Client 2 gets nothing.客户 2 一无所获。 My goal is to have any messages entered in the clients display on the client that sent the message as well as the other clients and server.我的目标是让在客户端中输入的任何消息都显示在发送消息的客户端以及其他客户端和服务器上。
Working example:工作示例:
Server:服务器:
Received client message: test1收到客户端消息:test1
Received client message: hello收到客户端消息:你好
Client 1:客户 1:
Enter message: test1输入消息:test1
test1测试1
From client 2: hello来自客户 2:你好
Client 2:客户 2:
Enter message:输入消息:
From client 1: test1从客户端 1:test1
hello你好
The formatting doesnt have to be exactly like that, but thats the idea.格式不必完全一样,但这就是想法。 My code so far is below.到目前为止,我的代码如下。 Ive read that I need to add my clients to a list and then loop over them and send them all the message but im not sure.我读过我需要将我的客户添加到列表中,然后遍历它们并将所有消息发送给他们,但我不确定。 Any help would be great.任何帮助都会很棒。
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());
}
}
}
server code服务器代码
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());
}
}
}
thread code线程代码
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
}
}
}
}
I can see two problems with your current logic:我可以看到您当前的逻辑存在两个问题:
main
method, then you can use it instead of creating a new one.由于您已经拥有 2 个线程之一,即运行main
方法的线程,那么您可以使用它而不是创建一个新线程。Echoer
is reading user input but only sending it back to the same client.在服务器端,您的Echoer
正在读取用户输入,但仅将其发送回同一个客户端。 You need for example a loop to send the client's input to all other clients too.例如,您还需要一个循环来将客户端的输入发送给所有其他客户端。So what would seem to me a proper logic is:所以在我看来,正确的逻辑是:
Reading server's responses thread logic:读取服务器的响应线程逻辑:
forever, do: get server's message. print server's message to user.
main
method: 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
thread: Echoer
线程:
forever, do: read client's message. for every client, do: send the message to the client.
main
method: main
方法:
start server socket. forever, do: accept incoming connection. start an Echoer thread for the accepted connection.
There are some missing bits though, such as how to maintain the list of all clients, but for that I can see you are already using a Vector<Echoer> clients
at the server side.虽然有一些缺失的部分,例如如何维护所有客户端的列表,但为此我可以看到您已经在服务器端使用Vector<Echoer> clients
端。 So just pass that Vector
to every Echoer
you create, so they can do the broadcasting of each incomming message.所以只需将该Vector
传递给您创建的每个Echoer
,这样他们就可以广播每个传入的消息。 Important note here : at the server side, you have more than one threads: the main one and each Echoer
, so make sure you synchronize on the Vector
while you are modifying it at the main thread and also while broadcasting at the Echoer
s.重要说明:在服务器端,您有多个线程:主线程和每个Echoer
,因此请确保在主线程修改 Vector 以及在Echoer
广播时在Vector
上同步。
Client code:客户端代码:
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.");
}
}
}
Server code:服务器代码:
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.");
}
}
}
}
}
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.