急救.JavaSocket多线程聊天室 奇怪的问题

时间:2021-03-29 10:06:57
我做了这么一个基于Socket的聊天室:
客户端:建立一个Socket,然后(1)将个人信息以对象的形式发给服务器,(2)接收从服务器生成的ID,(3)再接收从服务器发来的在线用户列表.
接下来进入接收消息循环,当接收到一个消息对象时,显示在GUI上,当接收到一个用户列表时,更新GUI上的用户列表显示....

服务器:建立一个ServerSocket,开始监听.每当接到一个请求时,生成一个ID,并用此ID与Socket构造一个客户服务线程.(1)接收客户发来的个人信息(2)向客户端发ID,(3)向所有关客户发更新以后的在线用户列表.

感觉如此就可以了.但现在的问题是:
第二个用户上线时,第一个用户会接收到一个更新后的用户列表,但是这个用户列表仍然只有一条数据.第二个用户接收到两条.
而第三个用户上线时,第一个用户仍然接收只有一条数据的列表,第二个用户接收到只有两条数据的列表.第三个用户3条.
第四个用户上线时,第一个用户是:1条,第二个用户:2条,第三个用户:3条.第四个用户:4条.

我把服务器端的输出和客户端的接收都打印出来了.
显示是:服务器发了正确的数据,但客户端接收到的不正确.但自己的代码查了下找不出问题.所以来求助高人.

因为代码比较长,所以没有贴出来.
有兴趣的可以留个邮箱,我把代码打包发给他.谢谢

18 个解决方案

#1


以前我也有做过

我觉得关键在于 协议

你自己得定下自己的协议

这样双方通信时才能更好得对收到的信息进行解析

#2


mengyingchuxiang@163.com
我看看

#3


梦里楚香职业版主?

#4


可能服务器发送的时候有问题!代码看看
luffyke@qq.com

#5


dajuezhao@gmail.com
我来看看

#6


从client发送给server的同时,要保留此线程的信息;然后再去接收时,要依次将所有的客户端信息接收进来才可以(当然,应排除自己的信息)

#7


引用 3 楼 kamiomisuzu01 的回复:
梦里楚香职业版主?

no,我最近不太忙,老在csdn上逛,打发时间。
谁敢抢版主的饭碗?

#8


LZ定义个消息对象?通过序列化和反序列化该消息对象,拿到对应的消息进行解析
至于出现LZ那样的情况,我觉得应该用个键值对的集合去保存客户端连接上来的套接字,要想往某客户端发消息,只要拿到对应的套接字就行,不会出现这样的情况,我也只是用C#做过聊天软件,相信JAVA差不多的,原理是一样的,LZ也可以联系我的QQ邮箱:jayqean@vip.qq.com

#9


 我是定义了一个DataPackage对象,里面有一个int type和Object data.在发数据时,把消息或用户列表等数据放到data里面.
然后用户列表是用的Vector.上线一个用户时,马上将其加入到vector,然后再调用服务器上的方法sendtoall(而不是每个任务线程中的方法)把这个vector发到每个用户上.

#10


gen.tianfeng@gmail.com

我看看

#11


你这是被动, 

   第一个人的用户列表还没更新呢。
 
  如果在聊天室里面的话, 只要有用户上线或下线你就把当前用户列表广播一下。
  当然前提是要你有自己的协议,当客户端接到时好更新列表

#12


目前只是设计的被动方式.
主要是说服务器里面维护一个Vector,保存着在线用户列表,每上线一个用户后就把这个加到Vector里面,然后再用for循环遍历这个Vector里面的用户,将Vector自身发到每一个用户.

客户端主要是用ObjectInputStream()封装了一个Socket.
 用Switch处理接到的类,如果读出来的是消息,就显示出来.如果是一个Vector,就把它当用户列表看待,直接更新本地的在线用户列表就可以了.
但就是出问题:
第一个用户上线时,会接到一个列表,只有它自己.
第二个用户上线时,接到一个列表,有它自己和第一个用户,而第一个用户此时接到的列表只有第一个用户
第三个用户上线时,接到列表中有它自己,第二个用户和第三个用户,而第二个用户接到的列表中只有第二个用户和第一个用户,第一个用户接到的列表只有第一个用户.
....如此下去.
但是问题在于我在服务器端将发送的消息打印出来了,是对每个用户都发了同一个列表(包含正确的当前在线用户信息).
而客户端接收到数据后,首先将其打印出来,显示的是如上所说的错误数据.

