以下是转载内容,转载地址是:
http://chenke1215.blog.163.com/blog/static/124414520103611222617/
http://chenke1215.blog.163.com/blog/static/124414520103611222617/ 写道
最近在开发中遇到一个问题,就是如何判断远端服务器是否已经断开连接,如果断开那么需要重新连接。
首先想到socket类的方法isClosed()、isConnected()、isInputStreamShutdown()、isOutputStreamShutdown()等,但经过试验并查看相关文档,这些方法都是本地端的状态,无法判断远端是否已经断开连接。
然后想到是否可以通过OutputStream发送一段测试数据,如果发送失败就表示远端已经断开连接,类似ping,但是这样会影响到正常的输出数据,远端无法把正常数据和测试数据分开。
最后又回到socket类,发现有一个方法sendUrgentData,查看文档后得知它会往输出流发送一个字节的数据,只要对方Socket的SO_OOBINLINE属性没有打开,就会自动舍弃这个字节,而SO_OOBINLINE属性默认情况下就是关闭的,太好了,正是我需要的!
于是,下面一段代码就可以判断远端是否断开了连接:
try{
socket.sendUrgentData(0xFF);
}catch(Exception ex){
reconnect();
}
首先想到socket类的方法isClosed()、isConnected()、isInputStreamShutdown()、isOutputStreamShutdown()等,但经过试验并查看相关文档,这些方法都是本地端的状态,无法判断远端是否已经断开连接。
然后想到是否可以通过OutputStream发送一段测试数据,如果发送失败就表示远端已经断开连接,类似ping,但是这样会影响到正常的输出数据,远端无法把正常数据和测试数据分开。
最后又回到socket类,发现有一个方法sendUrgentData,查看文档后得知它会往输出流发送一个字节的数据,只要对方Socket的SO_OOBINLINE属性没有打开,就会自动舍弃这个字节,而SO_OOBINLINE属性默认情况下就是关闭的,太好了,正是我需要的!
于是,下面一段代码就可以判断远端是否断开了连接:
try{
socket.sendUrgentData(0xFF);
}catch(Exception ex){
reconnect();
}
这里非常肯定的是:SO_OOBINLINE默认是关闭的,也就是说,sendUrgentData发送的字节,在客户端(准确的说是接收端,有时服务器端也接收消息)是舍弃的,但是这种情况在win7系统下会出现异常情况,即发送端在执行17次sendUrgentData后,发生异常,异常的结果是:
http://d2100.com/questions/16360 写道
我目前正在调试交换数据通过 TCP 连接的两个 Java 应用程序。
一个应用程序中,TCP 客户端,定期通过调用Socket#sendUrgentData(int)其他,TCP 服务器,发送紧急数据。18 尝试发送紧急数据,TCP 客户端将引发了以下异常
java.io.IOException:BrokenPipe
at java.net.PlainSocketImpl.socketSendUrgentData(Native Method)
at java.net.PlainSocketImpl.sendUrgentData(PlainSocketImpl.java:541)
at java.net.Socket.sendUrgentData(Socket.java:927)TCP 服务器会引发此异常
java.net.SocketException: Software caused connection abort: recv failed
at java.net.SocketInputStream.socketRead0(Native Method)
at java.net.SocketInputStream.read(Unknown Source)
at java.net.SocketInputStream.read(Unknown Source)我相信例外情况引起试图关闭连接、 插座来读写。不明白的是为什么连接或插座成为封闭后调用 sendUrgentData() 17 次。我能重复一遍,而且它的发生总是后 17 倍。
如果我在 Windows 上运行的客户端和服务器,出现问题。如果我的客户端和服务器上运行 Solaris 问题不会发生。如果我在 Windows 上 Solaris 和服务器上运行客户端时出现问题。如果在 Solaris 上运行在 Windows 客户端和服务器,则不出现问题。这让我想起了它可能相关的 Windows 吗?
使用 Wireshark 在该连接上看到以下交通
--> = from TCP client to TCP server
<-- = from TCP server to TCP client
--> [PSH, ACK, URG] (Seq=1, Ack=1)
<-- [ACK] (Seq=1, Ack=2)
--> [PSH, ACK, URG] (Seq=2, Ack=1)
<-- [ACK] (Seq=1, Ack=3)
...
--> [PSH, ACK, URG] (Seq=17, Ack=1)
<-- [RST, ACK] (Seq=1, Ack=18)
本人测试的结果是:
- java.net.SocketException: Software caused connection abort: recv failed
- at java.net.SocketInputStream.socketRead0(Native Method)
- at java.net.SocketInputStream.read(SocketInputStream.java:129)
- at java.io.DataInputStream.read(DataInputStream.java:83)
- at cn.DstClient.main(DstClient.java:25)
这里需要声明的一点是,在xp下是没有任何问题的,也就是说sendUrgentData在win7下使用,要自己处理一下.以下是外文翻译的结果,大家凑合着看看,
写道
我猜你其实正确正在接受紧急数据失败的应用程序中的数据,它将比你预期和吗?
有很多原因,此操作失败,特别是如果您在尝试它在跨平台的情况: 在 TCP 有两个相互矛盾描述如何紧急数据作品, RFC 793 TCP 说紧急指针指示如下紧急数据的字节的详细信息,但RFC 1122更正这以及国家紧急指针指示紧急数据的最后一个字节。这会导致互操作性问题,如果一个对等端使用 RFC 793 定义和其他使用 RFC 1122 定义。
因此,第一次确认您的应用程序实际上正在紧急数据的正确字节。是的我说字节,有更多的兼容性中的复杂性 Windows 仅支持的带数据的单个字节,而 RFC 1122 指定 TCP 必须支持任意长度的紧急数据字节序列。Windows 还没有指定如何,或者如果它将缓冲区随后的带数据,因此,如果您是慢读一个字节的紧急数据和紧急数据的另一个字节到达某个字节可能会丢失 ; 然后虽然我们的测试表明,Windows 不会缓冲紧急数据。这一切都使信号使用 TCP 窗口上的紧急数据有点不可靠的带外使用。
还有,如果你碰巧使用重叠的 I/O 来关于所有其他问题。
我已经介绍这一点更加深入,尽管从 c + + 的角度,在这里: http://www.serverframework.com/asynchronousevents/2011/10/out-of-band-data-and-overlapped-io.html
还有一点需要说明的是:flex中socket是没有SO_OOBINLINE属性设置的,所以呢,需要另外的办法来考虑了.
有的同学说new 一个DataInputStream即可,我想说的是,每次都去new,实在不为明智之举.