概述
在使用标准套接字API的Fedora 24 x86_64上的c ++(gcc-c ++ 6.3.1)中,我基于RFC-959的少数几个组件创建了一个基本的FTP客户端。我遇到了一个偶然的问题,当接收或发送数据到服务器时,数据连接被重置。我可以毫不费力地关闭套接字并在数据读取的情况下继续执行,但是在执行写操作时我的程序非常认真(例如使用put
/ STOR
)。
到目前为止,该程序中有很多代码,因此我将在答案中包含最相关的部分,并尝试抽象其余部分。如果需要额外的代码段,我将根据请求添加它们。
详细流程
假设控制连接已经起作用并且用户已通过身份验证。
PASV
)。STOR
)errno
值等于ECONNRESET
观察
当尝试在上面的步骤8中关闭连接时,我尝试了一些不同的东西。
shutdown(fileDescriptor, [any flag])
返回-1,ENOTCONN
/ 107,传输端点未连接。
close(fileDescriptor)
返回0表示成功(偶尔我也看到ECONNRESET
)。
将SO_LINGER
设置为立即关闭时,此行为不会更改。事实上,最有趣的部分是服务器向我发送Rst
数据包后,我的客户端永远不会发送自己的Rst
或Fin
,直到我终止该程序。
启用TCP_NODELAY以禁用Nagle的算法并不会改变任何内容。
数据包监视:标准FTP客户端
这是观察大多数unix操作系统附带的标准FTP客户端上传输的数据包。
Packet Direction Purpose
ftp: PASV --> Client prompt for passive mode
ftp: 227 <-- Server approve passive mode
tcp: Syn --> Client initiate handshake for data connection
tcp: Syn+Ack <-- Server acknowledge handshake
tcp: Ack --> Client acknowledge acknowledgement
ftp: STOR --> Client prompt to store file
ftp: 150 <-- Server approve of store request
tcp: Rst+Ack <-- Server warns that connection is closed/reset
tcp: Fin+Ack --> Client closes connection
tcp: Ack --> Client acknowledges server
ftp: 226 <-- Server sends response that file upload done
我的客户的差异
如果您从我的客户端观察到相同的事务,则在调用Fin
或Ack
时,永远不会发送Rst
+ close()
或shutdown()
个数据包。
此数据连接无法关闭后,控制连接拒绝再发送任何响应,这会导致将来所有命令挂起。但是一旦我关闭程序,就会在所有打开的连接上发送重置,并关闭连接。
有没有人知道我在尝试终止数据连接时遇到的任何明显麻烦?为什么不关闭()或关闭()实际关闭套接字,如何在成功之前强制它不返回?