Indy错误10038“非插座上的套接字操作” 61秒后不活动 [英] Indy error 10038 "Socket operation on non-socket" after 61 seconds of inactivity

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

问题描述

我想从FTP服务器下载一些大文件(GB)。
第一个文件的下载始终有效。然后,当试图获取我得到的第二个文件:


套接字错误#10038.套接字操作在非套接字。


错误在Get上。 'Get'后,我看到这些消息(通过FTP状态事件):

 启动FTP传输
断开连接。
断开连接。

代码如下:

  {伪代码} 
为1到AllFiles
开始
如果Connect2FTP然后
开始
FTP.Get(名称, GzFile,TRUE,FALSE); < ---非套接字上的套接字操作'错误(我也在IDE中得到EIdConnClosedGracefully连接关闭,F9将会恢复执行,但这是可以的)
Unpack(GzFile);< - - 这需要超过60秒
结束;
结束;
如果FTP.Connected
然后FTP.Disconnect;

-

 功能Connect2FTP(FTP:TIdFTP; RemoteFolder :string; Log:TRichLog):Boolean; 
begin
结果:= FTP.Connected;
如果NOT结果然后
开始{我们已经连接}
FTP .Host:= MyFTP;
FTP.Username:= usr;
FTP.Password:= psw;

TRY
FTP.Connect;
EXCEPT
在E:异常DO

结果:= FTP.Connected;
if Result then FTP.ChangeDir(RemoteFolder);
end;
end;

这里的完整代码:

解决方案

FTP使用多个套接字连接,一个用于命令/响应,以及每个传输的单独连接。



套接字错误的最可能原因是位于 TIdFTP 之间的FTP不知名的代理/路由器/防火墙并且FTP服务器在短时间不活动之后关闭命令连接。在 Unpack()(或手动暂停)期间,命令连接上没有发送命令/响应,它正在空闲,因此被关闭在这种代理/路由器/防火墙上超时。



在传输过程中,命令连接正在空闲,不会传输FTP命令/响应(除非您中止转移),直到转移完成。在此期间,FTP不知情的代理/路由器/防火墙可能会提前关闭命令连接。



为避免这种情况, TIdFTP 有一个 NATKeepAlive 属性,可以在空闲时在命令连接上启用TCP保持活动。这通常可以防止过早关闭。



但是,如果在prgress中没有传输, TIdFTP 禁用TCP保持活动在命令连接上,如果 NATKeepAlive.UseKeepAlive 为True。 TIdFTP 仅在传输过程中使用TCP保持活动,假设您不会在FTP命令之间执行长时间的延迟。如果需要延迟一段时间,请关闭FTP连接,或者定期发送FTP命令(例如在定时器/线程中调用 TIdFTP.Noop()



或者,您可以尝试在连接到服务器后手动启用TCP保持活动,并设置 NATKeepAlive.UseKeepAlive 到False所以 TIdFTP 不会在每次传输后自动禁用保持活动,例如:

 函数Connect2FTP(FTP:TIdFTP; RemoteFolder:string; Log:TRichLog):Boolean; 
begin
结果:= FTP.Connected;
如果没有结果然后
开始{我们还没有连接}
FTP.Host:= MyFTP;
FTP.Username:= usr;
FTP.Password:= psw;

try
FTP.Connect;
try
FTP.ChangeDir(RemoteFolder);

//在空闲10秒后每5秒发送TCP保持活动
FTP.NATKeepAlive.UseKeepAlive:= False; //默认为False,但是为了防止...
FTP.Socket.Binding.SetKeepAliveValues(True,10000,5000);

FTP.Disconnect(False);
加注
结束

退出;
结束

结果:= True;
结束
结束

请注意,大多数平台支持在每个连接的基础上启用TCP保持活动(keep-alives是TCP规范的一部分),设置保持活动间隔是平台特定的。此时,Indy支持在以下位置设置间隔:




  • Windows 2000+和WinCE 4.x +,适用于Win32和.NET

  • 当Indy用于Kylix

  • Unix / Linux和NetBSD时,当Indy用于FreePascal时,Linux。



    • 否则,根据操作系统配置,操作系统默认间隔会被使用,这可能或可能不会太大,这取决于操作系统配置。



      此时,除了Windows之外,Delphi FireMonkey中的间隔不可定制。



      如果特定平台支持设置自定义TCP保活间隔,但是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天全站免登陆