我正在尝试创建一个多客户端聊天类型的服务器,其中我们有多个客户端连接到服务器,无论客户端输入什么消息,它都会显示给所有客户端(包括发送消息的客户端)。我没有收到此输出,相反,消息仅在发送方客户端上回显,而没有其他客户端。代码很长,因此我显示我认为可以帮助您理解错误的任何代码片段。如果这还不够,只需评论您需要哪一部分。提前谢谢。我从大约一个半小时开始就被困在这个问题上,所以我感谢我能得到的任何帮助。
服务器类
public class Multiserver {
ServerSocket serversocket;
Socket socket;
ArrayList<Socket> al = new ArrayList<Socket>();
DataInputStream dis;
DataOutputStream dos;
Multiserver() throws IOException
{
serversocket = new ServerSocket(1036);
System.out.println("Server started on port 1036");
while(true)
{
socket = serversocket.accept();
System.out.println(socket);
al.add(socket);
Mythread thread = new Mythread(socket, al);
thread.start();
}
}
服务器类中使用的线程
public class Mythread extends Thread{
Socket socket;
ArrayList al;
DataInputStream dis;
DataOutputStream dos;
Mythread(Socket socket, ArrayList al)
{
this.socket = socket;
this.al = al;}
public void run()
{
try{
String data ="";
dis = new DataInputStream(socket.getInputStream());
data = dis.readUTF();
if(!data.equals("stop"))
{
broadcast(data);
}
else
{
dos = new DataOutputStream(socket.getOutputStream());
// data = dos.readUTF();
dos.writeUTF(data);
dos.flush();
//dos.close();
}
}
catch(Exception e){
System.out.println("Run "+e);
}
}
public void broadcast(String data)
{
try{
Iterator it = al.iterator();
while(it.hasNext())
{
Socket socket1 = (Socket)it.next();
dos = new DataOutputStream(socket1.getOutputStream());
dos.writeUTF(data);
dos.flush();
}
}
catch(Exception e){
System.out.println("Broadcast running "+ e);
}
}
}
客户端类
public class Multiclient {
Socket socket;
DataInputStream dis;
DataOutputStream dos;
Multiclient() throws IOException
{
socket = new Socket("127.0.0.1", 1036);
System.out.println(socket);
Mythreadc my = new Mythreadc(socket);
my.start();
}
客户端类中使用的线程
public class Mythreadc extends Thread{
DataInputStream dis;
DataOutputStream dos;
Socket socket;
Mythreadc(Socket socket)throws IOException
{
this.socket = socket;}
public void run()
{
BufferedReader br = null;
try{
br = new BufferedReader(new InputStreamReader (System.in));
dos = new DataOutputStream(socket.getOutputStream());
String data = "";
do{
data = br.readLine();
dos.writeUTF(data);
System.out.println(data);
dos.flush();
}
while(!data.equals("stop"));
}
catch(Exception e)
{
System.out.println("Client input "+e);
}
finally{
try{
br.close();
dis.close();
dos.close();
}
catch(Exception e)
{
System.out.println("Closing "+e);
}
}
}
}
很抱歉,我放了这么长的代码,几乎所有的程序。但我觉得有必要了解问题出在哪里。我已经尝试过,我认为它位于我们在客户端线程类中显示写入客户端套接字中的数据的部分,但我不知道它是什么???
#EDIT:忘了提。当客户端发送消息"停止"时,他会停止!
代码存在两个问题,导致客户端无法显示多条消息。
问题一:客户端代码从未实际显示或打印出从服务器接收的消息。该行
dos = new DataOutputStream(socket.getOutputStream());
创建一个输出流,可用于将数据写入套接字,即将消息发送到服务器。但是你从不使用套接字的输入流,这是你需要做的,从套接字读取数据,即从服务器接收消息。当您看到客户端上打印出的消息时,您实际上只是看到
System.out.println(data);
这让您的客户端打印它刚刚发送的消息。
为了使客户端能够同时接受来自用户的输入并读取来自服务器的消息,您可能应该在客户端上使用两个线程。一个线程可以只是您已经编写的客户端线程,因为它负责接受来自用户的输入。另一个线程应如下所示:
public class ClientReaderThread extends Thread {
Socket socket;
ClientReaderThread(Socket socket) {
this.socket = socket;
}
public void run() {
try (BufferedReader serverReader = new BufferedReader(
new InputStreamReader(socket.getInputStream()))){
String fromServer = serverReader.readLine();;
while(fromServer != null) {
if (fromServer.equals("stop"))
break;
System.out.println(fromServer);
fromServer = serverReader.readLine();
}
} catch (IOException e) {
System.out.println("Client error! Got exception: " + e);
}
}
}
(请注意,我使用 try-with-resources 语句来构造读取器,它负责在客户端停止时关闭它)。
然后在主客户端类中,使用相同的套接字启动两个线程:
Multiclient() throws IOException
{
socket = new Socket("127.0.0.1", 1036);
System.out.println(socket);
Mythreadc my = new Mythreadc(socket);
ClientReaderThread reader = new ClientReaderThread(socket);
my.start();
reader.start();
}
问题二:您的服务器只读取和回显每个客户端的一行,因为处理每个客户端(Mythread
)的套接字线程不包含循环。通过设置为为每个客户端创建单个线程,每个客户端仅调用一次run()
,因此run()
方法需要处理客户端发送的每条消息。
服务器线程中的 run()
方法应如下所示:
public void run() {
try (BufferedReader inStream = new BufferedReader(
new InputStreamReader(socket.getInputStream()))){
String data = inStream.readLine();
while(data != null) {
if(data.equals("stop"))
break;
broadcast(data);
data = inStream.readLine();
}
}
catch(Exception e){
System.out.println("Run exception "+e);
} finally {
al.remove(socket); //This is important to do
}
}
我在这里做了一个额外的重要更改:在 run()
方法结束时,当客户端断开连接或发生异常时,线程会从 ArrayList 中删除其套接字。这可确保其他服务器线程(它们都引用相同的 ArrayList)不会尝试广播到已断开连接的客户端的套接字。如果忽略执行此操作,则当客户端在另一个客户端断开连接后向服务器发送消息时,将出现异常。
杂项说明
- 正如我在评论中提到的,您应该在线程类中
al
提供一种ArrayList<Socket>
,并使用for-each循环而不是迭代器在broadcast()
中对其进行迭代。 - 我正在使用
BufferedReader
而不是DataInputStream
从套接字读取。这是因为DataInputStream.readUTF()
和writeUTF()
已被弃用,并已替换为BufferedReader.readLine()
和PrintWriter.println()
。 - 像
dis
和dos
这样的流不需要是线程类中的实例变量,因为它们只在run()
方法中使用。它们可以是run()
内部的局部变量,就像我在新的run()
方法中对inStream
所做的那样。
我想你只是错过了将当前连接到服务器的套接字用户的数组列表传递给线程
而不是发布你的服务器类,你刚刚发布了客户端程序 2 次,
您的服务器类应该以这种方式构建:-
一旦 ServerClass 收到来自任何客户端的请求,服务器类就应该将套接字添加到 ArrayList 中并创建新线程,并将两者传递给 MyThread 类
编辑:似乎您还没有编写代码来显示您将从服务器获得的数据。
在客户端发送消息 您可以在客户端类的主线程下简单地编写它
您实际上需要在客户端使用 Thread 不是为了发送消息,而是为了侦听来自服务器的消息,因为您永远不知道何时有人可以向您发送消息,但您将始终知道何时要向连接到此聊天应用程序的任何人发送消息
现在来到编码部分:
客户端类
public class Multiclient {
Socket socket;
DataInputStream dis;
DataOutputStream dos;
Multiclient() throws IOException
{
socket = new Socket("127.0.0.1", 1036);
System.out.println(socket);
Mythreadc my = new Mythreadc(socket);
my.start();
/**
* Here write out the code for taking input from Standard Console
*/
BufferedReader br = null;
try{
br = new BufferedReader(new InputStreamReader (System.in));
dos = new DataOutputStream(socket.getOutputStream());
String data = "";
do{
data = br.readLine();
dos.writeUTF(data);
System.out.println(data);
dos.flush();
}
while(!data.equals("stop"));
}
catch(Exception e)
{
System.out.println("Client input "+e);
}
}
客户端线程
try{
String data ="";
dis = new DataInputStream(socket.getInputStream());
while(data.equalsIgnorCase("stop")){
data = dis.readUTF();
System.out.println("Server Message : "+data);
}
}
catch(Exception e){
System.out.println("Run "+e);
}
客户端线程不完整,但我认为这些信息已经足够了。
希望对您有所帮助,您的问题确实让我想起了大学时代:)