ftp átvitel megszakad nagy fájlok / hosszú átvitel végén

sziasztok

az alábbi jelenséget már sok helyen tapasztaltam, de néha az én "szerveremen" is előfordul, ezért szeretnék utánajárni.

a jelenség: fájlátvitel (legtöbbször feltölteni szoktam, de előfordul letöltésnél is) egy vagy több nagyobb fájlra (de lassú kapcsolaton kisebb fájl is lehet), a lényeg, hogy sokáig tartson az átvitel. miután a feájl feltöltődik (vag le), elér 100%-ig, utána megáll. az ftp kliens szerintem nem kap valami visszajelzést, mondjuk az utolsó bájt vételekor egy "rendben" választ vagy ilyesmi. ekkor a ftp kliens is "megfagy", kilépni is alig lehet. (főleg total commandert használok, de láttam már ilyet gftp-vel és filezillával is)
miközben a kapcsolat látszólag megfagy, másik klienssel be lehet jelentkezni minden további nélkül (tehát nem a ftp szerver fagy le), és látszik, hogy valóban a fájl teljesen feltöltődik, bájtra pontosan.
m,ikor a kliens "lefagy", az abortot buzgón nyomkodva ki lehet lépni, ilyenkor próbál még parancsokat küldeni, de azok sem mennek, olyan, mintha a teljes felépített kapcsolat megbénulna. újrakapcsolódva minden remekül működik tovább.
fogalmam sincs, mitől lehet ez, természetesen a lkogokban semmiféle nyoma nincs.

esetleg a command csatorna hosszú tétlenség miatt bezárja a megnyitott kapcsolatot? vagy esetleg a tűzfalon lezár a csatorna?

van aki tudja a megoldást? előre is köszönöm.

Hozzászólások

szerintem a tűzfal lebontja a nat táblát, ezért akad meg a parancscsatorna.

Nagyon valószínű, hogy erről van szó.

Javaslat: nyergeljük meg a

socket()

függvényt, és ha TCP socket-et hoznak létre, akkor mindjárt állítsunk is be rá TCP keepalive-ot (a felhasználó az első szonda előtti csend idejét ill. a szondák közötti intervallumot egyaránt a

KA_IDLE_SECS

környezeti változóval szabályozhatja).


/*
  $Id: keepalive.c,v 1.3 2009-08-27 23:53:00 lacos Exp $

  Compilation:

    gcc -ansi -pedantic -Wall -Wextra -Wformat=2 \
        -fPIC -shared -o keepalive.so keepalive.c

  Usage:

    LD_PRELOAD=./keepalive.so KA_IDLE_SECS=60 ftp
*/

#define _GNU_SOURCE 1

#include <sys/socket.h>  /* AF_INET */
#include <netinet/in.h>  /* IPPROTO_TCP */
#include <netinet/tcp.h> /* TCP_KEEPIDLE -- Linux-specific */
#include <unistd.h>      /* close() */
#include <errno.h>       /* errno */
#include <stdlib.h>      /* getenv() */
#include <limits.h>      /* INT_MAX */
#include <dlfcn.h>       /* dlsym() */


static int (*real_socket)(int, int, int),
    idlearg;


int
socket(int domain, int type, int protocol)
{
  int sock;

  sock = (*real_socket)(domain, type, protocol);
  if (-1 != sock && AF_INET == domain && SOCK_STREAM == type
      && (0 == protocol || IPPROTO_TCP == protocol)) {
    int optval;

    optval = 1;
    if (0 == setsockopt(sock, SOL_SOCKET, SO_KEEPALIVE, &optval, sizeof optval)
        && 0 == setsockopt(sock, IPPROTO_TCP, TCP_KEEPIDLE, &idlearg,
        sizeof idlearg)
        && 0 == setsockopt(sock, IPPROTO_TCP, TCP_KEEPINTVL, &idlearg,
        sizeof idlearg)) {
      return sock;
    }

    (void)close(sock);
    sock = -1;
    errno = EPROTONOSUPPORT;
  }

  return sock;
}


static void __attribute__((constructor)) /* gcc-specific */
setup(void)
{
  const char *envstr;

  envstr = getenv("KA_IDLE_SECS");
  if (0 != envstr) {
    long envval;
    char *endptr;

    errno = 0;
    envval = strtol(envstr, &endptr, 0);
    if ('\0' != envstr[0] && '\0' == *endptr && 0 == errno && 0L < envval
        && envval <= (long)INT_MAX) {
      void *sym;

      sym = dlsym(RTLD_NEXT, "socket");
      if (0 != sym) {
        idlearg = envval;
        *(void **)&real_socket = sym;
        return;
      }
    }
  }

  abort();
}

Megjegyzések: ha nem Linuxon vagyunk, akkor jó eséllyel a TCP_* socket option-ök helyett a megfelelő (globális) kernelparamétereket kell reszelnünk. Ha nem gcc-t használunk, akkor a shared lib inicializálását máshogyan kell megoldani (esetleg a

setup()

tartalmának

socket()-be ágyazásával

és legelső híváskori futtatásával.)

Ugyan ezt tapasztalom, midnight commanderrel, jellemzően 4-12 GB-s fájlok letöltésekor.