day03-拉取在线用户功能
多用户即时通讯系统03
4.编码实现02
4.2功能实现-拉取在线用户


4.2.1思路分析
客户端想要知道在线用户列表,就要向服务器发送请求(Message),因为只有服务器端保持着所有与客户端相连接的socket和uid信息。
整个流程大致为:对Message的种类进行扩展,然后客户端向服务器发送一个Message,服务端向客户端返回数据,客户端接收信息(Message),在接收的这个Message信息里面包含了在线的用户列表。
4.2.2代码实现
1.客户端:
1.修改:MessageType接口
拓展了一些Message消息类型
package qqcommon; /** * @author 李 * @version 1.0 * 表示消息类型 */ public interface MessageType { //在接口中定义类一些常量,不同的常量的表示不同的消息类型 String MESSAGE_LOGIN_SUCCEED = "1";//表示登录成功 String MESSAGE_LOGIN_FAIL = "2";//表示登录失败 String MESSAGE_COMM_MES = "3";//表示普通信息包 String MESSAGE_GET_ONLINE_FRIEND = "4";//要求返回在线用户列表 String MESSAGE_RET_ONLINE_FRIEND = "5";//返回的在线用户列表 String MESSAGE_CLIENT_EXIT = "6";//客户端请求退出 }
2.修改:UserClientService类
在该类中添加onlineFriendList()方法,该方法向服务器发送要求,请求在线用户列表
//先服务器端请求在线用户列表 public void onlineFriendList() { //向服务器发送一个Message,类型MESSAGE_GET_ONLINE_FRIEND,要求返回在线用户列表 Message message = new Message(); message.setMesType(MessageType.MESSAGE_GET_ONLINE_FRIEND); message.setSender(u.getUserId()); //发送给服务器 try { //从管理线程的集合里面,通过userId,得到这个线程对象 ClientConnectServerThread clientConnectServerThread = ManageClientConnectServerThread.getClientConnectServerThread(u.getUserId()); //通过这个线程中获取关联的socket Socket socket = clientConnectServerThread.getSocket(); //得到当前线程的Socket对应的ObjectOutputStream对象 ObjectOutputStream oos = new ObjectOutputStream(socket.getOutputStream()); oos.writeObject(message);//发送一个Message对象向服务器,要求在线用户列表 } catch (IOException e) { e.printStackTrace(); } }
3.修改:ClientConnectServerThread类
在该类中的run方法中,增加判断Message的类型,然后做相应的业务处理
@Override public void run() { //因为Thread需要在后台和服务器通信,因此我们使用while循环 while (true) { try { System.out.println("客户端线程,等待读取从服务端发送的消息"); ObjectInputStream ois = new ObjectInputStream(socket.getInputStream()); //如果服务器没有发送Message对象,线程会阻塞在这里 Message message = (Message) ois.readObject(); //判断Message的类型,然后做相应的业务处理 //如果读取到的是 服务端返回的在线用户列表(MESSAGE_RET_ONLINE_FRIEND) if (message.getMesType().equals(MessageType.MESSAGE_RET_ONLINE_FRIEND)) { //取出在线用户列表信息,并展示 //这里假定返回的用户列表是用空格隔开的id名(如:100 200 紫霞仙子 至尊宝 唐僧) String[] onlineUsers = message.getContent().split(" "); System.out.println("\n=======当前在线用户列表======="); for (int i = 0; i < onlineUsers.length; i++) { System.out.println("用户:" + onlineUsers[i]); } } else { System.out.println("读取到的是其他类型的message,暂时不处理"); } } catch (Exception e) { e.printStackTrace(); } } }
4.修改QQView类
修改该类中的第54行
使用上述编写的方法,完成拉取在线用户列表的操作
//这里写一个拉取用户在线列表的方法 userClientService.onlineFriendList();

2.服务端
大致思路为:前面我们在服务端的线程的run方法中,使用for循环在不停地请求读取数据,这里我们可以在run方法里面拓展功能,读取客户端发送过来的拉取用户的Message对象,返回在线用户列表
1.修改:MessageType接口
和客户端一样,拓展Message消息类型
package qqcommon; /** * @author 李 * @version 1.0 * 表示消息类型 */ public interface MessageType { //在接口中定义类一些常量 //不同的常量的表示不同的消息类型 String MESSAGE_LOGIN_SUCCEED = "1";//表示登录成功 String MESSAGE_LOGIN_FAIL = "2";//表示登录失败 String MESSAGE_COMM_MES = "3";//表示普通信息包 String MESSAGE_GET_ONLINE_FRIEND = "4";//要求返回在线用户列表 String MESSAGE_RET_ONLINE_FRIEND = "5";//返回的在线用户列表 String MESSAGE_CLIENT_EXIT = "6";//客户端请求退出 }
2.修改:ServerConnectClientThread类
在该类中的run方法添加了客户请求拉取在线用户列表 的业务操作
package qqserver.server; import qqcommon.Message; import qqcommon.MessageType; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; import java.net.Socket; /** * @author 李 * @version 1.0 * 该类的一个对象和某个客户端保持通信 */ public class ServerConnectClientThread extends Thread { private Socket socket; private String userId;//连接到服务端的用户id public ServerConnectClientThread(Socket socket, String userId) { this.socket = socket; this.userId = userId; } @Override public void run() {//这里线程处于run的状态,可以发送/接收消息 while (true) { try { System.out.println("服务端和客户端" + userId + "保持通信,读取数据..."); ObjectInputStream ois = new ObjectInputStream(socket.getInputStream()); Message message = (Message) ois.readObject(); //后面会使用message,根据message的类型,做相应的业务处理 //业务-:客户请求拉取在线用户列表 if (message.getMesType().equals(MessageType.MESSAGE_GET_ONLINE_FRIEND)) { //客户请求拉取在线用户列表 //假定返回的用户列表是用空格隔开的id名(如:100 200 紫霞仙子 至尊宝 唐僧) System.out.println(message.getSender()+" 要在线用户列表"); String onlineUser = ManageClientThreads.getOnlineUser(); //返回message //构建一个Message对象(这个Message对象包含了在线用户列表信息),返回给客户端 Message message2 = new Message(); //设置消息类型--返回的在线用户列表类型-客户端会根据返回的消息类型来进行相应的业务处理 message2.setMesType(MessageType.MESSAGE_RET_ONLINE_FRIEND); message2.setContent(onlineUser);//返回用户消息列表 //服务器发送的消息的接收者Getter 就是服务器接收的信息 的发送者Sender message2.setGetter(message.getSender()); //返回给客户端 ObjectOutputStream oos = new ObjectOutputStream(socket.getOutputStream()); oos.writeObject(message2); }else{ System.out.println("其他类型的message,暂时不处理"); } } catch (Exception e) { e.printStackTrace(); } } } }
3.修改:ManageClientThreads类
在该类中增加了getOnlineUser()方法,用来遍历取出userId,并在上面的ServerConnectClientThread的run方法中使用。
//这里编写方法,可以返回在线用户列表 public static String getOnlineUser() { //遍历集合,遍历 hashmap 的 key Iterator<String> iterator = hm.keySet().iterator();//获取hm集合的ketSet集合的迭代器(这里的ketSet就是userId) String onlineUserList = ""; while (iterator.hasNext()) {//遍历 onlineUserList += iterator.next().toString() + " ";//遍历所有的userId,用空格拼接起来 } return onlineUserList; }
运行:
- 运行服务端:

-
分别运行三个客户端,并进行登录,拉取在线用户:
用户1:
用户2:
用户3:
此时服务端显示:

【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· 单线程的Redis速度为什么快?
· SQL Server 2025 AI相关能力初探
· AI编程工具终极对决:字节Trae VS Cursor,谁才是开发者新宠?
· 展开说说关于C#中ORM框架的用法!