Java-Day-31( 多用户即时通信系统 —— 无异常退出 + 私聊 + 群发 )

发布时间 2023-07-10 22:01:43作者: 朱呀朱~

Java-Day-31

多用户即时通信系统

无异常退出

  • 问题指出:

    • 客户端输入 9 退出的是输出在控制台的主线程,退出的是主菜单,并没有真正的退出
    • 因为客户端启动后,相当于是开启了一个进程,在这个进程中启动了一个主线程 ( main 线程 ),在 main 主线程中又启动了一个客户端的线程 ( ClientConnectServerThread,即和服务端通信的线程 ),此线程 run 方法是在不断循环的
    • 所以在输入 9 退出了 main 主线程,那个不断循环的 ClientConnectServerThread 并没有退出
  • 解决方法之一:

    • 客户端:

      • 在 main 线程调用方法,给服务器端发送一个退出系统的 message
      • 调用 System.exit(0) // 正常退出,直接关闭掉进程,从而此进程下的所有的线程就都结束了
    • 服务端器:

      • 服务器端和某个客户端通信的线程接收到一个退出系统的 message
      • 把这个线程持有的 socket 关闭
      • 退出该线程的 run 方法 ( 退出 while 循环 ),就相当于退出了这个线程
  • 具体解决实现:

    • 在客户端的 UserClientService 里编写发送退出 message 的方法
      • 最好在获取输出流对象的时候,是从集合中拿到线程 —— 用户 id 得到所属 socket 的线程来获取:ObjectOutputStream oos = new ObjectOutputStream(ManageClientConnectServerThread.getClientConnectServerThread(u.getUserId()).getSocket().getOutputStream());
      • 注意要传对 message 的 type,确保是放进了 setMesType 中
    • 服务端的 ManageClientThreads 中编写移除对应 userId 的线程对象的方法,在 ServerConnectClientThread 中调用此方法,并关闭此与用户连接的 socket,break 退出循环
      • 退出 run 方法,即退出所在的线程

私聊功能

  • 思路:
    • 客户端处,接收用户希望给其他用户聊天的内容,将消息构建成 Message 对象,通过对应 socket 发送给服务器
    • 服务器处,读取想私聊发送的信息,在管理线程的集合中,根据 message 的私聊信息接收者的 id,从而找到对应线程的 socket,然后将 message 对象转发给指定的客户
    • 指定的客户端处,读取 message 消息并显示
  • 客户端 ( 发送方 ):
    • 控制台输入接收方 id 和想发送的信息
    • 为更好区分,新建 MessageOtherClientService 用于编写发送消息的方法,即私聊 / 群聊所用
    • 方法里仍旧在新建存放完 message 后,为发送给服务端,通过用户 id,在集合中找到线程找到 socket 的输出流对象
  • 服务端:
    • serverConnectClientTreads 线程类中,接收如果是普通的信息发送,就根据接收方的 id 得到线程、socket、输出流对象
  • 客户端 ( 接收方 ):
    • UserClientService 判断如果是普通消息的发送,就直接拼接 message 的各个所需属性为字符串加以显示在控制台上

群聊

  • 同私聊的区别就是群聊不是指定对象发送,是直接遍历所有的在线线程

  • 服务端:需要遍历的时候就 get 方法拿到集合中的 HashMap,获取 key 遍历发送 message

  • 客户端:ClientConnectServerThread 线程随时 run 接收群发 Type,直接控制台输出 message 即可