草庐IT

day03-2无异常退出

liyuelian 2023-04-17 原文

多用户即时通讯系统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.服务端这边显示该用户正确退出:

有关day03-2无异常退出的更多相关文章

  1. ruby - 在 ruby​​ 中生成一个进程,捕获 stdout,stderr,获取退出状态 - 2

    我想从ruby​​rake脚本运行一个可执行文件,比如foo.exe我希望将foo.exe的STDOUT和STDERR输出直接写入我正在运行rake任务的控制台.当进程完成时,我想将退出代码捕获到一个变量中。我如何实现这一目标?我一直在玩backticks、process.spawn、system但我无法获得我想要的所有行为,只有部分更新:我在Windows上,在标准命令提示符下,而不是cygwin 最佳答案 system获取您想要的STDOUT行为。它还返回true作为零退出代码,这可能很有用。$?填充了有关最后一次system调

  2. ruby-on-rails - rails : Find tasks that were created on a certain day? - 2

    我有一个任务列表(名称、starts_at),我试图在每日View中显示它们(就像iCal)。deftodays_tasks(day)Task.find(:all,:conditions=>["starts_atbetween?and?",day.beginning,day.ending]end我不知道如何将Time.now(例如“2009-04-1210:00:00”)动态转换为一天的开始(和结束),以便进行比较。 最佳答案 deftodays_tasks(now=Time.now)Task.find(:all,:conditio

  3. 什么是0day漏洞?如何预防0day攻击? - 2

    什么是0day漏洞?0day漏洞,是指已经被发现,但是还未被公开,同时官方还没有相关补丁的漏洞;通俗的讲,就是除了黑客,没人知道他的存在,其往往具有很大的突发性、破坏性、致命性。0day漏洞之所以称为0day,正是因为其补丁永远晚于攻击。所以攻击者利用0day漏洞攻击的成功率极高,往往可以达到目的并全身而退,而防守方却一无所知,只有在漏洞公布之后,才后知后觉,却为时已晚。“后知后觉、反应迟钝”就是当前安全防护面对0day攻击的真实写照!为了方便大家理解,中科三方为大家梳理当前安全防护模式下,一个漏洞从发现到解决的三个时间节点:T0:此时漏洞即0day漏洞,是已经被发现,还未被公开,官方还没有相

  4. ruby - Rails 比较 date.end_of_day.to_datetime 和 date.to_datetime.end_of_day 返回的日期对象值时返回 false - 2

    ruby1.9.3dev(2011-09-23修订版33323)[i686-linux]轨道3.0.20最近为什么在与DateTimeonRails相关的RSpecs项目上工作我发现在给定日期以下语句发出的值date.end_of_day.to_datetime和date.to_datetime.end_of_day虽然它们表示相同的日期时间,但比较时返回false。为了确认这一点,我打开了Rails控制台并尝试了以下操作1.9.3dev:053>monday=Time.now.monday=>2013-02-2500:00:00+05301.9.3dev:054>monday.cla

  5. ruby-on-rails - 如何在不退出 IRB session 的情况下退出调试器? - 2

    这是一个长期存在的挫败感来源,但也许我遗漏了什么。如果我正在调试,并且我想退出调试器并返回到IRB或Rails控制台,“退出”将不起作用,因为它将退出IRB。“完成”似乎也与继续具有相同的效果。使用“删除”删除断点然后尝试“继续”或“完成”不起作用。有什么想法吗? 最佳答案 至少在byebug中,你可以这样做:evalreturn它具有计算当前函数的return语句的净效果。这有时会奏效,具体取决于调用堆栈的外观。现在虽然这不会删除当前断点....如果您只是想收回控制权,在大多数情况下这会做到这一点,具体取决于您的代码结构。在您的代

  6. ruby - Thin::Server#daemonize 立即退出 - 2

    我试图制作一个可执行文件,它通过Thin作为守护进程启动Sinatra应用程序。我正在使用此代码通过Sinatra应用程序调用Thin:#!/usr/bin/envrubyrequire'thin'require'app.rb'server=::Thin::Server.new('127.0.0.1',9999,App)server.log_file='tmp/thin.log'server.pid_file='tmp/thin.pid'server.daemonize这是我执行脚本时得到的日志输出:>>WritingPIDtotmp/thin.pid>>Exiting!服务器正常启动

  7. ruby - 当 shelled-out 命令返回非零退出代码时,如何让 Ruby 脚本失败? - 2

    在Ruby脚本中,有variousways调用系统命令/命令行反引号:`commandarg1arg2`分隔形式,例如%x(commandarg1arg2)(可用其他分隔符)Kernel#system方法:system('commandarg1arg2')Kernel#exec方法:exec('commandarg1arg2')如果我希望Ruby脚本在调用的命令失败时失败(有异常)(具有非零退出代码),我可以检查特殊变量中的退出代码$?对于前两个变体:`commandarg1arg2`failunless$?==0或%x,commandarg1arg2,failunless$?==0如

  8. ruby - 为什么退出 Ruby 线程会杀死我的整个程序? - 2

    我有这段代码:puts"Start"loopdoThread.startdoputs"Hellofromthread"exitendtext=getsputs"#{text}"endputs"Done"我希望看到“Start”后跟“Hellofromthread”,然后我可以输入会得到回显的输入。相反,我得到“Start”和“Hellofromthread”,然后程序退出。来自关于exit的文档:Terminatesthrandschedulesanotherthreadtoberun.Ifthisthreadisalreadymarkedtobekilled,exitreturnst

  9. ruby-on-rails - 在不使用 `at_exit` 的情况下,如何确保操作在 Rails 退出之前运行? - 2

    我有一个操作需要在我的Rails应用程序终止之前在我的Rails应用程序中执行。我可以在Rails中为此使用一个钩子(Hook)吗?我猜类似于at_exit的东西。 最佳答案 Ruby本身支持两个钩子(Hook),BEGIN和END,它们在脚本开始时运行,并在解释器停止运行时运行。有关详细信息,请参阅“WhatdoesRuby'sBEGINdo?”。BEGIN文档说:Designates,viacodeblock,codetobeexecutedunconditionallybeforesequentialexecutionofth

  10. ruby - 如果超过 5 秒,如何退出使用 Ruby 运行的进程? - 2

    我正在用Ruby实现一个检查系统。它运行具有不同测试的可执行文件。如果解决方案不正确,则可能需要很长时间才能完成某些严格的测试。这就是为什么我想将执行时间限制为5秒。我正在使用system()函数来运行可执行文件:system("./solution");.NET有一个很棒的WaitForExit()方法,那么Ruby呢?有没有办法将外部进程的执行时间限制为5秒?谢谢 最佳答案 您可以使用标准超时库,如下所示:require'timeout'Timeout::timeout(5){system("./solution")}这样您就不

随机推荐