首页 > 其他分享 >day03-2无异常退出

day03-2无异常退出

时间:2022-09-22 22:36:04浏览次数:46  
标签:day03 用户 线程 退出 message 异常 服务端 客户端

多用户即时通讯系统03

4.编码实现02

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

4.3.1思路分析

image-20220922191529799

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

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

解决方法:

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

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

image-20220922193057065

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();

image-20220922200240425

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.运行服务端:

image-20220922200701524

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

image-20220922200802600 image-20220922201246653

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

image-20220922201404318

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

image-20220922201011864

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

image-20220922201649953

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

image-20220922201736838

标签:day03,用户,线程,退出,message,异常,服务端,客户端
From: https://www.cnblogs.com/liyuelian/p/16721066.html

相关文章

  • day03-代码实现02
    多用户即时通讯系统034.编码实现024.2功能实现-拉取在线用户4.2.1思路分析客户端想要知道在线用户列表,就要向服务器发送请求(Message),因为只有服务器端保持着所有与客......
  • Day03
                         ......
  • Spring MVC框架:第十七章:异常映射
    异常映射异常机制是Java程序中针对有可能发生的问题所提前作出的应急解决方案。在SpringMVC中可以通过异常映射的方式,将异常类型和某个视图名称对应起来,让用户不是看到异......
  • CSAPP(第三版)第八章异常控制流学习笔记
    定义:从给处理器加点开始,知道你断电为止,程序计数器假设一个值的序列\(a_0,a_1,...,a_{n-1}\)其中,每个\(a_k\)是某个响应的指令\(I_k\)的地址。每次从\(a_k\)到\(a_{k+1}\)......
  • 使用可视化工具和统计方法检测异常值
    异常值(离群值)是指距离其他数据值太远的数据值。数据异常值可能是自然产生的,也可能是由于测量不准确、或系统故障造成的。与缺失值类似,异常值会破坏数据科学项目并返回错误......
  • 【全网最全】springboot整合JSR303参数校验与全局异常处理
    一、前言我们在日常开发中,避不开的就是参数校验,有人说前端不是会在表单中进行校验的吗?在后端中,我们可以直接不管前端怎么样判断过滤,我们后端都需要进行再次判断,为了安全。......
  • dotnet 线上异常分析工具
    现在.NETCore上线后,不可避免的会出现各种问题,如内存泄漏、CPU占用高、接口处理耗时较长等问题。这个时候就需要快速准确的定位问题,并解决。这时候就可以使用.NETCore......
  • 异常处理
    一、什么是异常    编程的时候也会发生一些错误,Java是先编译后运行,在Java编程时出现的错误可以检测语法的编写是否正确,运行时发生的错误称为异常(Execption)......
  • Day7 Javase抽象接口以及异常的捕获和抛出
    Day7面向对象编程抽象abstract修饰抽象类,如果修饰方法就是抽象方法。抽象方法可以写方法体,然后让继承抽象类的类去重写抽象方法。java的类是单继承的,但是接口可以实现......
  • JavaLearnDay03
    转义字符引用数据类型·特点:存储一个引用(内存地址)通过引用指向内存地址中的数据值·字符串类型:关键字:String作用:存储多个字符补充内容:在Java中所有整数默认为int......