通常情况下,一切工作正常,但当我在客户机上旋转50个线程,以相同的小数据包(只有39个字节)“同时”访问服务器时,服务器没有接收到所有字节的次数是随机的。更奇怪的是,它是非常一致的如何它不接收他们...只收到5个字节。
我正在使用tcpdump和tcpflow来捕获两端发生的事情(如果不熟悉tcp流,它会从tcp流中去除大量的tcp SYN/ACK/FIN/ETC噪声,并且只向您显示向任一方向发送的数据)。
在客户端,对于50个线程发射39字节的数据包,它看起来很完美。具体地说,tcpflow(它使用libpcap)向我展示了50个相同的数据传输:
07 B6 00 01 | 00 1E 00 00 | <etc>
按照我的理解,libpcap/tcpdump从相当低的级别(在TCP堆栈之下)获取数据,所以我认为这意味着数据发送正常,或者至少没有卡在内核缓冲区中。
但是,当看服务器端的时候,一切都不是十全十美的。一个随机数是失败的,它是一个很高的百分比。例如,在50个套接字连接中,有30个可以正常工作,但对于其中的20个,我有一个协议故障,服务器的socket.recv
在等待字节时超时(协议指示确切的数据包长度)。
它在失败的方式上是非常一致的。对于30/20的情况,有30个套接字完全接收发送的39字节。其余20个都接收此部分数据,之后我的socket.recv
超时:
07 B6 00 01 | 00
20个连接中的每一个只有5个字节到达,这似乎是在内核级别,因为tcpdump也只显示了5个字节到达。
怎么会这样呢?
这个5字节的边界不是100%的巧合。它是报头的第一部分,接下来是34字节的有效负载,但没有到达。在客户端,它是这样拆分的。
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
sock.connect((HOST, PORT))
sock.sendall(HEADER) # 5 bytes
sock.sendall(PAYLOAD) #34 bytes
回答我自己的问题...
简单的回答是,单独使用TCP,客户端无法知道预期的接收者是否实际收到了发送的字节。
IE:客户端是否“高兴地”发送了字节并不重要……即使使用TCP,它们也可能永远不会到达,而且您绝对不知道它们何时到达预定的接收者。无论如何,如果不在应用程序层中构建一些确认功能,就不可能。
数据在最后第2个条目(#974)中到达服务器,在发送后~30s,其间有大量的重传尝试。如果对服务器端#793感到好奇,这是我的应用层协议尝试向客户机发送一条消息:“等待更多数据时超时了...它在哪里?”。
除了固有的延迟之外,数据没有出现在服务器上的tcpdump
日志中的原因之一似乎也是我对tcpdump
的使用。简而言之:在查看捕获文件(使用-w
开关创建的文件)之前,确保Ctrl-C离开tcpdump
捕获,因为它似乎会对您在文件中看到的内容产生很大的不同。我想这是一个冲水/同步的问题,但我在猜测。然而,如果没有Ctrl-C,我肯定会丢失数据。
更多详细信息供将来参考...
很明显,第一个事实根本不是真的。TCP最好将您的字节发送给目标收件人(包括长时间重试),但这并不能保证,无论发送手册页是否为send
返回值指示“如果成功,这些调用将返回发送的字符数”。后者不是事实,具有很强的误导性(见下文)。
其根源主要来自各种套接字调用(特别是send
)的行为方式以及它们与操作系统的TCP/IP堆栈的交互方式...
connect()
返回成功肯定意味着您能够建立到服务器的连接,因此您至少知道此时服务器在那里并且正在监听(即:三部分TCP打开握手成功)。
对于'send',尽管调用的文档表明返回值(如果为正值)是“Number of[bytes]sent”,但这完全是错误的。返回值告诉您的只是底层OS中的TCP堆栈接受到其传出缓冲区中的字节数。在这一点之后,OS将尽力将那些字节传递给您最初与之建立连接的收件人。但这可能永远不会发生,所以这并不意味着您可以指望那些正在发送的字节!有些令人惊讶的是,也没有真正的方法来确定这是否是真的(或没有!)至少在TCP套接字层发生,即使TCP内置了ACK消息。为了验证发送字节的完全接收,您需要在应用程序层添加某种确认。nos在另一个问题中有一个很好的答案,这个问题有点涉及到这个问题。
增编...
我有一个TCP服务器和一个客户端,简单的TCP服务器将接收传入的数据并打印出来,而客户端将继续创建一个套接字连接并循环发送数据到TCP服务器。 我得到的信息是,如果一个TCP连接被正确地关闭了,这个过程应该会继续下去,不会发生任何崩溃。 但在从客户端接收到一定数量的数据到服务器之后,客户端会崩溃,并出现错误 有没有什么办法可以避免这次撞车,让它持续运行呢?我是新来的所以如果我犯了什么愚蠢的错误,我
我正在使用JavaNetty开发一个高负载tcp应用程序,该应用程序希望300k并发TCP连接。 它在测试服务器上工作得很好,可以到达300k个连接,但当部署到生产服务器时,它只能支持65387个连接,到达这个数量后,客户端将抛出“java.io.IOException:Connection reset by peer”异常。我尝试了很多次,每次当连接数达到65387时,客户端将无法创建连接。 网
我需要在javascript客户机和Web API控制器操作之间建立一个长时间运行(比如10分钟)的连接。Web应用程序的Azure负载均衡器的默认超时时间为4分钟,目前还无法配置。
[服务器]:
我创建了某种类型的客户机/服务器应用程序,它有自己的数据ACK系统。由于某些限制,它最初是用TCP编写的,但它的基础是考虑到UDP编写的。 我发送到服务器的数据包有自己的封装(数据包id和数据包大小报头。我知道UDP还有一个校验和,所以我没有为此添加报头),但是TCP是如何工作的,我知道服务器可能接收不到整个数据包,所以我收集并缓冲了接收到的数据,直到收到一个完整的有效数据包。 我想知道的是:如果
根据Stevens(图示为TCP/IP),traceroute程序用增量TTL(1、2、3等)向目的主机发送UDP数据包,以从ICMP TTL过期消息中获取中间跳信息。 “到达目的地”条件是ICMP端口无法到达的消息,因为traceroute寻址的随机端口数量很高(也就是说,不太可能有人在那里监听) 所以我的问题是:是否有技术原因(缺点、RFCs等)使用UDP数据包而不使用例如ICMP回送请求消息