为什么TCP客户端无法使用write检测服务器是否已关闭?

时间:2017-06-29 05:10:19

标签: java sockets tcp

我正在构建一个IM应用程序,从客户端,我编写这样的代码(我在阻塞模式下使用SocketChannel,历史原因,我认为它与此问题无关):

try {
    LogUtil.info(TAG, this.label + " tryConnect, attempt = " + (3 - retry));
    clientChannel = SocketChannel.open();
    clientChannel.configureBlocking(true);
    clientChannel.socket().setSoTimeout(100);
    clientChannel.socket().setTrafficClass(0x10);
    clientChannel.socket().setTcpNoDelay(true);
    clientChannel.socket().setPerformancePreferences(3, 3, 1);
    clientChannel.socket().connect(address, 10000);
    LogUtil.info(TAG, this.label + " socket connected successfully");
    break;
} catch (AlreadyConnectedException ace) {
    LogUtil.info(TAG, label + " AlreadyConnectedException");
    break;
} catch (NotYetConnectedException ace) {
    LogUtil.info(TAG, label + " NotYetConnectedException");
    break;
} catch (SocketTimeoutException e) {
    LogUtil.info(TAG, label + " SocketTimeoutException");
    break;
} catch (Exception e) {
    clientChannel = null;
    throw new SocketConnectionException(label + ", exception = " + ThrowableUtil.stackTraceToString(e));
}

问题是,当我有时关闭服务器时,客户端将继续成功写入(小块数据,总共少于50个字节)。大约3分钟后,客户端会遇到写入失败异常。

服务器关闭后,为什么客户端没有立即失败?我该如何解决这个问题?也许将发送缓冲区减少到10个字节?

修改

这是我实际写数据的方式:

public void writeXML(ByteBuffer buffer, int retry) {
        synchronized (writeLock) {
            if (retry < 0) {
                throw new SocketConnectionException(label + "Write Exception");
            }
            tryConnect(false);
            try {
                int written = 0;
                while (buffer.hasRemaining()) {
                    // I think it should be an exception here after I closed server
                    written += clientChannel.write(buffer);
                }
                if (LogUtil.debug) {
                    LogUtil.info(TAG, "\t successfully written = " + written);
                }
            } catch (Exception e) {
                e.printStackTrace();
                tryConnect(true);
                writeXML(buffer, --retry);
            }
        }
    }

2 个答案:

答案 0 :(得分:0)

因为您和对等应用程序之间存在:

  • 套接字发送缓冲区
  • TCP实施
  • 另一个TCP实施
  • 套接字接收缓冲区。

通常在写入时,数据会被传输到套接字发送缓冲区,并在线路上异步发送。因此,如果发送它会发生错误,您将无法立即发现。您将只知道TCP发送的时间是否足够,无论TCP的内部发送超时时间段是什么,以确定存在错误情况。 之后的下一次写入(或读取)将获得错误。可能只有几分钟的路程。

答案 1 :(得分:-1)

事实证明,read操作可以立即检测到已关闭的连接(通过@ EJP的回复,它与丢失的连接不同)。

在我的阅读主题中,我有这句话:

int read = clientChannel.read(buffer);

,当它返回-1表示服务器关闭(故意关机不同于网络不可达),我想write操作需要填充TCP发送缓冲区,所以没办法检测连接快速丢失。