简体   繁体   中英

How to send a message through all Threads?

At the moment i have a Server and a Client, and when the Client is connected to the Server, a Thread is created to handle all the resposnses from the respective Client and also to send any needed answers. My problem now is that i need to be able to send a message through every existent Thread to their respective Client.

I was thinking of doing it like this:

public class ServerThread extends Thread {
    //ignore most of the constructor, just things i need
    public ServerThread(Socket socket, int threadId, Manager manager) throws Exception {
        try {
            this.socket = socket;
            this.threadId=threadId;
            this.manager=manager;
            in = new BufferedReader(new InputStreamReader(socket.getInputStream()));
            out = new BufferedWriter(new OutputStreamWriter(socket.getOutputStream()));
            manager.addThread(); //This should add this Thread to the Collection in the Manager class      
        } catch (IOException ex) {
            throw new Exception("Error", ex);
        }
    }


    public void notify(String message){
        // Do something
    }

    //In the end of the thread i would call manager.removeThread to remove the Thread from the Collection

}

public class Manager {

    private //Thread Collection here

    public Manager(){
         //Initialize the collection;
    }

    public void addThread(){
        //Add thread
    }

    public void removeThread(){
        //Remove Thread
    }
}

If this is a viable option to handle this, what Collection would i need to store the Threads and also, what would the notify(String message) method look like? It would need to call a method in Manager that would send a message to every Thread right?

If you want to create a multi-client server what is usually recommended is that in the main thread (or a separate thread) of the server class, the server will be accepting incoming Sockets (client) and with every socket accepted a new thread is created to service that client and it is better to have the service as a separate class that implements runnable or extends thread. Each service thread will be waiting for input from the client it is associated with and replying according to the client's request.

If you are looking to broadcast data to all the connected clients then what you need is to have an ArrayList that stores the client service objects and then loop over it, with every loop you send data to one of the connected clients but you have to make sure that you remove the clients that disconnected from the ArrayList otherwise it will start throwing exceptions.

usually, client service classes have the accepted socket, an input stream, and an output stream.

here is an example of a multiclient echo server that I have made maybe it will help.

public class TcpServer {

public TcpServer(){
    ServerSocket server = null;
    try{
        server = new ServerSocket(9991);
        while(!server.isClosed()){
            Socket acceptedSocket = server.accept();
            EchoService service = new EchoService(acceptedSocket);
            service.start();

        }
    }catch (IOException e){
        e.printStackTrace();
    } finally {
        if(server!=null) {
            try {
                server.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
}

public static void main(String[] args){
    new TcpServer();
}}

This is the service class:

public class EchoService extends Thread {
private Socket acceptedSocket;
private DataInputStream is;
private DataOutputStream os;

public EchoService(Socket acceptedSocket) {
    try {
        this.acceptedSocket = acceptedSocket;
        is = new DataInputStream(acceptedSocket.getInputStream());
        os = new DataOutputStream(acceptedSocket.getOutputStream());
    } catch (IOException e) {
        try {
            if (this.acceptedSocket != null)
                acceptedSocket.close();
            if(is != null)
                is.close();
            if(os != null)
                os.close();
        } catch (IOException e1) {
            e1.printStackTrace();
        }
    }
}


@Override
public void run() {
    super.run();
    try {
        while (!acceptedSocket.isClosed()) {
            String usrMsg = is.readUTF();
            String serverMsg = "server: "+usrMsg;
            os.writeUTF(serverMsg);
            os.flush();
        }
    } catch (IOException e) {
        try {
            if(this.acceptedSocket != null)
                acceptedSocket.close();
            if(is != null)
                is.close();
            if(os != null)
                os.close();
        } catch (IOException e1) {
            e1.printStackTrace();
        }
    }
}}

This is the same example but with the Broadcast feature included

Server class:

package TCP;

import java.io.IOException;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.ArrayList;

public class TcpServer {
public static ArrayList<EchoService> connectedServices;

public TcpServer(){
    ServerSocket server = null;
    try{
        server = new ServerSocket(9991);
        System.out.println("server started");
        connectedServices = new ArrayList<>();
        while(!server.isClosed()){
            Socket acceptedSocket = server.accept();
            System.out.println("client connected: " 
            +acceptedSocket.getInetAddress());
            EchoService service = new EchoService(acceptedSocket);
            service.start();

        }
    }catch (IOException e){
        e.printStackTrace();
    } finally {
        if(server!=null) {
            try {
                server.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
}

public static void main(String[] args){
    new TcpServer();
}

public static void removeConnectedService(EchoService client) {
    boolean removed = connectedServices.remove(client);
    System.out.println("client has been removed"+ 
    client.getAcceptedSocket().getInetAddress()+", "+removed);
}

public static void broadCastMsg(long id, String usrMsg) throws IOException {
    for(EchoService client: connectedServices){
        if(client.getId()!=id)
        {
            String serverMsg = "server broadcast: " + usrMsg;
            client.getOs().writeUTF(serverMsg);
            client.getOs().flush();
        }
    }

}
}

service class:

    package TCP;

import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.net.Socket;

public class EchoService extends Thread {
    private Socket acceptedSocket;
    private DataInputStream is;
    private DataOutputStream os;

    public EchoService(Socket acceptedSocket) {
        try {
            this.acceptedSocket = acceptedSocket;
            is = new DataInputStream(acceptedSocket.getInputStream());
            os = new DataOutputStream(acceptedSocket.getOutputStream());

        } catch (IOException e) {
            try {
                if (this.acceptedSocket != null)
                    acceptedSocket.close();
                if(is != null)
                    is.close();
                if(os != null)
                    os.close();
            } catch (IOException e1) {
                e1.printStackTrace();
            }
        }
    }


    @Override
    public void run() {
        super.run();
        try {
            TcpServer.connectedServices.add(this);
            while (!acceptedSocket.isClosed()) {
                String usrMsg = is.readUTF();
                if(usrMsg.contains("BROADCAST"))
                    TcpServer.broadCastMsg(this.getId(),usrMsg);
                else {
                    String serverMsg = "server: " + usrMsg;
                    os.writeUTF(serverMsg);
                    os.flush();
                }
            }
        } catch (IOException e) {
            TcpServer.removeConnectedService(this);
            try {
                if(this.acceptedSocket != null)
                    acceptedSocket.close();
                if(is != null)
                    is.close();
                if(os != null)
                    os.close();
            } catch (IOException e1) {
                e1.printStackTrace();
            }
        }
    }

    public DataInputStream getIs() {
        return is;
    }

    public DataOutputStream getOs() {
        return os;
    }

    public Socket getAcceptedSocket() {
        return acceptedSocket;
    }
}

Server output:

在此处输入图片说明

client 1 output:

在此处输入图片说明

client 2 output:

在此处输入图片说明

client 3 output:

在此处输入图片说明

I would create a static method getInstance(int threadId) in ServerThread.

Inside this, you create a syncronized and static Map (see class Collections).

In notify just navigate over the map and send your messages to your ServerThread instances.

(note: if it's a TreMap it will be sorted by the key)

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM