问题
I'm working on a program involving a multithreaded server, in which I want messages sent by clients to be echoed back to every client currently connected to the server. It doesn't exactly do this. I will send a message from a client to the server, and it will echo back to that same client. Not to the other client. Let's say, with one client I sequentially type "One" then "Two" then "Three". The exchange will be something like this:
Client 1: "One"
Echo from Server ON Client 1's console: "One"
Client 1: "Two"
Echo from Server ON Client 1's console: "Two"
Client 1: "Three"
Echo from Server ON Client 1's console: "Three"
This part does what it should. But absolutely nothing happens on Client 2's console. Let's say the exchange above has already happened. Client 2's screen will still be blank. I will then type something in Client 2, let's say "Test". The server will respond to Client 2 with "One". Let's say I type "Test" again in Client 2. The server will respond with "Two". You get the idea. I'm not sure why it's doing this. I have three files involved, The Client, The Server, and one meant to manage connections between them.
EDIT: I THINK I KNOW THE PROBLEM! On line 43 in client, the console expects some user input before it will proceed. Which I THINK is why when the first client sends user input, it gets a correct reply, but the second one doesn't: because the second client didn't enter anything in the console, and it's still waiting for some input in order to proceed. Any ideas on how to work around this?
Client:
package client;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.net.Socket;
import java.net.UnknownHostException;
import java.util.Scanner;
public class Client {
//The socket for the client
Socket sock;
//The stream to read incoming data
DataInputStream din;
//The stream to send outgoing data
DataOutputStream dout;
public static void main(String[] args) {
//Create a new client
new Client();
}
public Client() {
try {
//Activate the socket to the host and port
sock = new Socket("localhost", 4444);
//Open the input and output streams
din = new DataInputStream(sock.getInputStream());
dout = new DataOutputStream(sock.getOutputStream());
//Start listening for user input
listenIn();
} catch (UnknownHostException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
public void listenIn() {
//Monitors the console for user input
Scanner userIn = new Scanner(System.in);
while(true) {
//While there is nothing left to read from the console
while(!userIn.hasNextLine()) {
try {
//Ensures resources aren't constantly being used by listening for input
Thread.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
//Get line from user input
String input = userIn.nextLine();
//if user exits the client, break the loop and exit the program
if(input.toLowerCase().equals("quit")) {
break;
}
try {
//outputs user input to Server
dout.writeUTF(input);
//Flushes all data out of the data output stream's buffer space
dout.flush();
//While there's nothing to read from the input stream, save resources
while(din.available() == 0) {
try {
Thread.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
//When there's incoming data, print it to the console
String reply = din.readUTF();
System.out.println(reply);
} catch (IOException e) {
e.printStackTrace();
break;
}
}
//Close all the I/O streams and sockets, so there aren't memory leaks
try {
din.close();
dout.close();
sock.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
Server:
package server;
import java.io.IOException;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.ArrayList;
public class Server {
//The server's socket
ServerSocket sSock;
ArrayList<ServerConnection> connections = new ArrayList<ServerConnection>();
boolean run = true;
public static void main(String[] args) {
//Create a new server
new Server();
}
public Server() {
try {
//Initialize the server socket to the correct port
sSock = new ServerSocket(4444);
//While the socket should be open
while(run) {
//Initialize the client socket to the correct port
Socket sock = sSock.accept();
//Create a new server connection object between the client socket and the server
ServerConnection sConn = new ServerConnection(sock, this);
//Start the thread
sConn.start();
//Add the connection to the arraylist
connections.add(sConn);
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
Server Connection:
package server;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.net.Socket;
public class ServerConnection extends Thread{
Socket sock;
Server server;
DataInputStream in;
DataOutputStream out;
boolean run = true;
//Create the server connection and use super to run it with Thread's constructor
public ServerConnection(Socket socket, Server server) {
super("ServerConnectionThread");
this.sock = socket;
this.server = server;
}
public void sendOne(String text) {
try {
//Write the text to the output stream
out.writeUTF(text);
//Flush the remaining data out of the stream's buffer space
out.flush();
} catch (IOException e) {
e.printStackTrace();
}
}
//Send a string to every client
public void sendAll(String text) {
/*Iterate through all of the server connections in the server
and send the text to every client*/
for(int i = 0; i < server.connections.size(); i++) {
ServerConnection sc = server.connections.get(i);
sc.sendOne(text);
}
}
public void run() {
try {
//Set the input stream to the input from the socket
in = new DataInputStream(sock.getInputStream());
//Set the output stream to write out to the socket
out = new DataOutputStream(sock.getOutputStream());
//While the loop should be running (as determined by a boolean value)
while(run) {
//While there is no incoming data, sleep the thread to save resources
while(in.available() == 0) {
try {
Thread.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
//Store the incoming data in a string
String textIn = in.readUTF();
//Send it to all clients
sendAll(textIn);
}
//Close datastreams and socket to prevent memory leaks
in.close();
out.close();
sock.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
回答1:
Like you have done in the server side, you may use a separate thread to take care of incoming data in the client side. That way, the waiting for the user input in the console will not block the incoming data flow.
Here is an idea of how you could implement this.
New ClientConnection:
package client;
import java.io.DataInputStream;
import java.io.IOException;
import java.net.Socket;
import java.net.SocketException;
public class ClientConnection extends Thread {
DataInputStream din = null;
public ClientConnection(Socket socket) throws IOException {
this.setName("Client-Thread");
this.din = new DataInputStream(socket.getInputStream());
}
public void run() {
boolean run = true;
while (run) {
// While there's nothing to read from the input stream, save resources
try {
// When there's incoming data, print it to the console
String reply = din.readUTF();
System.out.println(reply);
run = this.isAlive();
} catch (SocketException e) {
System.out.println("Disconnected");
run = false;
} catch (IOException e) {
e.printStackTrace();
}
}
try {
din.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
And here is the reformulated Client:
package client;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.net.Socket;
import java.net.UnknownHostException;
import java.util.Scanner;
public class Client {
// The socket for the client
Socket sock;
// The stream to send outgoing data
DataOutputStream dout;
public static void main(String[] args) {
// Create a new client
new Client();
}
public Client() {
try {
// Activate the socket to the host and port
sock = new Socket("localhost", 4444);
// Open the input and output streams
dout = new DataOutputStream(sock.getOutputStream());
//Listening for incoming messages
ClientConnection client = new ClientConnection(sock);
client.start();
// Start listening for user input
listenIn();
} catch (UnknownHostException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
public void listenIn() {
// Monitors the console for user input
Scanner userIn = new Scanner(System.in);
while (true) {
// While there is nothing left to read from the console
while (!userIn.hasNextLine()) {
try {
// Ensures resources aren't constantly being used by listening for input
Thread.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
// Get line from user input
String input = userIn.nextLine();
// if user exits the client, break the loop and exit the program
if (input.toLowerCase().equals("quit")) {
break;
}
try {
// outputs user input to Server
dout.writeUTF(input);
// Flushes all data out of the data output stream's buffer space
dout.flush();
} catch (IOException e) {
e.printStackTrace();
break;
}
}
// Close all the I/O streams and sockets, so there aren't memory leaks
try {
dout.close();
sock.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
On the server side, you may also consider removing the disconnected clients from the list of connections:
public class ServerConnection extends Thread {
...
public void run() {
try {
...
} catch (SocketException e) {
System.out.println("Client disconnected");
server.connections.remove(this);
} catch (IOException e) {
e.printStackTrace();
}
}
}
I hope this helps.
来源:https://stackoverflow.com/questions/52452782/server-multiclient-program-wont-send-message-to-all-clients