问题描述
我要做的是第2组客户端,并使它们彼此通信。因此,如果连接了2个客户端,则它们只能彼此通信;如果连接了第三个客户端,则它将无法与其他2个客户端通信,但是它将创建另一个由2个客户端组成的组,依此类推...下面的代码当前将一个消息从一个客户端广播到所有客户端,但是我希望它像上面描述的那样工作,并且一直很难找到解决方案。任何帮助表示赞赏。
public class ChatServer {
private static final int PORT = 9001;
private static HashSet<String> names = new HashSet<String>();
private static HashSet<PrintWriter> writers = new HashSet<PrintWriter>();
static int clientCounter = 0;
public static void main(String[] args) throws Exception {
System.out.println("The chat server is running.");
ServerSocket listener = new ServerSocket(PORT);
try {
while (true) {
new Handler(listener.accept()).start();
}
} finally {
listener.close();
}
}
private static class Handler extends Thread {
private String name;
private Socket socket;
private BufferedReader in;
private PrintWriter out;
public Handler(Socket socket) {
this.socket = socket;
}
public void run() {
try {
in = new BufferedReader(new InputStreamReader(socket.getInputStream()));
out = new PrintWriter(socket.getoutputStream(),true);
while (true) {
out.println("SUBMITNAME");
name = in.readLine();
if (name == null) {
return;
}
synchronized (names) {
if (!names.contains(name)) {
names.add(name);
break;
}
}
}
out.println("NAMEACCEPTED");
clientCounter++;
if (clientCounter > 0 && clientCounter <= 2) {
writers.add(out);
while (true) {
String input = in.readLine();
if (input == null) {
return;
}
for (PrintWriter writer : writers) {
writer.println("MESSAGE " + name + ": " + input);
}
}
} else {
clientCounter = 1;
}
} catch (IOException e) {
System.out.println(e);
} finally {
if (name != null) {
names.remove(name);
}
if (out != null) {
writers.remove(out);
}
try {
socket.close();
} catch (IOException e) {
}
}
}
}
}
解决方法
我能想到的最简单的解决方案是:
创建一个扩展线程并接受两个套接字作为输入的类
此类运行的所有操作是等待将任何内容写入第一个套接字并将其发送到第二个套接字
您的主要功能只是在等待连接
建立连接后,它会接受并保存套接字(我从现在起将其称为socketA),但会继续等待第二个连接(因为您的聊天程序需要两个客户端)
第二次连接出现后,您接受它(这将是socketB)
现在它从一开始就从您的类中创建了两个对象,一个对象以socketA为第一个,socketB为第二个参数,另一个对象以socketB为第一个,而socketA作为第二个参数
第一个实例负责将消息从套接字A发送到套接字B,第二个实例从套接字B发送到套接字A,让我们进行两个聊天
您的主服务器现在只需要等待下一个连接并从头开始“重启”
,这是使其工作的一种方法。首先,您需要修改接受连接的循环,以便跟踪“先前接受的套接字”。当客户端连接时,您检查是否存在previousSocket
:如果没有,则将其当前套接字存储为previousSocket
。如果有一个,则您现在有两个已连接的客户端,它们可以彼此通信:
Socket previousSocket = null;
while (true) {
Socket newSocket = listener.accept();
if (previousSocket == null) {
previousSocket = newSocket;
} else {
new Handler(previousSocket,newSocket).start();
new Handler(newSocket,previousSocket).start();
previousSocket = null;
}
}
您需要进行的另一项更改是在Handler类中添加“对等”的概念。对等体是用于与其他客户端进行通信的套接字。然后,用仅将消息发送到对等方的代码替换将消息发送到所有客户端的for
循环:
peerOut.println("MESSAGE " + name + ": " + input);
这是修改后的Handler类的更完整的代码示例,以进行说明。为了简洁起见,我省略了名称注册和错误处理-您需要将其重新添加。
private static class Handler extends Thread {
private String name;
private Socket socket;
private Socket peerSocket;
private BufferedReader in;
private PrintWriter out;
public Handler(Socket socket,Socket peerSocket) {
this.socket = socket;
this.peerSocket = peerSocket;
}
public void run() {
in = new BufferedReader(new InputStreamReader(socket.getInputStream()));
// name registration and error handling omitted for brevity
PrintWriter peerOut = new PrintWriter(peerSocket.getOutputStream(),true);
while (true) {
String input = in.readLine();
if (input == null) {
return;
}
// Replaces "for (PrintWriter writer : writers) { ... }"
peerOut.println("MESSAGE " + name + ": " + input);
}
}
}