由于服务器关闭套接字,当写入失败时,Netty无法从服务器读取字节

时间:2013-10-14 20:03:58

标签: java sockets netty

Netty版本:4.0.10.Final

我使用Netty编写了一个客户端和服务器。这是客户端和服务器的作用。

服务器:

  1. 等待来自客户端的连接
  2. 接收来自客户的消息
  3. 如果消息不正确,请写入错误消息(6个字节),将其刷新, 关闭套接字,不要读取套接字中的任何未读消息。 否则继续阅读消息。好消息什么都不做。
  4. 客户端:

    1. 连接服务器。
    2. 写完N条好消息后,写下一条坏消息并继续 写好消息。此过程发生在单独的线程中。 该频道在频道激活后启动。
    3. 如果服务器有任何响应,请记录并关闭 插座。 (请注意,只有在出现错误时服务器才会响应)
    4. 我已将客户端和服务器都绑定在一起。我发现写入错误消息后服务器正在关闭连接。在错误消息之后写入好消息时,客户端开始看到管道错误。这是因为服务器检测到错误消息并响应错误消息并关闭套接字。只有在使用侦听器完成写入操作后才会关闭连接。客户端始终不从服务器读取错误消息。客户端中的早期步骤(2)在I / O线程中执行。这导致在K次实验中接收的错误消息的百分比非常低(<10%)。将步骤(2)移动到单独的线程后,%转到(70%)。无论如何它都不准确。如果由于管道损坏导致写入失败,netty会触发通道读取吗?

      更新1 : 我正在澄清并回答这里提出的任何问题,所以每个人都可以在一个地方找到问题/澄清。 “你写的是一条会导致重置的错误消息,然后是你已经知道无法通过的好消息,并试图读取可能被丢弃的响应。这对我没有任何意义无论如何“ - 来自EJP

      - 在现实世界中,服务器可能会因为客户事先无法知道的原因而将某些内容视为糟糕。为了简化,我说客户端故意发送一条导致服务器重置的错误消息。即使总消息中有错误消息,我也希望发送所有好消息。

      我所做的与Apple Push Notification Service实施的协议类似。

2 个答案:

答案 0 :(得分:1)

  

如果消息不正确,请写入错误消息(6个字节),刷新它,关闭套接字并且不读取套接字中的任何未读消息。否则继续阅读消息。

这将导致连接重置,客户端将其视为Unix,Linux等中的损坏管道。

  

写完N条好消息后,写一条坏消息并继续写好消息。

那会遇到刚刚提到的破损管道错误。

  

此过程在一个单独的线程中发生。

为什么呢? NIO和Netty的重点在于你不需要额外的线程。

  

我发现服务器在写完错误消息后正在关闭连接。

那就是你所说的,所以就是这样。

  

在收到错误消息后写入好消息时,客户端开始看到管道错误。

正如我所说。

  

这是因为服务器检测到错误消息并回复了错误消息并关闭了套接字。

正确。

  

客户端始终没有从服务器读取错误消息。

由于连接重置。重置后,待处理数据的交付将停止。

  

如果由于管道损坏导致写入失败,netty会触发通道吗?

不,它会在数据或EOS到达时触发读取

然而,你奇怪的系统设计/协议使得这一点变得不可预测,如果不是不可能的话。您正在编写一条导致重置的错误消息,然后是您已经知道无法通过的好消息,并尝试读取可能已被丢弃的响应。这对我来说没有任何意义。你想在这里证明什么?

尝试与其他人一样的请求 - 响应协议。

答案 1 :(得分:0)

APN协议似乎很尴尬,因为它不承认成功收到通知。相反,它只会告诉您遇到错误时已成功收到哪些通知。该协议正在假设您通常会发送格式良好的通知。

我建议你需要某种过期的缓存(LinkedHashMap可能在这里工作),你需要在通知中使用opaque identifier字段作为全局唯一的有序值。序列号将起作用(但如果可以重新启动客户端,则需要保留)。

每次生成APN

  • 将其标识符设置为下一个序列号
  • 发送
  • 将其放在LinkedHashMap中,其序列号的字符串键与当前时间连接(例如String key = sequenceNumber +“ - ”+ System.currentTimeMillis())

如果收到错误,则需要重新打开连接,并重新发送映射中的所有APN,其序列号高于错误中报告的标识符。这相对容易。只需遍历地图,删除任何序列号低于报告的APN。然后按顺序重新发送剩余的APN,在地图中用当前时间替换它们(即重新发送时删除APN,然后使用新的当前时间重新插入地图)。

您需要定期清除旧条目的地图。您需要根据发送格式错误的APN时APN服务返回错误所需的时间来确定合理的时间长度。我怀疑它只需几秒钟(如果不是更快)。例如,如果您发送10个APN /秒,并且您知道APN服务器肯定会在30秒内响应,则30秒的到期时间(每秒清除一次)可能是合适的。只需沿着地图迭代,删除任何元素,其中键的时间部分小于System.currentTimeMillis() - 30000(30秒到期时间)。您需要适当地同步线程。

我会捕获因编写而导致的任何IOExceptions,并将您尝试在地图中写入的APN放置并重新发送。

您无法应对的是一个真正的网络错误,您不知道APN服务是否收到通知(或一堆通知)。您必须根据您的服务决定是立即重新发送受影响的APN,还是在一段时间后重新发送或根本不重新发送受影响的APN。如果您在一段时间后发送,则需要在发送它们时向他们提供新的序列号。这将允许您在此期间发送新的APN。