如何使用套接字将数据从服务器发送到多个客户端?

3
我有一个简单的服务器客户端程序:
public class Server extends Thread {
    private ServerSocket server;

    public Server(int port) {
        try {
            this.server = new ServerSocket(port);
            System.out.println("New server initialized!");
            this.start();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    public void run() {
        while (true) {
            try {
                Socket client = server.accept();
                System.out.println(client.getInetAddress().getHostName()
                        + " connected");
                new ServerHandler(client);
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }
}

服务器处理程序必须在流中发送消息:

public class ServerHandler extends Thread {
    protected Socket client;
    protected String userInput;
    protected PrintWriter out;
    protected BufferedReader console;

    public ServerHandler(Socket client) {
        this.client = client;
        this.userInput = null;
        this.start();
    }

    public void run() {
        System.out.println("New Communication Thread Started");
        System.out.println("Enter message:");
        try {
            this.out = new PrintWriter(client.getOutputStream(), true);
            this.console = new BufferedReader(new InputStreamReader(System.in));
            while ((this.userInput = console.readLine()) != null) {
                this.out.println(userInput);
            }               
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

客户端接收到该消息:
public class Client {
    protected Socket client;
    protected BufferedReader in;

    public Client(String hostName, int ip) {
        try {
            this.client = new Socket(hostName, ip);
            this.in = new BufferedReader(new InputStreamReader(
                    this.client.getInputStream()));
            String buffer = null;
            while ((buffer = in.readLine()) != null) {
                System.out.println(buffer);
            }
        } catch (UnknownHostException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

它与一个客户端一起工作良好,但当我启动新客户端时,从服务器接收消息的问题出现了。 我做错了什么?


不确定这是否与您的问题有关,但在您的Server类中,您创建了一个新的ServerHandler实例,但没有将其分配给任何东西。由于没有任何引用它,它可以随时被垃圾回收。 - John Sheridan
你需要在服务器上创建一个socket连接的ArrayList。将每个客户端放在一个线程中,然后在服务器上创建一个线程向客户端发送数据。确保在ArrayList上进行同步。 - rush2sk8
@JohnSheridan 这是不正确的。它继承了Thread,并且它的构造函数调用了start(),所以直到它退出之前都不符合GC的条件。 - user207421
@EJP,你说得完全正确。可能是我在梦游。忘记了正在运行的线程被视为GC根并且免受GC影响。感谢你指出这一点。 - John Sheridan
1
更改了代码但仍然有问题。请阅读下面我的回答。 - Ruslan Lomov
1个回答

6
我找到了答案:
public class Server extends Thread {
    private ServerSocket server;
    protected List<ClientHandler> clients;

    public Server(int port) {
        try {
            this.server = new ServerSocket(port);
            System.out.println("New server initialized!");
            clients = Collections
                    .synchronizedList(new ArrayList<ClientHandler>());
            this.start();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
    public void run() {
        while (true) {
            try {
                Socket client = server.accept();
                System.out.println(client.getInetAddress().getHostName()
                        + " connected");
                ClientHandler newClient = new ClientHandler(client);
                clients.add(newClient);
                new SendMessage(clients);

            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }
}

使用ClientHandler类替代Serverhandler类:

public class ClientHandler {
    protected Socket client;
    protected PrintWriter out;

    public ClientHandler(Socket client) {
        this.client = client;
        try {
            this.out = new PrintWriter(client.getOutputStream());
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

可以将线程添加到客户端类中,但即使没有它也能正常工作。我认为这是因为当调用主方法时,会自动创建新的线程:

public class Client {
    protected Socket client;
    protected BufferedReader in;

    public Client(String hostName, int ip) {
        try {
            this.client = new Socket(hostName, ip);
            this.in = new BufferedReader(new InputStreamReader(
                    this.client.getInputStream()));
            String buffer = null;
            while ((buffer = in.readLine()) != null) {
                System.out.println(buffer);
            }
        } catch (UnknownHostException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }       
}

向客户端发送数据的类:

 public class SendMessage extends Thread {
        protected List<ClientHandler> clients;
        protected String userInput;
        protected BufferedReader console;

        public SendMessage(List<ClientHandler> clients) {
            this.clients = clients;
            this.userInput = null;
            this.start();
        }

        public void run() {
            System.out.println("New Communication Thread Started");
            if (clients.size() == 1) {
                System.out.println("Enter message:");
            }
            try {
                if (clients.size() > 0) {
                    this.console = new BufferedReader(new InputStreamReader(
                            System.in));
                    while ((this.userInput = console.readLine()) != null) {
                        if (userInput != null & userInput.length() > 0) {
                            for (ClientHandler client : clients) {
                                client.out.println(userInput);
                                client.out.flush();
                            Thread.currentThread();
                            Thread.sleep(1 * 1000);
                            }
                        }
                    }
                }
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }

现在它可以工作了!需要在刷新数据后使SendMessage线程休眠。服务器启动的主要内容:

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

客户端启动器主要内容:

public static void main(String[] args) {
new Client("127.233.0.1", 1200);
}

1
如果有人需要工作版本,我可以提供Bitbucket链接。 - Ruslan Lomov
很久以前就做过了。这是链接。 - Ruslan Lomov

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接