2

Я выполняю SFTP, чтобы поместить файл в новый внешний клиент. У нас есть около 10 других клиентов, некоторые внутренние и некоторые внешние, которые мы загружаем без проблем. Этот новый клиент находится с нами в VPN (некоторые другие наши клиенты также находятся у нас с VPN, но не со всеми) и отвечает на команду пут EOF после передачи около 40 М файла 50 МБ. Это произошло на 10 из последних 14 казней.

За исключением двух случаев, EOF принимается один раз, при первом запуске дня, около 5:00 утра. Когда я снова запускаю SFTP через несколько минут, команда put работает и передается полный файл. Для остальной части дня это работает также. В том же исполнении мы также отправляем два других файла, один из которых равен 1М, а другой - 10М. Мой скрипт сортирует список файлов по размеру файла, поэтому меньшие файлы отправляются первыми. Это все в рамках одной и той же связи. Мой скрипт сначала подключается, отправляет пароль и проверяет, что он принят, выполняет изменение каталога, команду ls и команду df. Затем в скрипте есть цикл, чтобы поместить все файлы, которые перечислены. Первые два файла помещаются успешно, а затем третий, но он завершается неудачно с EOF.

В двух случаях, которые являются исключением, я получил EOF со второй попытки, а затем пут успешно завершился с третьей попытки. в целом, это было 10 из последних 14 пробежек. Остальные четыре пробежки сработали с первого раза (дня). Эта работа выполняется примерно в одно и то же время (с 4:50 до 5:15). Я поговорил с клиентом и немного изменил время, чтобы обойти интервалы, в течение которых они запускают процесс мониторинга папок. Но это не имеет значения.

Мы работаем с OpenSSH_6.0p1 в AIX 7.1, а они работают с OpenSSH_7.4p1 в CentOS Linux 7.4. Дважды я пытался запустить SFTP из командной строки с подробным параметром (-vvv), и пут работал в этих двух случаях. Я обновлю свой скрипт, чтобы каким-то образом записать подробный вывод в файл журнала. Но я получил этот EOF как в сценарии, так и в командной строке, поэтому я не думаю, что это сценарий. И я могу сначала отправить два других файла, так что это не просто проблема с первым отправленным файлом. И это не просто проблема с размером файла, даже если он не работает только с файлом 50M, потому что он отлично работает через несколько минут.

Поэтому некоторые из моих вопросов - что может вызвать EOF для операции put? Я нашел спецификацию RFC версии 2 для SFTP, и я не вижу упоминания EOF для операций put, только для операций get. Может ли быть какой-то фоновый процесс в CentOS Linux, который может вызвать EOF при передаче большого файла? Передача занимает около 25 секунд. Может ли это быть сетевой ошибкой? Может ли качество сети сначала быть плохим, но через несколько минут автоматически перейти на более высокий уровень качества? Клиент 2700 миль. Спасибо за любые комментарии. Питер

ОБНОВЛЕНИЕ: поставщик отправил мне свой журнал отладки SFTP, и я не вижу какой-либо конкретной причины:

28745   debug1: request 786: write "/upload/X_Y_Z_file_20180315040147.txt" (handle 0) off 25329664 len 32768
28745   debug3: request 786: sent status 0
28745   sent status Success
28745   debug1: request 787: write "/upload/X_Y_Z_file_20180315040147.txt" (handle 0) off 25362432 len 32768
28745   debug3: request 787: sent status 0
28745   sent status Success
28745   debug1: request 788: write "/upload/X_Y_Z_file_20180315040147.txt" (handle 0) off 25395200 len 32768
28745   debug3: request 788: sent status 0
28745   sent status Success
28745   debug1: read eof
28745   forced close "/upload/X_Y_Z_file_20180315040147.txt" bytes read 0 written 25427968
28745   session closed for local user abc from [1.2.3.4]

Изменить: я не думаю, что мой сценарий является проблемой (но я знаю, как это звучит), потому что я получаю ту же проблему, независимо от того, использую ли я сценарий или запускаю команду SFTP из командной строки. Сценарий использует EXPECT для перехвата ответа SFTP, который является одним из '100%', или EOF или TIMEOUT. Я приложу сценарий, как только выясню, как это сделать.

