TCP套接字停止接收数据直到关闭。

8
我有一个非常奇怪的问题,让我感到非常疯狂。
我有一个Ruby服务器和Flash客户端(Action Script 3)。这是一个多人游戏。
问题在于一切都正常运行,然后突然间,一个随机玩家停止接收数据。当服务器因为不活动而关闭连接时,大约20-60秒后,客户端会接收到所有缓冲的数据。
客户端使用XMLsocket检索数据,因此客户端接收数据的方式不是问题所在。
socket.addEventListener(Event.CONNECT, connectHandler);
function connectHandler(event)
{
    sendData(sess);
}

function sendData(dat)
{
    trace("SEND: " + dat);
    addDebugData("SEND: " + dat)
    if (socket.connected) {
        socket.send(dat);
    } else {
        addDebugData("SOCKET NOT CONNECTED")
    }
}

socket.addEventListener(DataEvent.DATA, dataHandler);
function dataHandler(e:DataEvent) {
    var data:String = e.data;
    workData(data);
}

服务器在每次写入后都会刷新数据,因此不是刷新问题:
sock.write(data + DATAEOF)
sock.flush()

DATAEOF 是空字符,因此客户端解析字符串。

当服务器接受一个新的套接字时,它将 sync 设置为 true,以自动刷新,并将 TCP_NODELAY 也设置为 true:

newsock = serverSocket.accept
newsock.sync = true
newsock.setsockopt(Socket::IPPROTO_TCP, Socket::TCP_NODELAY, true)

这是我的研究:
信息:我每秒将netstat数据转储到文件中。
- 当客户端停止接收数据时,netstat显示套接字状态仍为“已建立”。 - 几秒钟后,发送队列相应增长到已发送的数据。 - tcpflow显示数据包被发送了两次。 - 当服务器关闭套接字时,套接字状态如预期更改为“FIN_WAIT1”。然后,tcpflow显示所有缓冲数据都发送到客户端,但客户端没有接收数据。几秒钟后,连接从netstat消失,tcpflow显示相同的数据再次发送,但这次客户端接收数据,因此开始向服务器发送数据,而服务器则接收它。但为时已晚……服务器已关闭连接。
我认为这不是操作系统/网络问题,因为我已经从位于西班牙的VPS更改为位于爱尔兰的Amazon EC2,问题仍然存在。
我也不认为这是客户端网络问题,因为这种情况每天发生数十次,平均在线用户数量约为45-55,每天约有400个唯一用户,因此比例极高。

编辑: 我做了更多的研究。 我已经将服务器更改为C ++。

当客户端停止发送数据时,一段时间后,服务器收到“对等方重置连接”的错误。 此时,tcpdump向我显示客户端发送了一个RST数据包,这可能是因为客户端关闭了连接并且服务器尝试读取,但是......为什么客户端关闭连接? 我认为答案是客户端不是关闭连接的人,而是内核。 这里有一些信息:http://scie.nti.st/2008/3/14/amazon-s3-and-connection-reset-by-peer

基本上,据我所知,Linux内核2.6.17+增加了TCP窗口/缓冲区的最大大小,如果其他设备无法处理足够大的TCP窗口,则会导致其它设备出现故障。设备将重置连接,我们会看到这条消息:“对等方重置连接”。

我按照步骤进行了操作,现在似乎只有在客户端失去与互联网的连接时,服务器才会关闭连接。


我将把这个作为答案添加,以便人们更了解这个。


2
发送队列的增长并不一定意味着问题出在服务器端。服务器需要将数据保留在队列中,直到客户端确认。因此,问题可能是由于客户端未读取数据引起的。 - Jan Wrobel
好的,谢谢建议,我会编辑帖子。 - Jorge Fuentes González
我不了解Flash架构,但类似的问题通常是由于客户端死锁导致无法读取输入数据。您确定所有由调用dataHandler()函数的线程调用的事件侦听器都是非阻塞的吗?如果您阻塞了此线程,则可能会防止客户端读取数据。也许您正在等待用户输入而阻塞了该线程? - Jan Wrobel
我的代码大约有6k个客户端/服务器行,我不知道要发布哪些代码,因为我不知道问题出在哪里 :-/,这是因为我已经发布了客户端和服务器用于连接的代码。如果你指的是Flash安全策略文件,一切都很好,当客户端请求服务器的策略文件时,它会被发送,然后客户端可以连接到服务器并开始播放。问题是,当用户玩了一段时间(有时是30分钟,有时是几个小时),就会出现我在帖子中解释过的行为。顺便说一句,谢谢你给我提供思路。 - Jorge Fuentes González
顺便说一下,我马上要添加更多的代码。 - Jorge Fuentes González
显示剩余2条评论
1个回答

2
我认为答案是内核关闭了连接。这里有一些信息:http://scie.nti.st/2008/3/14/amazon-s3-and-connection-reset-by-peer 基本上,据我所知,Linux内核2.6.17+增加了TCP窗口/缓冲区的最大大小,如果其他设备无法处理足够大的TCP窗口,则会导致其他设备发生故障。设备将重置连接,我们会看到这样的“对等方重置连接”的消息。
我已经按照步骤进行了操作,现在似乎只有在客户端失去与互联网的连接时,服务器才会关闭连接。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接