#13


精简版代码如下:(缩减为仅有服务器和客户端)
//Server.java
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.Vector;
public class Server {
Vector<String> v=new Vector<String>();
Vector<Task> tasklist=new Vector<Task>();
ServerSocket  server;
static int count=0;
Server()throws Exception{
server=new ServerSocket(9999);
}
public static void main(String[] args)throws Exception {
Server s=new Server();
s.listen();
}

public void listen()throws Exception{
while(true){
Socket tmp=server.accept();
count++;
Task tmptask=new Task(tmp);
addUser(tmptask,"user"+count);
Thread s=new Thread(tmptask);
s.start();
}
}

public synchronized void addUser(Task t,String u){
this.tasklist.add(t);
this.v.add(u);
}

public synchronized void removeUser(Task t,String u){
this.tasklist.remove(t);
this.v.remove(u);
}
public void sendUserToAll()throws IOException {
Task tmptask;
System.out.println("#######begin send to all######");
for(int i=0;i<tasklist.size();i++){
tmptask=tasklist.get(i);
tmptask.oos.writeObject(v);
System.out.println("send v to the port:"+tmptask.socket.getPort()+" ,the v's data is :");
for(int j=0;j<v.size();j++){
String s=v.get(i);
System.out.println("******"+s);
}

}
System.out.println("#######end  send to all######");
}

class Task implements Runnable{
Task(Socket s)throws Exception{
this.socket=s;
ois=new ObjectInputStream(s.getInputStream());
oos=new ObjectOutputStream(s.getOutputStream());
}
public Socket socket;
public ObjectInputStream ois;
public ObjectOutputStream oos;
public void run(){
try{
sendUserToAll();
}catch(IOException e){
e.printStackTrace();
}
}
}
}

//******************
//Client.java
import java.net.Socket;
import java.util.Vector;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
public class Client {
Socket s;
ObjectInputStream ois;
ObjectOutputStream oos;

Client()throws Exception{
s=new Socket("127.0.0.1",9999);
oos=new ObjectOutputStream(s.getOutputStream());
ois=new ObjectInputStream(s.getInputStream());
       }
public void work()throws Exception {
while(true){
Vector v=(Vector)ois.readObject();
System.out.println("received a vector ,this is the data:");
for(int i=0;i<v.size();i++){
System.out.println("******"+(String)(v.get(i)));
}
}
}
public  static void main(String[] args)throws Exception{
Client c=new Client();
c.work();
}
}
运行结果如下:
运行Client1: java Client
received a vector ,this is the data:
******user1
received a vector ,this is the data:
******user1

再打开一个新的CMD,运行Client2: java Client
received a vector ,this is the data:
******user1
******user2
为什么收到的不是同样的数据
服务器端输出:
#######begin send to all######
send v to the port:2563 ,the v's data is :
******user1
#######end  send to all######

#######begin send to all######
send v to the port:2563 ,the v's data is :
******user1
******user1
send v to the port:2564 ,the v's data is :
******user2
******user2
#######end  send to all######




#14


mark

#15


