连接终止时为什么不抛出异常?

时间:2014-07-27 17:31:20

标签: java sockets url networking download

我在这里解决我面临的问题。我希望得到专家的一些帮助。

下面是java中一个简单文件下载器的代码。我想在下载文件的过程中断开连接时检测到异常。现在,我运行代码,并在下载文件的中间,我关闭我的无线键盘wifi关闭按钮。这样做程序会永远挂起而不会抛出任何异常并且不会终止。它似乎永远阻止。首先我的困惑是为什么不抛出异常?其次,在下面的代码中你可以看到这行//con.setReadTimeout(2000);目前已注释掉了。删除注释并运行程序,现在如果通过关闭wifi中断连接中断然后它等待2秒然后如果它无法读取则它终止而不抛出异常。所以,在这种情况下,为什么它只是终止而不抛出任何异常?我对此行为感到非常困惑。我希望我能得到一些帮助。非常感谢你!

import java.io.FileOutputStream;
import java.net.URL;
import java.net.URLConnection;
import java.nio.channels.Channels;
import java.nio.channels.ReadableByteChannel;
import java.util.*;

    public class Main {
        public static void main(String[] args) {

            try{

                URL website = new URL("http://128f1.downloadming1.com/bollywood%20mp3/Jai%20Ho%20(2014)/06%20-%20Naacho%20Re%20-%20DownloadMing.SE.mp3");

                URLConnection con = website.openConnection();


                //con.setReadTimeout(2000);



                ReadableByteChannel rbc = Channels.newChannel(con.getInputStream());
                FileOutputStream fos = new FileOutputStream("song.mp3");

                fos.getChannel().transferFrom(rbc, 0, Long.MAX_VALUE);


            }
            catch(Exception e){

                System.out.println("got here");
                e.printStackTrace();
            }



            System.out.println("Done downloading...");

        }
    }

1 个答案:

答案 0 :(得分:1)

TCP连接旨在处理不可靠的网络和数据包丢失。如果您的网络连接消失了,除非您设置SO_KEEPALIVE套接字选项(java.net.SocketOptions有一些该选项的文档),否则它将无法知道。

不幸的是,URLConnection不允许您设置此选项或任何其他套接字选项。即使它确实如此,在大多数操作系统上,保持活动间隔将非常高,两个小时。保持活动间隔只能在操作系统设置中更改,不能通过Java更改(SocketOptions.SO_KEEPALIVE文档在这方面令人困惑)。

总而言之,您不会遇到异常,因为基础网络协议并非旨在检测您的网络介质在初始建立连接后是否中断。

正如您所发现的,setReadTimeout有所帮助,但是您已经碰到了transferFrom方法的未记录的功能。该方法的实际实现位于sun.nio.ch.FileChannelImpl.transferFromArbitraryChannel。我在这里粘贴JDK 7代码:

private long transferFromArbitraryChannel(ReadableByteChannel src,
                                          long position, long count)
    throws IOException
{
    // Untrusted target: Use a newly-erased buffer
    int c = (int)Math.min(count, TRANSFER_SIZE);
    ByteBuffer bb = Util.getTemporaryDirectBuffer(c);
    long tw = 0;                    // Total bytes written
    long pos = position;
    try {
        Util.erase(bb);
        while (tw < count) {
            bb.limit((int)Math.min((count - tw), (long)TRANSFER_SIZE));
            // ## Bug: Will block reading src if this channel
            // ##      is asynchronously closed
            int nr = src.read(bb);
            if (nr <= 0)
                break;
            bb.flip();
            int nw = write(bb, pos);
            tw += nw;
            if (nw != nr)
                break;
            pos += nw;
            bb.clear();
        }
        return tw;
    } catch (IOException x) {
        if (tw > 0)
            return tw;
        throw x;
    } finally {
        Util.releaseTemporaryDirectBuffer(bb);
    }
}

正如您在catch块中看到的,如果某些数据已经写入目标,它将忽略任何IOException并返回已写入的字节数。