【问题标题】:Java NIO TCP timeout issueJava NIO TCP 超时问题
【发布时间】:2015-05-01 04:15:59
【问题描述】:

我在 2 个线程中使用一个 SocketChannel,一个线程用于发送数据,另一个用于接收数据。

SocketChannel socketChannel = SocketChannel.open(new InetSocketAddress(ip,port));
socketChannel.configureBlocking(false);

线程1:使用上面的socketchannel写入数据

线程2:使用同一个socketchannel读取数据

我没有在 socketchannel 中使用任何选择器,因为我需要异步读写(使用 2 个不同的线程)

问题:当连接丢失时,socketchannel.write() 和socketchannel.read() 操作不会抛出任何错误。它只是阻止操作。

我需要检测连接丢失。

我尝试在线程 2 中使用 heartbeat 方法,但由于读取操作只是阻塞,因此该方法不起作用。有没有其他方法可以在不使用新线程中的心跳的情况下检测连接丢失?

如果连接丢失,是否可能在写入/读取时抛出错误?

提前致谢。

编辑:

线程 1:

public void run() {
  socketChannel = SendAndReceivePacketUtil.createConnection(ip, port);
  socketChannel.configureBlocking(false);

  RecTask task = new RecTask(socketChannel);
  Thread recThread = new Thread(task);
  recThread.start();

  while(true)
  {
     byte[] data= getDataFromQueue(ip);
     if(data!= null) {
         //print(new String(data));
         sendPacket(data, socketChannel);
     }
   }
}

线程 2:(RecTask)

public void run() {
  while(true) {
    byte[] data = receivePacket(socketChannel);
    //print(new String(data));
  }
}

线程 1 和 2 都有 try-catch-finally 块。最后关闭socketchannel。

发送包:

int dataSent = 0;
while (dataSent < data.length) {
    long n = socketChannel.write(buf);
        if (n < 0) {
            throw new Exception();
        }
        dataSent += (int) n;
 }

接收包:

int dataRec = 0;
byte[] data = new byte[length];
ByteBuffer buffer = ByteBuffer.wrap(data);

while (dataRec < length) {
    long n = socketChannel.read(buffer);
    if (n < 0) {
        throw new Exception();
    }
    dataRec += (int) n;
}       
return data;

我不断地发送和接收数据。但是一旦连接丢失,什么都不会打印,代码就会卡住。它是一个 android wifi 直接应用程序。对于连接丢失的情况,我只需关闭 wifi 模块。

【问题讨论】:

    标签: java multithreading tcp nio socketchannel


    【解决方案1】:

    我没有在 socketchannel 中使用任何选择器,因为我需要异步读写(使用 2 个不同的线程)

    这不是避免使用Selector. 的理由事实上,如果没有Selector.,编写正确的非阻塞 NIO 代码是相当困难的

    问题:当连接丢失时,socketchannel.write() 和socketchannel.read() 操作不会抛出任何错误。它只是阻止操作。

    不,它没有。您处于非阻塞模式。它要么返回一个正整数,要么返回零,或者抛出一个异常。是哪个?

    我尝试在线程 2 中使用 heartbeat 方法,但由于读取操作只是阻塞,因此该方法不起作用。

    读操作在非阻塞模式下不会阻塞。

    有没有其他方法可以在不使用新线程中的心跳的情况下检测连接丢失?

    在 TCP 中检测连接丢失的唯一可靠方法是写入连接。最终这会抛出IOException: connection reset.,但由于缓冲、重试等原因,它不会在连接丢失后第一次发生。

    如果连接丢失,是否可能在写入/读取时抛出错误?

    就是这样。

    这个问题存在严重问题。您发布的代码不是真正的代码,或者它的行为与您描述的不同。您需要发布更多内容,例如你的读写代码。

    【讨论】:

    • 我已经用所需的代码更新了问题。虽然我处于非阻塞模式,但连接丢失时我仍然无法打印任何内容。代码也没有进入 catch/finally 块,这就是为什么我假设代码等待读/写操作。
    • @Pulkit 显然,您的读取和写入都返回零,这会导致无限循环。这就是为什么你需要select(). 编写没有select() 的非阻塞循环只是糟糕的技术和CPU 时间的浪费。
    • 是否可以使用 select 进行异步读写?还有,如果我把socketChannel.configureBloacking()改成true,那问题应该就解决了吧?
    • 正确。在这种情况下,我会使用阻塞模式,因为显然线程除了读取或写入之外没有其他事情可做,否则您将不会编写这些循环。
    • 但是在阻塞模式的情况下,写入和读取也会相互依赖。就像数据正在写入套接字通道一样,同时我无法从套接字通道读取数据。不是吗?
    【解决方案2】:

    您可以在套接字上寻找启用 TCP-KEEP alive 选项。在空闲连接时,keep-alive 消息被发送,并且 TCP 层的消息预计会收到 ACK。

    如果 TCP-KEEP alive 失败,您的下一个读/写操作将导致错误 (ECONNRESET),这可用作连接丢失的标志。

    【讨论】:

    • 我们不能为保持活动设置具有较小超时值的套接字选项吗?
    • 据我所知,它依赖于操作系统。您必须在操作系统级别更改它
    • @Prahbu 在某些平台上,是的,但不是通过 Java 代码。在其他平台上,您必须在操作系统配置级别进行设置,这需要管理权限。
    • @EJP。啊!我懂了。不知道 Java 限制。谢谢。
    猜你喜欢
    • 2011-09-26
    • 1970-01-01
    • 2012-07-17
    • 1970-01-01
    • 2012-04-07
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多