Server/Multiclient program wont send message to all clients

别等时光非礼了梦想. 提交于 2020-02-06 15:52:30

问题


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

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!