我正在尝试创建一个多客户聊天服务器,其中我们有多个客户端连接到服务器以及客户端输入的任何消息,它会显示给所有客户端(包括发送消息的客户端)。我没有得到这个输出,而是消息只响应发送方客户端而没有其他客户端。代码很长,因此我显示了我认为可以帮助您理解错误的代码片段。如果还不够,只需评论您需要的部分。提前致谢。我大约一个半小时就坚持这个,所以我很感激能得到的任何帮助。
服务器类
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:忘了提。客户端在发送消息“停止”时停止!
答案 0 :(得分:1)
您的代码存在两个问题,即阻止客户端显示多条消息。
问题一:您的客户端代码实际上从未显示或打印出从服务器收到的消息。这条线
dos = new DataOutputStream(socket.getOutputStream());
创建一个OutputStream,可用于将数据写入套接字,即将消息发送到服务器。但是你永远不会使用套接字的InputStream,这是从套接字读取数据所需要做的,即从服务器接收消息。当您在客户端上看到打印出的消息时,您实际上只是看到了
的结果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循环代替Iterator在{{1}中迭代它}}。 broadcast()
而不是BufferedReader
来读取套接字。这是因为DataInputStream
和DataInputStream.readUTF()
已弃用,已被writeUTF()
和BufferedReader.readLine()
取代。 PrintWriter.println()
和dis
这样的流不需要是线程类中的实例变量,因为它们只在dos
方法中使用。它们可以是run()
中的局部变量,就像我在新run()
方法中使用inStream
一样。答案 1 :(得分:-1)
我认为您错过了将当前连接到服务器的套接字用户的ArrayList 传递给线程
而不是发布您的服务器类您刚刚发布了2次客户端程序,
您的 ServerClass 应该以这种方式构建: -
只要 ServerClass 收到来自任何客户端的请求,服务器类应该将Socket添加到ArrayList并创建新线程并将两者都传递给MyThread类
修改: 您似乎还没有编写代码来显示您将从服务器获取的数据。
在客户端发送消息您可以在客户端类主要方面
下的主线程中编写简单的内容您实际上在客户端需要线程而不是用于发送消息,而是用于从服务器侦听消息, 因为您从未知道任何人何时可以向您发送消息,但您始终知道您何时希望向与此聊天应用相关联的任何人发送消息
现在来到编码部分:
客户等级
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);
}
客户端线程不完整,但我认为这些信息足够了。
希望它可以帮助你,你的问题让我想起大学时代:)