Java-Day-30( 多用户即时通信系统 —— 登录 + 获取在线用户列表 )
Java-Day-30
多用户即时通信系统
- 需求分析
- 用户登录
- 拉取在线用户列表
- 无异常退出
- 私聊
- 群聊
- 发文件
- 服务器推送新闻
用户登录
-
功能说明
- 我们暂时人为规定用户名 / id = 100,密码 123456 就可以登录,其他用户不能登录
- 后面使用 HashMap 模拟数据库,可以多个用户登录
-
思路分析
- 为方便,客户端 A 的 socket 输出流发送数据时使用对象方式 ( 即使用对象流来读写 ) ,由服务端的 9999 端口监听,监听 accept 产生一个输入流 socket
- 一个对象希望用 Object 对象流的方式进行读写的话,此对象所对应的类需要序列化 —— implements Serializable ( IO 流部分 ),并且为保证兼容性需要加上 —— private static final long serialVersionUID = 1L; —— 防止在随机分配时,若类修改,反序列化报错
- 如果多个客户端访问的话,服务器端就产生多个 socket
- 服务器端验证 User 对象,验证通过后才能到后面的发送信息等功能
- 每获取一个 socket 就多一个线程,一个线程包裹着一个 socket,也可以说 socket 就是该线程的属性,只一个 socket 不方便维护
- 为更好的管理线程,两端都需要使用集合,即多个线程都包裹在一个管理线程的集合中 ( 为了扩展,就算是只有一个 socket 也最好写到线程集合中 )
- 每对 socket 对应一个通道 ( 两端之间也可能有多条通道 )
- 为方便,客户端 A 的 socket 输出流发送数据时使用对象方式 ( 即使用对象流来读写 ) ,由服务端的 9999 端口监听,监听 accept 产生一个输入流 socket
- 注意:客户端的线程中只有 socket,而服务端的线程中不仅要有 socket,还要有用于区分的客户端传来的身份验证的 userId
前提准备
-
服务端和客户端都共有文件:Message、User 映射实体类,MessageType 内含各种信息类型的常量的接口
-
信息 Message 包含所有发送的东西,例如客户向服务器发送请求类型是存在 Message 里,服务端将客户请求到的信息也存到 Message 中,即凡是要两端信息交互,就是通过 Message 来存储发送的
-
接口中:
// 在接口中定义了一些常量 // 不同的常量的值,表示不同的消息类型 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"; // 客户端请求退出
-
用户登录 —— 客户端
- 为了用户信息可发送到服务端
- QQView 为登录页面简单样式
- UserClientService 为用户登录验证、用户注册等具体方法所在处
- 如果登录成功,就将 socket 存入线程,线程启动并存入线程集合,关于线程与集合的具体如下
- ClientConnectServerThread 为一个持有 Socket 的线程,始终保持着与服务端的连接,时刻处于可接收状态
- 为了能够管理 socket,否则客户端多个 socket 时会很麻烦,而且只能做一件事情
- 减缓了反复链接的延迟和资源浪费等
- ManageClientConnectServerThread 类中,我们将线程放入到集合管理 ( 如上图所画 )
- 主要是为了后面客户端的扩展,如果以后和服务端只会有一个连接的话,此处就不需要集合管理了
- 内就仅含一个 HashMap、一个 ( id, 线程 ) 存放方法、一个根据 id 获取线程的方法
用户登录 —— 服务端
- 为了验证客户端发来的信息,随后服务端发送信息对象给用户以登录结果
- QQServer 中用 ServerSocket 来监听端口并获取 Socket
- 端口可以写在配置文件中
- 注意:监听也同样是一个循环不断的操作,并不是获取到一个 Socket 就结束了
- 获取输入流得到客户端发送的 User 对象进行验证
- 再根据 Socket 创建输出流,用来根据验证结果发送 Message 对象给客户端进行回复
- 登录成功就将 socket 存入线程,线程启动并存入线程集合,关于线程与集合的具体如下
- QQServer 中用 ServerSocket 来监听端口并获取 Socket
- ServerConnectClientThread 似过程一的 ClientConnectServerThread 持有 socket 的线程,用于始终和对应用户保持连接
- ManageClientThreads 管理线程类,似过程一的 ManageClientConnectServerThread
- 若是没链接数据库想要临时测试,可以在 QQServer 中使用 HashMap / ConcurrentHashMap 集合于静态代码块中存储用户信息
拉取在线用户列表
-
不管是客户端还是服务端都是有一个线程在不停执行 run 方法,在此方法中,不断接收对方的 Message 对象,根据传来的 Message 对象的类型来执行进一步的操作
-
在客户端的 UserClientService 中编写向服务端请求在线用户列表的方法,在需要时 QQView 在合适的位置调用即可
-
在 ClientConnectServerThread 线程中判断服务端发送回来的信息类型,若接收到的是服务端发来的用户列表,就在线程中直接控制台打印出用户列表即可,在需要时直接在 QQView 页面调用即可
- split 分割为数组,循环输出
-
服务端获取在线用户列表:所有在线的用户 id 都存放在 socket 中,socket 都包在一个线程中,而每个线程都在一个线程集合中,所以想要获取所有的在线用户,就需要通过管理线程的集合来获取,即在 ManageClientThreads 里编写获取列表的方法
- HashMap 的遍历,可以用迭代器,
- 服务端的线程中判断客户传来的 Message 类型,若是,就通过线程集合的方法获取所有的用户信息,拿到后将所需的类型、发送者、内容 ( 即用户信息 ) 等打包,输出流返回给客户端即可
分类:
java学习
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· 阿里巴巴 QwQ-32B真的超越了 DeepSeek R-1吗?
· 【译】Visual Studio 中新的强大生产力特性
· 【设计模式】告别冗长if-else语句:使用策略模式优化代码结构
· 10年+ .NET Coder 心语 ── 封装的思维:从隐藏、稳定开始理解其本质意义