delphi - Indy error 10038 "Operación del zócalo en un zócalo" después de 61 segundos de inactividad
ftp delphi-xe7 (1)
FTP utiliza conexiones de socket múltiples, una para comandos / respuestas y conexiones separadas para cada transferencia.
La causa más probable del error de socket es un proxy / enrutador / cortafuegos que no TIdFTP
FTP, que se encuentra entre TIdFTP
y el servidor FTP está cerrando la conexión de comando después de un breve período de inactividad. Durante el Unpack()
(o la pausa manual), no se transmiten comandos / respuestas en la conexión de comando, está inactiva y, por lo tanto, está sujeta a ser cerrada por un tiempo de espera en dicho proxy / enrutador / cortafuegos.
Durante una transferencia, la conexión de comando permanece inactiva, no se transmiten comandos / respuestas de FTP (a menos que se cancele la transferencia), hasta que se complete la transferencia. Un proxy / enrutador / cortafuegos que no conozca el FTP puede cerrar prematuramente la conexión de comando durante este tiempo.
Para evitar eso, TIdFTP
tiene una propiedad NATKeepAlive
que puede habilitar TCP keep-alives en la conexión de comando mientras está inactivo. Esto generalmente evita cierres prematuros.
Sin embargo, cuando no hay transferencia en curso, TIdFTP
desactiva TCP-keep-alives en la conexión de comando si NATKeepAlive.UseKeepAlive
es True. TIdFTP
utiliza TCP keep-alives solo durante las transferencias, con la suposición de que no va a realizar largas demoras entre los comandos de FTP. Si necesita retrasar por un tiempo, cierre la conexión FTP o envíe un comando FTP a intervalos regulares (como llamar a TIdFTP.Noop()
en un temporizador / hilo).
Alternativamente, puede intentar activar manualmente keep-alive TCP después de conectarse al servidor, y establecer NATKeepAlive.UseKeepAlive
en False para que TIdFTP
no desactive automáticamente las keep-alives después de cada transferencia, por ejemplo:
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;
Tenga en cuenta que, si bien la mayoría de las plataformas admite la activación de TCP keep-alives por conexión (keep-alives son parte de la especificación TCP), la configuración de intervalos de keep-alive es específica de la plataforma. En este momento, Indy admite establecer los intervalos en:
- Windows 2000+ y WinCE 4.x +, para Win32 y .NET
- Linux, cuando se usa Indy en Kylix
- Unix / Linux y NetBSD, cuando Indy se usa en FreePascal.
De lo contrario, se utilizan los intervalos predeterminados del sistema operativo, que pueden ser o no demasiado grandes en esta situación, según la configuración del sistema operativo.
En este momento, los intervalos no se pueden personalizar en Delphi FireMonkey, a excepción de Windows.
Si una plataforma en particular admite la configuración de intervalos TCP keep-alive personalizados, pero Indy no los implementa en SetKeepAliveValues()
, puede usar TIdFTP.Socket.Binding.SetSockOpt()
lugar para establecer los valores manualmente según sea necesario. Muchas plataformas admiten TCP_KEEPIDLE
/ TCP_KEEPINTVL
o opciones de socket equivalentes.
Quiero descargar algunos archivos grandes (GB) desde un servidor FTP. La descarga del primer archivo siempre funciona. Luego, cuando intento obtener el segundo archivo, obtengo:
"Socket Error # 10038. Operación del zócalo en no zócalo".
El error está en ''Obtener''. Después de ''Obtener'' veo estos mensajes (a través del evento de estado de FTP):
Starting FTP transfer
Disconnecting.
Disconnected.
El código es así:
{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;
El código completo aquí: http://pastebin.com/RScj86R8 (PAS) o aquí https://ufile.io/26b54 (ZIP)
Creo que el problema aparece después de llamar a ''Unpack'', que toma unos minutos.
ACTUALIZACIÓN: CONFIRMADO: el problema aparece después de llamar a ''Unpack''. Eliminé la llamada y todo está bien. Pausar (dormir o romper el punto) el programa entre descargas por un tiempo (creo que por más de 60 segundos) creará el mismo problema.