多用户通信系统
项目开发流程
多用户通信需求
通信系统整体分析
qq用户登录
1
拉取在线用户(client端)
public class ClientConnectServerThread extends Thread{
//该线程需要持有Socket
private Socket socket;
//构造器接受一个socket
public ClientConnectServerThread(Socket socket){
this.socket = socket;
}
public void run(){
//因为Thread需要在后台和服务器通信,所以while循环
while (true){
try {
System.out.println("客户端线程,等待读取从服务器端发送的消息");
ObjectInputStream ois = new ObjectInputStream(socket.getInputStream());
Message message = (Message) ois.readObject();//如果服务器没有发送Message对象,那么线程将会进入阻塞状态
//后面将使用message
//判断message类型,然后做相应的业务逻辑处理
//如果读取到的是服务器返回的在线列表
if (message.getMesType().equals(MyssageType.MESSAGE_RET_ONLINE_FRIEND)){
//取出在线列表,并显示
//规定
String[] OnlineUsers = message.getContent().split(" ");
System.out.println("\n========当前在线用户列表========");
for (int i = 0; i < OnlineUsers.length ; i++){
System.out.println("用户: "+OnlineUsers[i]);
}
}else{
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
//更方便得到Socket
public Socket getSocket() {
return socket;
}
}
public class UserClientServise {
//因为我们在别的地方也要用到User,所以设为属性
private User u = new User();
//Socket 同理
private Socket socket;
//根据userId 和pwd 到服务器验证该用户是否合法
public boolean checkUser(String userid , String pwd){
boolean b =false;
//创建User对象
u.setUserid(userid);
u.setPassword(pwd);
//连接到服务器,发送u对象
try {
Socket socket = new Socket(InetAddress.getByName("127.0.0.1"), 9999);//try 快捷键 Ctrl+Alt+t
//得到ObjectOutputStream对象
ObjectOutputStream oos = new ObjectOutputStream(socket.getOutputStream());
oos.writeObject(u); //发送User对象
//读取从服务器回复的Message对象
ObjectInputStream ois = new ObjectInputStream(socket.getInputStream());
Message ms = (Message) ois.readObject();
if (ms.getMesType().equals(MyssageType.MESSAGE_LOGIN_SUCCEED)){
//成功
//创建一个和服务器端保持通信的线程—>创建类 ClientConnectServerThread
ClientConnectServerThread clientConnectServerThread = new ClientConnectServerThread(socket);
//启动客户端线程
clientConnectServerThread.start();
//这里为了后面客户端的扩展,我们将线程放入集合进行管理
ManageClientConnectServerThread.addclientConnectServerThread(userid,clientConnectServerThread);
b = true;
}else {
//如果失败,我们就不能启动和服务器通信的线程,关闭socket
socket.close();
}
} catch (Exception e) {
e.printStackTrace();
}
return b;
}
//向服务器请求在线用户列表
public void onlineFriends(){
//发送一个message 类型是MESSAGE_GET_ONLINE_FRIEND
Message message = new Message();
message.setMesType(MyssageType.MESSAGE_GET_ONLINE_FRIEND);
message.setSender(u.getUserid());
try {
//得到当前socket对应的ObjectOutputStream对象
ObjectOutputStream oos = new ObjectOutputStream(ManageClientConnectServerThread.getclientConnectServerThread(u.getUserid())
.getSocket().getOutputStream());
oos.writeObject(message);//发送一个Message对象,向服务器要求在在线列表
//判断这个message类型,然后做相应的业务处理
} catch (IOException e) {
e.printStackTrace();
}
}
}
拉取在线用户(Server端)
package qqserver.server;
import java.util.HashMap;
import java.util.Iterator;
/**
* @author lyq
* @version 1.0
* 管理和客户端通信的线程
*/
public class MangerServerConnectClientThread {
//线程放入HashMap中,k是id,v是线程
private static HashMap<String,ServerConnetClientThread> hm = new HashMap<>();
//添加线程进入集合
public static void addServerConnectClientThread(String userId ,ServerConnetClientThread serverConnetClientThread){
hm.put(userId,serverConnetClientThread);
}
//根据用户名查找线程
public static ServerConnetClientThread getserverConnetClientThread(String userId){
return hm.get(userId);
}
public static String getOnlineUsers() {
//集合遍历,遍历hashmap中的key
Iterator<String> iterator = hm.keySet().iterator();
String onlineUserList = " ";
while(iterator.hasNext()){
onlineUserList += iterator.next().toString()+" ";
}
return onlineUserList;
}
}
package qqserver.server;
import qqcommon.Message;
import qqcommon.MyssageType;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.net.Socket;
/**
* @author lyq
* @version 1.0
* 该类的一个对象和某个客户端保持通信
*/
public class ServerConnetClientThread extends Thread{
Socket socket;
String userId; //区分和哪个客户端保持连接
public ServerConnetClientThread(Socket socket, String userId) {
this.socket = socket;
this.userId = userId;
}
@Override
public void run() { //线程处于运行状态,可以接受/发送消息
while(true){
try {
System.out.println("服务端和客户端"+userId+"保持通信,读取数据");
ObjectInputStream ois = new ObjectInputStream(socket.getInputStream());
Message message =(Message) ois.readObject();
//后面使用Message
if(message.getMesType().equals(MyssageType.MESSAGE_GET_ONLINE_FRIEND)){
//客户端要在线用户列表
System.out.println(message.getSender()+" 要用户在线列表");
String onlineUsers = MangerServerConnectClientThread.getOnlineUsers();
//返回message
Message message2 = new Message();
message2.setMesType(MyssageType.MESSAGE_RET_ONLINE_FRIEND);
message2.setContent(onlineUsers);
message2.setGetter(message.getSender());
//写入数据通道,返回给客户端
ObjectOutputStream oos = new ObjectOutputStream(socket.getOutputStream());
oos.writeObject(message2);
}else {
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
}
无异常退出
主线程退出了,但是子线程还在运行,导致进程没有真正退出
解决方法
给出了思路,结果自己的纰漏
1 进程应该是在客户端退出
2 服务端中收到退出线程的消息之后,管理线程的集合中也要把线程删掉
3 还要退出服务端的while 循环
私聊功能实现
功能说明
思维导图
实现漏洞
接收的content 应该是在界面中实现
看图向明白整个过程,首先是客户端给服务端,然后服务端再次输出,客户端在读取到
群发功能实现
功能实现
漏洞
总是忘记在菜单上还有定义输入的变量,Map方法的遍历 itit 还不熟悉 要重新复习看
发文件
框架
漏洞
不记得了文件的上传和读取怎么写了
要先明确 是要把 存放文件的字节数组 要转换撑message
最后保存文件的时候,先把message中的字节输出取出来
在写方法时,注意是设置类型和传入的值