Indy 错误 10038 “非套接字上的套接字操作"闲置 61 秒后 [英] Indy error 10038 "Socket operation on non-socket" after 61 seconds of inactivity
问题描述
我想从 FTP 服务器下载一些大文件 (GB).第一个文件的下载始终有效.然后在尝试获取第二个文件时,我得到:
<块引用>套接字错误#10038.非套接字上的套接字操作."
错误出现在获取"上.在获取"之后,我看到了这些消息(通过 FTP 状态事件):
开始 FTP 传输断开连接.断开连接.
代码是这样的:
{伪代码}为 1 到 AllFiles 做开始如果 Connect2FTP 那么开始FTP.Get(名称,GzFile,真,假);<--- 非套接字上的套接字操作"错误(我在 IDE 中也得到 EIdConnClosedGracefully 'Connection Closed Gracefully',F9 将恢复执行没有问题,但这没关系)解包(GzFile);<--- 这需要超过 60 秒结尾;结尾;如果 FTP.Connected然后 FTP.断开连接;
--
function Connect2FTP(FTP: TIdFTP; RemoteFolder: string; Log: TRichLog): Boolean;开始结果:= FTP.Connected;如果不是结果那么开始{我们已经连接}FTP.Host := MyFTP;FTP.用户名:= usr;FTP.密码:= psw;尝试FTP.连接;除了在 E 上:例外 DO结果:= FTP.Connected;如果结果然后 FTP.ChangeDir(RemoteFolder);结尾;结尾;
完整代码在这里:
FTP 使用多个套接字连接,一个用于命令/响应,每个传输单独连接.
套接字错误的最可能原因是位于 TIdFTP
之间的 FTP 不感知代理/路由器/防火墙,并且 FTP 服务器在短时间不活动后关闭命令连接.在 Unpack()
期间(或手动暂停),命令连接上没有传输任何命令/响应,它处于空闲状态,因此会因此类代理/超时而关闭路由器/防火墙.
在传输过程中,命令连接处于空闲状态,没有 FTP 命令/响应在其上传输(除非您中止传输),直到传输完成.在此期间,不支持 FTP 的代理/路由器/防火墙可能会过早关闭命令连接.
为了避免这种情况,TIdFTP
有一个 NATKeepAlive
属性,可以在命令连接空闲时启用 TCP 保持连接.这通常可以防止过早关闭.
但是,当 prgress 中没有传输时,如果 NATKeepAlive.UseKeepAlive
为 True,TIdFTP
会在命令连接上禁用 TCP keep-alives.TIdFTP
仅在传输期间使用 TCP keep-alives,假设您不会在 FTP 命令之间执行长时间延迟.如果需要延迟一段时间,要么关闭 FTP 连接,要么定期发送 FTP 命令(例如在定时器/线程中调用 TIdFTP.Noop()
).
或者,您可以尝试在连接到服务器后手动启用 TCP keep-alives,并将 NATKeepAlive.UseKeepAlive
设置为 False,以便 TIdFTP
不会自动禁用 keep-每次传输后都活着,例如:
function Connect2FTP(FTP: TIdFTP; RemoteFolder: string; Log: TRichLog): Boolean;开始结果:= FTP.Connected;如果不是结果那么开始{我们尚未连接}FTP.Host := MyFTP;FTP.用户名:= usr;FTP.密码:= psw;尝试FTP.连接;尝试FTP.ChangeDir(远程文件夹);//在空闲 10 秒后每 5 秒发送一次 TCP keep-aliveFTP.NATKeepAlive.UseKeepAlive := False;//默认为 False,但以防万一...FTP.Socket.Binding.SetKeepAliveValues(True, 10000, 5000);除了FTP.断开连接(假);增加;结尾;除了出口;结尾;结果:=真;结尾;结尾;
请注意,虽然大多数平台都支持在每个连接的基础上启用 TCP 保持活动(保持活动是 TCP 规范的一部分),但设置保持活动间隔是特定于平台的.目前,Indy 支持将间隔设置为:
- Windows 2000+ 和 WinCE 4.x+,用于 Win32 和 .NET
- Linux,当 Indy 在 Kylix 中使用时
- 在 FreePascal 中使用 Indy 时,Unix/Linux 和 NetBSD.
否则,将使用操作系统默认间隔,对于这种情况,它的值可能太大也可能不会太大,具体取决于操作系统配置.
目前,Delphi FireMonkey 中无法自定义间隔,Windows 除外.
如果特定平台支持设置自定义 TCP keep-alive 间隔,但 Indy 没有在 SetKeepAliveValues()
中实现,则可以使用 TIdFTP.Socket.Binding.SetSockOpt()
而是根据需要手动设置值.许多平台确实支持 TCP_KEEPIDLE
/TCP_KEEPINTVL
或等效的套接字选项.
I want to download some large files (GB) from an FTP server. The download of the first file always works. Then when trying to get the second file I get:
"Socket Error # 10038. Socket operation on non-socket."
The error is on 'Get'. After 'Get' I see these messages (via FTP status event):
Starting FTP transfer
Disconnecting.
Disconnected.
The code is like this:
{pseudo-code}
for 1 to AllFiles do
begin
if Connect2FTP then
begin
FTP.Get(Name, GzFile, TRUE, FALSE); <--- Socket operation on non-socket" error (I also get EIdConnClosedGracefully 'Connection Closed Gracefully' in IDE, F9 will resume execution without problems, but this is OK)
Unpack(GzFile); <--- this takes more than 60 seconds
end;
end;
if FTP.Connected
then FTP.Disconnect;
--
function Connect2FTP(FTP: TIdFTP; RemoteFolder: string; Log: TRichLog): Boolean;
begin
Result:= FTP.Connected;
if NOT Result then
begin { We are already connected }
FTP.Host := MyFTP;
FTP.Username:= usr;
FTP.Password:= psw;
TRY
FTP.Connect;
EXCEPT
on E: Exception DO
Result:= FTP.Connected;
if Result then FTP.ChangeDir(RemoteFolder);
end;
end;
Full code here: http://pastebin.com/RScj86R8 (PAS) or here https://ufile.io/26b54 (ZIP)
I think the problem appears after calling 'Unpack' which takes few minutes.
UPDATE: CONFIRMED: The problem appears after calling 'Unpack'. I removed the call and everything is fine. Pausing (sleep or break point) the program between downloads for a while (I think for more than 60 sec) will create the same problem.
FTP uses multiple socket connections, one for commands/responses, and separate connections for each transfer.
The most likely cause of the socket error is an FTP-unaware proxy/router/firewall sitting in between TIdFTP
and the FTP server is closing the command connection after a short period of inactivity. During the Unpack()
(or manual pause), no commands/responses are being transmitted on the command connection, it is sitting idle, and thus is subject to being closed by a timeout on such a proxy/router/firewall.
During a transfer, the command connection is sitting idle, no FTP commands/responses are being transmitted on it (unless you abort the transfer), until the transfer is complete. An FTP-unaware proxy/router/firewall may close the command connection prematurely during this time.
To avoid that, TIdFTP
has a NATKeepAlive
property that can enable TCP keep-alives on the command connection while it is sitting idle. This usually prevents premature closes.
However, when there is no transfer in prgress, TIdFTP
disables TCP keep-alives on the command connection if NATKeepAlive.UseKeepAlive
is True. TIdFTP
uses TCP keep-alives only during transfers, with the assumption that you are not going to perform long delays in between FTP commands. If you need to delay for awhile, either close the FTP connection, or send an FTP command at regular intervals (such as calling TIdFTP.Noop()
in a timer/thread).
Alternatively, you can try manually enabling TCP keep-alives after connecting to the server, and set NATKeepAlive.UseKeepAlive
to False so TIdFTP
will not automatically disable the keep-alives after each transfer, eg:
function Connect2FTP(FTP: TIdFTP; RemoteFolder: string; Log: TRichLog): Boolean;
begin
Result := FTP.Connected;
if not Result then
begin { We are not already connected }
FTP.Host := MyFTP;
FTP.Username:= usr;
FTP.Password:= psw;
try
FTP.Connect;
try
FTP.ChangeDir(RemoteFolder);
// send a TCP keep-alive every 5 seconds after being idle for 10 seconds
FTP.NATKeepAlive.UseKeepAlive := False; // False by default, but just in case...
FTP.Socket.Binding.SetKeepAliveValues(True, 10000, 5000);
except
FTP.Disconnect(False);
raise;
end;
except
Exit;
end;
Result := True;
end;
end;
Note that while most platforms support enabling TCP keep-alives on a per-connection basis (keep-alives are part of the TCP spec), setting keep-alive intervals is platform-specific. At this time, Indy supports setting the intervals on:
- Windows 2000+ and WinCE 4.x+, for Win32 and .NET
- Linux, when Indy is used in Kylix
- Unix/Linux and NetBSD, when Indy is used in FreePascal.
Otherwise, OS default intervals get used, which may or may not be too large in value for this situation, depending on OS configuration.
At this time, the intervals are not customizable in Delphi FireMonkey, except for Windows.
If a particular platform supports setting custom TCP keep-alive intervals, but Indy does not implement them in SetKeepAliveValues()
, you can use TIdFTP.Socket.Binding.SetSockOpt()
instead to set the values manually as needed. Many platforms do support TCP_KEEPIDLE
/TCP_KEEPINTVL
or equivalent socket options.
这篇关于Indy 错误 10038 “非套接字上的套接字操作"闲置 61 秒后的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!