【问题标题】:Socket connection half dropped - IOException not thrown套接字连接一半掉线 - 未抛出 IOException
【发布时间】:2017-02-17 01:57:22
【问题描述】:

我在这里阅读了一些关于这个问题的答案,但我对它们并不满意,所以我决定自己问。所以我知道有类似的问题,但由于答案对我来说真的不起作用,所以我问自己。

我有一个应用程序可以让 2 个用户相互连接(一个用作服务器,另一个用作客户端)。他们将通过该套接字连接发送文件。我正在使用一个内部有 2 个线程的服务,一个用于读取,另一个用于发送用户选择的文件。

这就是问题所在:如果客户端通过在(正在运行的应用程序的)android 菜单上滑动它来关闭应用程序,然后服务器(另一个人)试图向他发送一些东西,在我的认为它应该抛出一个 IOException,因为套接字流的另一端已经结束。但它没有这样做,我不知道为什么。如果我尝试向离开的人发送一些东西,我想展示一个 Toast。

编辑:刚刚注意到它总是停在指令 out.reset();

你们中的任何人都知道为什么没有抛出该异常吗? 有什么可能的解决方案。

PS:这是一个精简的应用程序,所以发送 Keep Alive 消息不是一个好的解决方案。此外,它已经向我展示了一两次吐司,但我无法再次复制这种行为。

这是我不希望发生的代码:

   ClientHandler tmp = connectedClients.get(key);
                    ObjectOutputStream out = tmp.getOut();
                    Socket s = tmp.getSocket();

                    if(s.isClosed()){
                        System.out.println("The socket of this client "+key + " is closed!");
                    }

                    if(s.isOutputShutdown()){
                        System.out.println("The output of this client is shutdown !");//only checks this side, the other one is the one that is shutdown
                    }
                    System.out.println("changed the culpado to : "+1);
                    createSendNotification();

                    File apkToSend;

                    for(int i=0;i<listOfApps.size();i++){
                        System.out.println("Item do be sent is : "+i);
                        HighwayGridViewAppItem tmpItem=listOfApps.get(i);
                        filePath=tmpItem.getFilePath();
                        appName=tmpItem.getAppName();
                        System.out.println("his filepath to send is : "+filePath);
                        System.out.println("his appname to send is : "+appName);


                        couldSend=false;

                        apkToSend=new File(filePath);

                        if(apkToSend.exists()){//do i reallly need this if?

                            apkToSendSize=apkToSend.length();
                            System.out.println("File size: " +apkToSendSize);

                            try{
                                out.writeObject(appName +": "+ apkToSendSize);//appName to send to have the name of the file

                                byte[] buffer = new byte [8192];
                                BufferedInputStream bis=new BufferedInputStream(new FileInputStream(apkToSend));
                                int count;
                                totalToSend =0;
                                showSendProgress();

                                while((count=bis.read(buffer))!=-1){
                                    out.write(buffer,0,count);
                                    totalToSend +=count;
                                    out.reset();
                                    System.out.println("ServerComm send thread - already sent this ammount : "+ totalToSend);
                                }
                                out.flush();
                                bis.close();

                            }
                            catch ( IOException e){

                                System.out.println("It is throwing the input output exception");

                                e.printStackTrace();

                                connectedClients.remove(key);
                                if(clients.size()<=1){

                                    h.post(new Runnable() {
                                        @Override
                                        public void run() {
                                            Toast.makeText(context, "No one is in your group.", Toast.LENGTH_SHORT).show();
                                        }
                                    });

                                    i=listOfApps.size()+1;
                                }else{

                                    System.out.println("Has more than one");
                                }

                            }

PS:当我尝试发送到“关闭”客户端时,它会打印一些“ServerComm 发送线程 - 已发送此数量:”+ totalToSend”,但随后停止,这是我认为应该抛出异常,但它只是停止,并且没有给出任何错误,应用程序继续它的生命,但我需要向用户提供一些输入,说明某些问题出现了。

另外,我在此服务的 onCreate 方法中创建了该处理程序,它正在与主循环器正确创建(因为它在服务中,它需要不同的创建)。

提前谢谢你们。

编辑:最终,差不多 4 分钟后,它抛出了一个 SocketException,但我不能等那么久。

【问题讨论】:

  • 您别无选择。只要 TCP 需要检测中断,您就必须等待,其中包括发送重试超时。
  • 我可以缩短等待时间吗?奇怪的是它写了,然后它停止写,什么都不做。就像什么都没发生一样。
  • 另外,奇怪的是它在正确的时间抛出了 2 到 3 次异常,然后我做了一些小的改动,比如添加了一个 system.out.println,当我注意到时,它没有再次工作,这很愚蠢。
  • 您必须记住 TCP 发送是缓冲的和异步的。因此,许多写入将成功,因为它们仅缓冲,然后写入将阻塞,因为缓冲区已满并且没有被 ACK 耗尽,然后您在写入中被阻塞,直到触发重试超时,然后您获得连接重置.
  • 我每次发送内容时都会重置流,我注意到当某些写入成功时,它会在重置中阻塞。在 reset() 中阻塞的原因逻辑是一样的吧?

标签: java android sockets exception networking


【解决方案1】:

仅仅因为 Android 处理应用程序并不意味着在内部关闭所有打开的连接,很可能您需要检测 Android 事件,然后执行显式关闭打开的套接字的代码,而不是等待 Android最终照顾它。否则,您将不得不等待通过调用终结器的垃圾收集关闭套接字。

这里有一些关于 Android 事件和 onDestroy 方法的详细信息:How to close Android application?

【讨论】:

    【解决方案2】:

    如果您需要立即进行断开连接检测,那么您必须实现自己的 ping/keep alive 机制,这通常意味着发送数据包并连续确认它们以便能够更可靠地捕获异常。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2017-05-25
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2023-03-10
      相关资源
      最近更新 更多