楼主程序逻辑与功能基本没有问题。
1.笔误一处:
System.out.println("send v to the port:"+tmptask.socket.getPort()+" ,the v's data is :"); 
for(int j=0;j <v.size();j++){ 
String s=v.get(i); 
System.out.println("******"+s); 

应该是:
String s=v.get(j); 

2.第二个用户上线时,接到一个列表,有它自己和第一个用户,而第一个用户此时接到的列表只有第一个用户,以此类推:
本人以为是
Vector v=(Vector)ois.readObject(); 
的问题。
用这种对象传递包括子元素的Vector很难说会有什么结果。估计是和序列化有关。

建议使用更底层的通讯,例如字节流。

简单修改样例如下:

Server:
public void sendUserToAll()throws IOException { 
Task tmptask; 
System.out.println("#######begin send to all######"); 
for(int i=0;i <tasklist.size();i++){ 
tmptask=tasklist.get(i); 
//tmptask.oos.writeObject(v); 
System.out.println("send v to the port:"+tmptask.socket.getPort()+" ,the v's data is :"); 
for(int j=0;j <v.size();j++){ 
tmptask.oos.writeObject((String)v.get(j));
String s = v.get(j);
System.out.println("******"+s); 


Client:
while(true){ 
String v=(Vector)ois.readObject(); 
System.out.println("received a vector ,this is the data:" + v); 





#16


打错了:
Client: 
while(true){ 
String v=(String)ois.readObject(); 
System.out.println("received a vector ,this is the data:" + v); 

#17


jswzjhy@163.com  我也帮你调试看看吧!

#18


这个网上有人帮我改了一下,可以正确运行了.但不知道究竟是为什么.
他改的是:在服务器端的发送数据的语句前加一句重构oos: tmptask.oos=new ObjectOutputStream(tmptask.socket);
然后对应的客户端接收数据前也加一句重构ois:ois=new ObjectInputStream(socket)

这样就可以得到正常运行的结果了.但是却不知道是什么原因.

#1


以前我也有做过

我觉得关键在于 协议

你自己得定下自己的协议

这样双方通信时才能更好得对收到的信息进行解析

#2


mengyingchuxiang@163.com
我看看

#3


梦里楚香职业版主?

#4


可能服务器发送的时候有问题!代码看看
luffyke@qq.com

#5


dajuezhao@gmail.com
我来看看

#6


从client发送给server的同时,要保留此线程的信息;然后再去接收时,要依次将所有的客户端信息接收进来才可以(当然,应排除自己的信息)

#7


引用 3 楼 kamiomisuzu01 的回复:
梦里楚香职业版主?

no,我最近不太忙,老在csdn上逛,打发时间。
谁敢抢版主的饭碗?

#8


LZ定义个消息对象?通过序列化和反序列化该消息对象,拿到对应的消息进行解析
至于出现LZ那样的情况,我觉得应该用个键值对的集合去保存客户端连接上来的套接字,要想往某客户端发消息,只要拿到对应的套接字就行,不会出现这样的情况,我也只是用C#做过聊天软件,相信JAVA差不多的,原理是一样的,LZ也可以联系我的QQ邮箱:jayqean@vip.qq.com

#9


 我是定义了一个DataPackage对象,里面有一个int type和Object data.在发数据时,把消息或用户列表等数据放到data里面.
然后用户列表是用的Vector.上线一个用户时,马上将其加入到vector,然后再调用服务器上的方法sendtoall(而不是每个任务线程中的方法)把这个vector发到每个用户上.

#10


gen.tianfeng@gmail.com

我看看

#11


你这是被动, 

   第一个人的用户列表还没更新呢。
 
  如果在聊天室里面的话, 只要有用户上线或下线你就把当前用户列表广播一下。
  当然前提是要你有自己的协议,当客户端接到时好更新列表

#12


目前只是设计的被动方式.
主要是说服务器里面维护一个Vector,保存着在线用户列表,每上线一个用户后就把这个加到Vector里面,然后再用for循环遍历这个Vector里面的用户,将Vector自身发到每一个用户.

客户端主要是用ObjectInputStream()封装了一个Socket.
 用Switch处理接到的类,如果读出来的是消息,就显示出来.如果是一个Vector,就把它当用户列表看待,直接更新本地的在线用户列表就可以了.
但就是出问题:
第一个用户上线时,会接到一个列表,只有它自己.
第二个用户上线时,接到一个列表,有它自己和第一个用户,而第一个用户此时接到的列表只有第一个用户
第三个用户上线时,接到列表中有它自己,第二个用户和第三个用户,而第二个用户接到的列表中只有第二个用户和第一个用户,第一个用户接到的列表只有第一个用户.
....如此下去.
但是问题在于我在服务器端将发送的消息打印出来了,是对每个用户都发了同一个列表(包含正确的当前在线用户信息).
而客户端接收到数据后,首先将其打印出来,显示的是如上所说的错误数据.

#13


精简版代码如下:(缩减为仅有服务器和客户端)
//Server.java
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.Vector;
public class Server {
Vector<String> v=new Vector<String>();
Vector<Task> tasklist=new Vector<Task>();
ServerSocket  server;
static int count=0;
Server()throws Exception{
server=new ServerSocket(9999);
}
public static void main(String[] args)throws Exception {
Server s=new Server();
s.listen();
}

public void listen()throws Exception{
while(true){
Socket tmp=server.accept();
count++;
Task tmptask=new Task(tmp);
addUser(tmptask,"user"+count);
Thread s=new Thread(tmptask);
s.start();
}
}

public synchronized void addUser(Task t,String u){
this.tasklist.add(t);
this.v.add(u);
}

public synchronized void removeUser(Task t,String u){
this.tasklist.remove(t);
this.v.remove(u);
}
public void sendUserToAll()throws IOException {
Task tmptask;
System.out.println("#######begin send to all######");
for(int i=0;i<tasklist.size();i++){
tmptask=tasklist.get(i);
tmptask.oos.writeObject(v);
System.out.println("send v to the port:"+tmptask.socket.getPort()+" ,the v's data is :");
for(int j=0;j<v.size();j++){
String s=v.get(i);
System.out.println("******"+s);
}

}
System.out.println("#######end  send to all######");
}

class Task implements Runnable{
Task(Socket s)throws Exception{
this.socket=s;
ois=new ObjectInputStream(s.getInputStream());
oos=new ObjectOutputStream(s.getOutputStream());
}
public Socket socket;
public ObjectInputStream ois;
public ObjectOutputStream oos;
public void run(){
try{
sendUserToAll();
}catch(IOException e){
e.printStackTrace();
}
}
}
}

//******************
//Client.java
import java.net.Socket;
import java.util.Vector;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
public class Client {
Socket s;
ObjectInputStream ois;
ObjectOutputStream oos;

Client()throws Exception{
s=new Socket("127.0.0.1",9999);
oos=new ObjectOutputStream(s.getOutputStream());
ois=new ObjectInputStream(s.getInputStream());
       }
public void work()throws Exception {
while(true){
Vector v=(Vector)ois.readObject();
System.out.println("received a vector ,this is the data:");
for(int i=0;i<v.size();i++){
System.out.println("******"+(String)(v.get(i)));
}
}
}
public  static void main(String[] args)throws Exception{
Client c=new Client();
c.work();
}
}
运行结果如下:
运行Client1: java Client
received a vector ,this is the data:
******user1
received a vector ,this is the data:
******user1

再打开一个新的CMD,运行Client2: java Client
received a vector ,this is the data:
******user1
******user2
为什么收到的不是同样的数据
服务器端输出:
#######begin send to all######
send v to the port:2563 ,the v's data is :
******user1
#######end  send to all######

#######begin send to all######
send v to the port:2563 ,the v's data is :
******user1
******user1
send v to the port:2564 ,the v's data is :
******user2
******user2
#######end  send to all######




#14


mark

#15


楼主程序逻辑与功能基本没有问题。
1.笔误一处:
System.out.println("send v to the port:"+tmptask.socket.getPort()+" ,the v's data is :"); 
for(int j=0;j <v.size();j++){ 
String s=v.get(i); 
System.out.println("******"+s); 

应该是:
String s=v.get(j); 

2.第二个用户上线时,接到一个列表,有它自己和第一个用户,而第一个用户此时接到的列表只有第一个用户,以此类推:
本人以为是
Vector v=(Vector)ois.readObject(); 
的问题。
用这种对象传递包括子元素的Vector很难说会有什么结果。估计是和序列化有关。

建议使用更底层的通讯,例如字节流。

简单修改样例如下:

Server:
public void sendUserToAll()throws IOException { 
Task tmptask; 
System.out.println("#######begin send to all######"); 
for(int i=0;i <tasklist.size();i++){ 
tmptask=tasklist.get(i); 
//tmptask.oos.writeObject(v); 
System.out.println("send v to the port:"+tmptask.socket.getPort()+" ,the v's data is :"); 
for(int j=0;j <v.size();j++){ 
tmptask.oos.writeObject((String)v.get(j));
String s = v.get(j);
System.out.println("******"+s); 


Client:
while(true){ 
String v=(Vector)ois.readObject(); 
System.out.println("received a vector ,this is the data:" + v); 





#16


打错了:
Client: 
while(true){ 
String v=(String)ois.readObject(); 
System.out.println("received a vector ,this is the data:" + v); 

#17


jswzjhy@163.com  我也帮你调试看看吧!

#18


这个网上有人帮我改了一下,可以正确运行了.但不知道究竟是为什么.
他改的是:在服务器端的发送数据的语句前加一句重构oos: tmptask.oos=new ObjectOutputStream(tmptask.socket);
然后对应的客户端接收数据前也加一句重构ois:ois=new ObjectInputStream(socket)

这样就可以得到正常运行的结果了.但是却不知道是什么原因.