多用户即时通讯系统03

4.编码实现02

4.3功能实现-无异常退出系统

4.3.1思路分析

上述代码运行时,在客户端选择退出系统的时候,可以发现程序并没有停止运行,原因是:

退出时,程序将循环标志loop设为false,退出了内层循环,而外层循环因为也用了loop来作为循环条件,外层循环也同样退出。此时在客户端 类QQView中的主线程已经结束,但是在循环过程中,因为与服务端连接而产生的线程并没有结束,整个进程也就没有结束,因此程序仍在运行中。

解决方法:

客户端:在main线程中调用方法,给服务端发送一个退出系统的message对象,然后调用System.exit(0)指令,正常退出。这样整个进程就可以关闭。

服务器端:在服务器这边,接收到一个退出系统的message对象后,把这个客户端对应的线程所持有的socket关闭,然后退出该线程

4.3.2代码实现

1.客户端:
1.修改:UserClientService类

在该类中增加logout()方法

//编写方法,退出客户端,并给服务器端发送一个退出系统的message对象
public void logout(){
Message message = new Message();
message.setMesType(MessageType.MESSAGE_CLIENT_EXIT);
message.setSender(u.getUserId());//一定要指定是那个客户端,服务端要根据这个userId移除集合中的线程 //发送message
try {
//从管理线程的集合里面,通过userId,得到这个线程对象
ClientConnectServerThread clientConnectServerThread =
ManageClientConnectServerThread.getClientConnectServerThread(u.getUserId());
//通过这个线程中获取关联的socket
Socket socket = clientConnectServerThread.getSocket();
//得到当前线程的Socket对应的ObjectOutputStream对象
ObjectOutputStream oos = new ObjectOutputStream(socket.getOutputStream());
oos.writeObject(message);
System.out.println(u.getUserId()+"退出系统");
System.exit(0);
} catch (IOException e) {
e.printStackTrace();
}
}
2.修改:QQView类

在该类中的内层循环中,调用logout方法:

//调用方法,给服务器发送一个退出系统的message
userClientService.logout();

2.服务端:
1.修改:ManageClientThreads类

在该类中增加removeServerConnectClientThread()方法

//增加一个方法,从集合中移除某个对象
public static void removeServerConnectClientThread(String userId){
hm.remove(userId);
}
2.修改:ServerConnectClientThread类

在该类的run方法中增加业务二操作:

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 if (message.getMesType().equals(MessageType.MESSAGE_CLIENT_EXIT)) {
//业务二:客户请求退出系统
System.out.println(message.getSender() + " 退出");
//将客户端对应的线程从集合中删除
ManageClientThreads.removeServerConnectClientThread(message.getSender());
socket.close();//关闭的是当前的线程持有的socket属性
//退出线程的循环
break;
} else {
System.out.println("其他类型的message,暂时不处理");
}
} catch (Exception e) {
e.printStackTrace();
}
}
}

运行:

1.运行服务端:

2.运行客户端,登录两个用户:


3.查看当前用户列表,可以看到有两个用户:

3.其中一个用户选择退出系统,可以看到用户正确退出,程序结束运行:

4.在另一个用户中查看当前用户列表,可以看到只剩下一个用户,说明服务端已经成功将退出的用户的线程从集合中删除

4.服务端这边显示该用户正确退出:

最新文章

  1. 供应链需求调研CheckList
  2. CADisplayLink 及定时器的使用
  3. JAVA源码分析-HashMap源码分析(一)
  4. Codevs 2370 小机房的树 LCA 树上倍增
  5. 在Excel中将数字转换为大写
  6. JSP+Servlet中使用jspsmartupload.jar进行图片上传下载
  7. UART UVM验证平台平台搭建总结
  8. Warning: Data truncated for column 'AirPress' at row 1
  9. 前端之JavaScript第二天学习(4)-JavaScript-注释
  10. 从零开始搭建TestCpp工程
  11. System.Windows.Forms.Timer反编译学习
  12. SPI协议及其工作原理浅析
  13. JAVA提高十三:Hashtable&Properties深入分析
  14. 使用scp从远程服务器下载文件到本地
  15. Java IO学习--(一)概述
  16. 自研网关纳管Spring Cloud(一)
  17. EF core的模型映射
  18. Python基础之面向对象2(封装)
  19. 关于Servlet的一些归纳(1)
  20. bootstrap modal 点击头部移动

热门文章

  1. Vue3 项目实战
  2. Vmware虚拟机硬件兼容性
  3. protobuf 的交叉编译使用(C++)
  4. Windows安装face_recognition
  5. Homebrew安装(macos)
  6. logback-spring 集成 ELK、kafka的配置
  7. 动手实践丨手把手教你用STM32做一个智能鱼缸
  8. c++小游戏———扫雷
  9. 【每天学一点-05】使用umi.js代理,解决跨域问题(前端)
  10. python 生成Windows快捷方式