Indy 错误 10038 “非套接字上的套接字操作"闲置 61 秒后 [英] Indy error 10038 "Socket operation on non-socket" after 61 seconds of inactivity

查看:46
本文介绍了Indy 错误 10038 “非套接字上的套接字操作"闲置 61 秒后的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我想从 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屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