Редактировать: вот скрипт для sftp:

#! /usr/bin/ksh
# The following line is seen as a continuecomment by Tcl\
exec $QUOVADX_INSTALL_DIR/integrator/bin/hcitcl "$0" ${1+"$@"}

# subroutines first
proc gts {} { 
   set tt [clock format [clock seconds] -format {%y/%m/%d %H:%M:%S} ]
   set ms [format %03d [expr {[clock clicks -milliseconds] % 1000}]]
   return $tt.$ms 
}

# proc to write to debug file
proc debugw {msg} {
  global debugfile
  set fh [open $debugfile a] ; puts $fh "$msg" ; close $fh
}


set frdir [lindex $argv 0]
set lfile [lindex $argv 1]
set todir [lindex $argv 2]
set site  [lindex $argv 3]

set debug $::env(cldebug)
set parmfile $::env(clparmfile)
set module "sftp_put_list"

# get debugfilename
if {$debug} {set debugfile [exec getdebugfile.tcl] ; global debugfile}
if {$debug} {debugw "[gts] $module start"}
if {$debug} {debugw "[gts] $module processing files in list $lfile"}

# get SFTP connection parms from parmfile using $site as key
set hfile [open $parmfile r] ; set data [read $hfile] ; close $hfile
set lList [split $data "\n"] ; set pos [lsearch -regexp $lList "^key_sftp_$site"]
lassign [split [lindex $lList $pos] " "] key port url pass
if {$debug > 1} {debugw "[gts] $module key: $key port $port url $url pass $pass"}

# url with vertical bar means the first part is the userid and second is the url
set userList [split "$url" "|"] ; lassign $userList user url
if {$debug} {debugw "[gts] $module userList: $userList"}
set ullen [llength $userList]
if {$debug} {debugw "[gts] $module userList length: $ullen"} 


log_user 0

# start/spawn the sftp process - either simple user url or separate user  
if {[llength $userList] eq 1} {
  set hname "$user"
  spawn /usr/bin/sftp -o Port=$port -o ConnectTimeout=60  $user 
  if {$debug} {debugw "[gts] $module connection uses simple user url $user"}
} else {
  set hname "$url"
  spawn /usr/bin/sftp -o Port=$port -o ConnectTimeout=60 -o User=$user $url
  if {$debug} {debugw "[gts] $module connection uses separate user $user and url $url"}
}

set pass_word_prompt "*?assword:*"
set sftp_prompt "*?ftp>"
set timeout 300

set sftp_put_timeout 1000
set prompt_timeout 300

#--------------------------------------------------------------
# first, wait for the password prompt - if a password is required!
#--------------------------------------------------------------
if {$pass ne "#"} {
  expect {
    eof {
       echo "sftp EOF waiting for password prompt|NA"
       if {$debug} {debugw "[gts] $module EOF received waiting for password prompt from $hname - exit 8"}
       exit 8
    }
    "$pass_word_prompt" {
       if {$debug} {debugw "[gts] $module password prompt received and answered"}
    }
    timeout {
       echo "sftp timeout waiting for password prompt|NA"
       if {$debug} {debugw "[gts] $module timeout waiting for password prompt from $hname - exit 8"}
       exit 8
    }
  }

  set timeout $prompt_timeout
  send "$pass\r"
  expect {
    eof {
      echo "sftp EOF waiting for command prompt after entering password|NA"
      if {$debug} {debugw "[gts] $module EOF received waiting for command prompt from $hname after entering password - exit 8"}
      exit 8
    }
    "$sftp_prompt" {
      if {$debug} {debugw "[gts] $module received command prompt; will enter LCD command"}
    }
    timeout {
      echo "sftp timeout after $prompt_timeout seconds waiting for command prompt after entering password|NA"
      if {$debug} {debugw "[gts] $module timeout after $prompt_timeout seconds waiting for command prompt from $hname after entering password - exit 8"}
      exit 8
    }
  }
} else {
  expect {
    eof {
      echo "sftp EOF waiting for command prompt after logging on|NA"
      if {$debug} {debugw "[gts] $module EOF received waiting for command prompt from $hname after logging on - exit 8"}
      exit 8
    }
    "$sftp_prompt" {
      if {$debug} {debugw "[gts] $module received command prompt; will enter LCD command"}
    }
    timeout {
      echo "sftp timeout after $prompt_timeout seconds waiting for command prompt after logging on|NA"
      if {$debug} {debugw "[gts] $module timeout after $prompt_timeout seconds waiting for command prompt from $hname after logging on - exit 8"}
      exit 8
    }
  }

}

