JAVA入门到精通-第92讲-山寨QQ项目6-多对多的聊天
QQ聊天项目演示-多对多的聊天

流同时并发的异常;
服务器中转
2/3号只有一个连接;

接收信息:while循环不停地读取;

当1号点开两个窗口时,2/3会共同去争取这个socket;
会出现流同时并发异常;

Socket处理成static不太合理;
启动一个窗口就会占据Socket不放;
-------------
-实现真正的多人聊天,互相不出现错误
-每一个登录的账号独享一份socket
-去掉static
当一个链接达成的时候,
启动线程,
和服务器保持通讯的状态;
做一个类来管理客户端这边的线程;
-新的账号登录,开一个线程ClientConServerThread


-管理线程的类,放入到一个HashMap里面:ManageClientConServerThread;
HashMap<k,v>
k:用户账号,某个账号独享的线程
v:ClientConServerThread

-----------------
-当账号登录成功,创建线程,把线程放入HashMap

--------------------
Qq聊天界面没有必要成为一个线程;本身是不合理的;

通过 管理线程的类,取得了线程,通过线程取得socket,
通过socket,取得了输出流:getOutputStream;
----------------------
给每一个登录账号分配线程;
把通讯的任务交给线程类;
---------------------
-多对多聊天
经测试,后台发送消息正常;
---------------
-显示后台的消息到各自的聊天界面
QqChat.java]
110
110
1
/**2
* 这是与好友聊天的界面3
* 因为客户端,要处于读取的状态,因此我们把它做成一个线程4
*/5
package com.qq.client.view;6
7
import java.awt.event.ActionEvent;8
import java.awt.event.ActionListener;9
import java.io.IOException;10
import java.io.ObjectInputStream;11
import java.io.ObjectOutputStream;12
import java.util.Date;13
import javax.swing.ImageIcon;14
import javax.swing.JButton;15
import javax.swing.JFrame;16
import javax.swing.JPanel;17
import javax.swing.JScrollPane;18
import javax.swing.JTextArea;19
import javax.swing.JTextField;20
import com.qq.client.model.QqClientConServer;21
import com.qq.client.tools.ManageClientConServerThread;22
import com.qq.common.Message;23
import com.qq.common.MessageType;24
25
public class QqChat extends JFrame implements ActionListener {26
JTextArea jta;27
JTextField jtf;28
JButton jb;29
JPanel jp;30
JScrollPane jsp;31
String ownerId;32
String friendId;33
34
// public static void main(String[] args) {35
// new QqChat("小四");36
// }37
38
// 构造函数39
public QqChat(String friendId, String ownerId) {40
this.ownerId = ownerId;41
this.friendId = friendId;42
43
jta = new JTextArea();44
jtf = new JTextField(15);45
jb = new JButton("发送");46
jb.addActionListener(this);47
jp = new JPanel();48
jp.add(jtf);49
jp.add(jb);50
jsp = new JScrollPane(jta);51
52
this.add(jsp, "Center");53
this.add(jp, "South");54
55
this.setTitle(ownerId + " 正在与 " + friendId + " 聊天");56
this.setSize(350, 300);57
this.setIconImage((new ImageIcon("image/qie.jpg")).getImage());58
this.setLocationRelativeTo(null);59
this.setVisible(true);60
}61
62
// 写一个方法,让它显示消息63
public void showMessage(Message m){64
String info=m.getSender()+" 对 "+m.getGetder()+" 说:"+m.getCon()+"\r\n";65
jta.append(info);66
}67
68
@Override69
public void actionPerformed(ActionEvent e) {70
if (e.getSource() == jb) {71
// 如果用户点击了发送按钮72
Message m = new Message();73
m.setMesType(MessageType.message_comm_mes);74
m.setSender(this.ownerId);75
m.setGetder(this.friendId);76
m.setCon(this.jtf.getText());77
m.setSendTime(new Date().toString());78
jta.append(ownerId + " 对 " + friendId + " 说:" + jtf.getText()79
+ "\r\n");80
jtf.setText("");81
// 发送给服务器82
try {83
ObjectOutputStream oos = new ObjectOutputStream(84
ManageClientConServerThread85
.getClientConServerThread(ownerId).getS()86
.getOutputStream());87
oos.writeObject(m);88
} catch (Exception e1) {89
e1.printStackTrace();90
}91
}92
}93
94
// @Override95
// public void run() {96
// while(true){97
// //读取[如果读不到就等待]98
// try {99
// ObjectInputStream ois=new100
// ObjectInputStream(QqClientConServer.s.getInputStream());101
// Message m=(Message)ois.readObject();102
// //显示出来103
// String info=m.getSender()+" 对 "+m.getGetder()+" 说:"+m.getCon()+"\r\n";104
// jta.append(info);105
// } catch (Exception e) {106
// e.printStackTrace();107
// }108
// }109
// }110
}*******************************************************************************
com.qq.client.model
[QqClienConServer.java]
54
54
1
/**2
* 这是客户端连接服务器的后台3
* 功能:客户端与服务器进行数据交互4
*/5
package com.qq.client.model;6
7
import java.io.ObjectInputStream;8
import java.io.ObjectOutputStream;9
import java.net.Socket;10
import com.qq.client.tools.ClientConServerThread;11
import com.qq.client.tools.ManageClientConServerThread;12
import com.qq.common.Message;13
import com.qq.common.User;14
15
public class QqClientConServer {16
public Socket s;17
18
//发送第一次请求19
public boolean sendLoginInfoToServer(Object o){20
boolean b=false;21
22
try {23
s=new Socket("127.0.0.1",9999);24
ObjectOutputStream oos=new ObjectOutputStream(s.getOutputStream());25
oos.writeObject(o);26
27
ObjectInputStream ois=new ObjectInputStream(s.getInputStream());28
Message ms=(Message)ois.readObject();29
//这里就是验证用户登录的地方30
if(ms.getMesType().equals("1")){31
b=true;32
//就创建一个该qq号和服务器端保持通讯连接的线程33
ClientConServerThread ccst=new ClientConServerThread(s);34
//启动该通讯线程35
ccst.start();36
37
ManageClientConServerThread.addClientConServerThread(((User)o).getUserId(), ccst);38
}39
} catch (Exception e) {40
e.printStackTrace();41
} finally{42
}43
return b;44
}45
46
//向服务器发送一个对象47
public void SendInfoToServer(Object o){48
try {49
Socket s=new Socket("127.0.0.1",9999);50
} catch (Exception e) {51
e.printStackTrace();52
} finally{53
}54
}}
*******************************************************************************
[QqClientUser.java]
15
15
1
/**2
* 用户操作逻辑类3
* QqClientUser会调用QqClientConServer的4
* 方法sendLoginInfoToServer向服务器发送5
*/6
package com.qq.client.model;7
8
import com.qq.common.User;9
10
public class QqClientUser {11
//验证用户是否合法12
public boolean checkUser(User u){13
return new QqClientConServer().sendLoginInfoToServer(u);14
}15
}*******************************************************************************
com.qq.client.tools
[ClientConServerThread.java]
58
58
1
/**2
* 这是客户端和服务器端保持通讯的线程3
*/4
package com.qq.client.tools;5
6
import java.io.ObjectInputStream;7
import java.net.Socket;8
import com.qq.client.view.QqChat;9
import com.qq.client.view.QqFriendList;10
import com.qq.common.Message;11
import com.qq.common.MessageType;12
13
public class ClientConServerThread extends Thread{14
private Socket s;15
16
public Socket getS() {17
return s;18
}19
20
public void setS(Socket s) {21
this.s = s;22
}23
24
//构造函数25
public ClientConServerThread(Socket s){26
this.s=s;27
}28
29
public void run(){30
while(true){31
//不停的读取从服务器端发来的消息32
try {33
ObjectInputStream ois=new ObjectInputStream(s.getInputStream());34
Message m=(Message)ois.readObject();35
36
//按消息包的类型来处理不同的包37
if(m.getMesType().equals(MessageType.message_comm_mes)){38
//把从服务器获得的消息,显示到该显示的聊天界面上39
QqChat qqChat=ManageQqChat.getQqChat(m.getGetder()+" "+m.getSender());40
//显示41
qqChat.showMessage(m);42
}else if(m.getMesType().equals(MessageType.message_ret_onLineFriend)){43
String con=m.getCon();44
String []friends=con.split(" ");45
String getter=m.getGetder();46
//修改相应的好友列表47
QqFriendList qqFrindList=ManageQqFriendList.getQqFriendList(getter);48
//更新在线好友49
if(qqFrindList!=null){50
qqFrindList.updateFriend(m);51
}52
}53
} catch (Exception e) {54
e.printStackTrace();55
}56
}57
}58
}*******************************************************************************
[ManageClientConServerThread.java]
x
1
/**2
* 这是一个管理客户端与服务器保持通讯的线程类3
*/4
package com.qq.client.tools;5
6
import java.util.HashMap;7
8
public class ManageClientConServerThread {9
private static HashMap hm=new HashMap<String, ClientConServerThread>();10
//把创建好的ClientConServerThread放入到hm中11
public static void addClientConServerThread(String qqId,ClientConServerThread ccst){12
hm.put(qqId, ccst);13
}14
15
//可以通过qqId取得该线程16
public static ClientConServerThread getClientConServerThread(String qqId){17
return (ClientConServerThread)hm.get(qqId);18
}19
}20









浙公网安备 33010602011771号