#--------------------------------------------------------------
# lcd to local directory from where you will send the file(s)
#--------------------------------------------------------------
set timeout $prompt_timeout
send "lcd $frdir\r"
expect {
   eof {
     echo "sftp EOF waiting for command prompt after entering LCD|NA"
     if {$debug} {debugw "[gts] $module EOF received waiting for command prompt from $hname after entering LCD - exit 8"}
     exit 8
   }
   "$sftp_prompt" {
     if {$debug} {debugw "[gts] $module received command prompt after LCD ; will enter CD command"}
   }
   timeout {
     echo "sftp timeout after $prompt_timeout seconds waiting for command prompt after entering LCD|NA"
     if {$debug} {debugw "[gts] $module timeout after $prompt_timeout seconds waiting for command prompt from $hname after entering LCD - exit 8"}
     exit 8
   }
}

#--------------------------------------------------------------
# cd to remote directory where you will put the file(s)
#--------------------------------------------------------------
set timeout $prompt_timeout
send "cd $todir\r"
expect {
  eof {
    echo "sftp EOF waiting for command prompt after entering CD command|NA"
    if {$debug} {debugw "[gts] $module EOF received waiting for prompt from $hname after entering CD command - exit 8"}
    exit 8
  }
  "No such file or directory" {
    echo "remote directory $todir not found or not accessible|NA"
    if {$debug} {debugw "[gts] $module remote directory $todir not found or not accessible at $hname - exit 8"}
    exit 8
  }
  "$sftp_prompt" {
    if {$debug} {debugw "[gts] $module received command prompt after CD; will execute PWD command"}
  }
  timeout {
    echo "sftp timeout after $prompt_timeout seconds waiting for prompt after entering CD command|NA"
    if {$debug} {debugw "[gts] $module timeout after $prompt_timeou seconds waiting for command prompt from $hname after entering CD command - exit 8"}
    exit 8
  }
}


#--------------------------------------------------------------
# print working directory
#--------------------------------------------------------------
set timeout $prompt_timeout
send "pwd\r"
expect {
  eof {
    echo "sftp EOF waiting for command prompt after entering pwd command|NA"
    if {$debug} {debugw "[gts] $module EOF received waiting for prompt from $hname after entering pwd command - exit 8"}
    exit 8
  }
  "$sftp_prompt" {
    set response $expect_out(0,string)
    set rwd [lindex [split "$response" ":"] 1] 
    set rwd [lindex [split "$rwd" "\r" ] 0]
    if {$debug} {debugw "[gts] $module pwd: $rwd"}
    if {$debug} {debugw "[gts] $module received command prompt after PWD; will execute DIR command"}
  }
  timeout {
    echo "sftp timeout after $prompt_timeout seconds waiting for prompt after entering pwd command|NA"
    if {$debug} {debugw "[gts] $module timeout after $prompt_timeout seconds waiting for command prompt from $hname after entering pwd command - exit 8"}
    exit 8
  }
}

#--------------------------------------------------------------
# directory list
#--------------------------------------------------------------
set timeout $prompt_timeout
send "ls -l\r"
expect {
  eof {
    echo "sftp EOF waiting for command prompt after entering dir command|NA"
    if {$debug} {debugw "[gts] $module EOF received waiting for prompt from $hname after entering dir command - exit 8"}
    exit 8
  }
  "$sftp_prompt" {
    set response $expect_out(0,string)
    set dir [lrange [split "$response" "\r"] 1 end-1] 
    set dir [string map {\{ "" \} ""} "$dir"]
    set dir [string trim "$dir"]
    if {$debug} {debugw "[gts] $module dir: \r$dir"}
    if {$debug} {debugw "[gts] $module received command prompt after DIR; will execute PUT command"}
  }
  timeout {
    echo "sftp timeout after $prompt_timeout seconds waiting for prompt after entering dir command|NA"
    if {$debug} {debugw "[gts] $module timeout after $prompt_timeout seconds waiting for command prompt from $hname after entering dir command - exit 8"}
    exit 8
  }
}

#------------------------------------------------
# loop through list of files - put each file to remote sftp server
#------------------------------------------------
set hfile [open $lfile r] ; set data [read $hfile] ; close $hfile
set flist [string map {"\n" " "} "$data" ] ; set flist [string trim "$flist"] 
set flist [split $flist " "] ; set nfiles [llength $flist]
if {$debug} {debugw "[gts] $module $nfiles file names found in list file $lfile"}

set ct 0
while {$ct < $nfiles} {
  set nextfile [lindex $flist $ct]
  if {$debug} {debugw "[gts] $module starting SFTP Put for filename $nextfile"}
  set dct [expr $ct + 1]

  set timeout $sftp_put_timeout
  send "put $nextfile\r"
  expect {
    eof {
      set response $expect_out(0,string)
      echo "sftp EOF waiting for command prompt after entering put command|$nextfile"
      if {$debug} {debugw "[gts] $module put file $dct - $nextfile failed"}
      if {$debug} {debugw "[gts] $module EOF response from Put:\n$response"}
      if {$debug} {debugw "[gts] $module EOF received waiting for prompt from $hname after entering put command - exit 8"}
      exit 8
    }
    "100%*$sftp_prompt" {
      if {$debug} {debugw "[gts] $module received PUT 100% and command prompt after PUT"}
      if {$debug} {debugw "[gts] $module put file $dct - $nextfile succeeded"}
    }
    timeout {
      echo "sftp timeout after $sftp_put_timeout seconds waiting for prompt after entering put command|$nextfile"
      if {$debug} {debugw "[gts] $module timeout after $sftp_put_timeout seconds waiting for command prompt from $hname after entering put command - exit 8"}
      exit 8
    }
  }

  incr ct
}

#--------------------------------------------------------------
# post transfer directory list
#--------------------------------------------------------------
set timeout $prompt_timeout
send "ls -l\r"
expect {
  eof {
    echo "sftp EOF waiting for command prompt after entering dir command|NA"
    if {$debug} {debugw "[gts] $module EOF received waiting for prompt from $hname after entering dir command - exit 8"}
    exit 8
  }
  "$sftp_prompt" {
    set response $expect_out(0,string)
    set dir [lrange [split "$response" "\r"] 1 end-1] 
    set dir [string map {\{ "" \} ""} "$dir"]
    set dir [string trim "$dir"]
    if {$debug} {debugw "[gts] $module dir: \r$dir"}
    if {$debug} {debugw "[gts] $module received command prompt after dir; will execute QUIT command"}
  }
  timeout {
    echo "sftp timeout after $prompt_timeout seconds waiting for prompt after entering dir command|NA"
    if {$debug} {debugw "[gts] $module timeout after $prompt_timeout seconds waiting for command prompt from $hname after entering dir command - exit 8"}
    exit 8
  }
}

#---------------------------------------------------------
# sftp checks for spawn EOF and will terminate prematurely
# unless you add extra expect command
#---------------------------------------------------------
set timeout $prompt_timeout
send "quit\r"
expect {
  eof {
    if {$debug} {debugw "[gts] $module EOF expected and received after QUIT command|NA"}
    exit 0
  }
  "$sftp_prompt" {
    echo "unexpected sftp prompt after entering QUIT command - should be EOF|NA"
    if {$debug} {debugw "[gts] $module unexpected sftp prompt after entering QUIT command - should be EOF - exit 2"}
    exit 2
  }
  timeout {
    echo "sftp timeout after $prompt_timeout seconds waiting for EOF after QUIT command|NA"
    if {$debug} {debugw "[gts] $module sftp timeout after $prompt_timeout seconds waiting for EOF after QUIT command - exit 8"}
    exit 8
  }
}

exit 0

0