2013-09-04 2 views
8

Я пытаюсь написать письмо ftp-клиента против Filezilla, поддерживающего активный режим с помощью node.js. Я новичок в ftp и node.js. Я думал, что смогу хорошо понять связь с tcp-сокетом и протокол ftp, выполнив это упражнение. Кроме того, node-ftp a jsftp, похоже, не поддерживают активный режим, поэтому я думаю, что это будет приятное (хотя и редко используемое) дополнение к npm.Активный FTP-клиент для Node.js

У меня есть некоторые доказательства кода концепции, который работает хотя бы иногда, но не все время. В случае, когда он работает, клиент загружает файл с именем file.txt с текстом «привет». Когда он работает, я получаю это:

220-FileZilla Server version 0.9.41 beta 
220-written by Tim Kosse ([email protected]) 
220 Please visit http://sourceforge.net/projects/filezilla/ 

331 Password required for testuser 

230 Logged on 

listening 
200 Port command successful 

150 Opening data channel for file transfer. 

server close 
226 Transfer OK 

half closed 
closed 

Process finished with exit code 0 

Когда он не работает, я получаю это:

220-FileZilla Server version 0.9.41 beta 
220-written by Tim Kosse ([email protected]) 
220 Please visit http://sourceforge.net/projects/filezilla/ 

331 Password required for testuser 

230 Logged on 

listening 
200 Port command successful 

150 Opening data channel for file transfer. 

server close 
half closed 
closed 

Process finished with exit code 0 

Таким образом, я не получаю 226, и я не уверен, почему я получаю непоследовательные результаты.

Простите плохо написанный код. Я рефакторинга, когда я уверен, что я понимаю, как это должно работать .:

var net = require('net'), 
    Socket = net.Socket; 

var cmdSocket = new Socket(); 
cmdSocket.setEncoding('binary') 

var server = undefined; 
var port = 21; 
var host = "localhost"; 
var user = "testuser"; 
var password = "Password1*" 
var active = true; 
var supplyUser = true; 
var supplyPassword = true; 
var supplyPassive = true; 
var waitingForCommand = true; 
var sendFile = true; 

function onConnect(){ 

} 

var str=""; 
function onData(chunk) { 
    console.log(chunk.toString('binary')); 

    //if ftp server return code = 220 
    if(supplyUser){ 
     supplyUser = false; 
     _send('USER ' + user, function(){ 

     }); 
    }else if(supplyPassword){ 
     supplyPassword = false; 
     _send('PASS ' + password, function(){ 

     }); 
    } 
    else if(supplyPassive){ 
     supplyPassive = false; 
     if(active){ 
      server = net.createServer(function(socket){ 
       console.log('new connection'); 
       socket.setKeepAlive(true, 5000); 

       socket.write('hi', function(){ 
        console.log('write done'); 
       }) 

       socket.on('connect', function(){ 
        console.log('socket connect'); 
       }); 

       socket.on('data', function(d){ 
        console.log('socket data: ' + d); 
       }); 

       socket.on('error', function(err){ 
        console.log('socket error: ' + err); 
       }); 

       socket.on('end', function() { 
        console.log('socket end'); 
       }); 

       socket.on('drain', function(){ 
        console.log('socket drain'); 

       }); 

       socket.on('timeout', function(){ 
        console.log('socket timeout'); 

       }); 

       socket.on('close', function(){ 
        console.log('socket close'); 

       }); 
      }); 

      server.on('error', function(e){ 
       console.log(e); 
      }); 

      server.on('close', function(){ 
       console.log('server close'); 
      }); 

      server.listen(function(){ 
       console.log('listening'); 

       var address = server.address(); 
       var port = address.port; 
       var p1 = Math.floor(port/256); 
       var p2 = port % 256; 

       _sendCommand('PORT 127,0,0,1,' + p1 + ',' + p2, function(){ 

       }); 
      }); 
     }else{ 
      _send('PASV', function(){ 

      }); 
     } 
    } 
    else if(sendFile){ 
     sendFile = false; 

     _send('STOR file.txt', function(){ 

     }); 
    } 
    else if(waitingForCommand){ 
     waitingForCommand = false; 

     cmdSocket.end(null, function(){ 

     }); 

     if(server)server.close(function(){}); 
    } 
} 

function onEnd() { 
    console.log('half closed'); 
} 

function onClose(){ 
    console.log('closed'); 
} 

cmdSocket.once('connect', onConnect); 
cmdSocket.on('data', onData); 
cmdSocket.on('end', onEnd); 
cmdSocket.on('close', onClose); 

cmdSocket.connect(port, host); 

function _send(cmd, callback){ 
    cmdSocket.write(cmd + '\r\n', 'binary', callback); 
} 

Кроме того, это server подходит, или я должен сделать это какой-то другой способ?

EDIT: Я изменил обратный вызов в server.listen, чтобы использовать случайный порт. Это удалило 425, которые я получал ранее. Тем не менее, я все еще не получаю согласованное поведение с передачей файлов.

+0

Вы проверили это? https://github.com/sergi/jsftp. – yeya

ответ

1

Поток для активных передач в режиме FTP идет примерно так:

  • преамбулы соединения (USER/PASS)
  • Установите клиент локальный сокет для данных
  • Inform сервера этого сокета (PORT)
  • Сообщите серверу открыть удаленный файл для записи (STOR)
  • Начните писать данные из сокета данных, установленного выше (socket.write())
  • Закройте поток на стороне клиента (socket.end()) для завершения передачи файлов
  • Сообщить сервер вы сделали (QUIT)
  • очистить любые открытые сокеты и сервера на клиенте

Так как только вы сделали это:

else if(sendFile){ 
    sendFile = false; 

    _send('STOR file.txt', function(){ 

    }); 
} 

сервер будет реагировать с 150 говорит, что он подключен к установочному гнезду данных и готов к приему данных.

Одним из улучшений, позволяющих упростить рассуждение о выполнении на этом этапе, было бы изменение потока управления для работы с анализируемым кодом ответа, а не с предопределенными bools.

function onData(chunk) { 
    console.log(chunk); 
    var code = chunk.substring(0,3); 

    if(code == '220'){ 

вместо:

function onData(chunk) { 
    console.log(chunk.toString('binary')); 

    //if ftp server return code = 220 
    if(supplyUser){ 

Затем вы можете добавить раздел для отправки данных:

//ready for data 
else if (code == '150') { 
    dataSocket.write('some wonderful file contents\r\n', function(){}); 
    dataSocket.end(null, function(){}); 
} 

И немного больше, чтобы очистить:

//transfer finished 
else if (code == '226') { 
    _send('QUIT', function(){ console.log("Saying Goodbye");}); 
} 

//session end 
else if (code == '221') { 
    cmdSocket.end(null, function(){}); 
    if(!!server){ server.close(); } 
} 

Очевидно, что вещи будут усложняться, если вы nding несколько файлов и т. д., но это должно привести к более надежной проверке концепции:

var net = require('net'); 
    Socket = net.Socket; 

var cmdSocket = new Socket(); 
cmdSocket.setEncoding('binary') 

var server = undefined; 
var dataSocket = undefined; 
var port = 21; 
var host = "localhost"; 
var user = "username"; 
var password = "password" 
var active = true; 

function onConnect(){ 
} 

var str=""; 
function onData(chunk) { 
    console.log(chunk.toString('binary')); 
    var code = chunk.substring(0,3); 
    //if ftp server return code = 220 
    if(code == '220'){ 
     _send('USER ' + user, function(){ 
     }); 
    }else if(code == '331'){ 
     _send('PASS ' + password, function(){ 
     }); 
    } 
    else if(code == '230'){ 
     if(active){ 
      server = net.createServer(function(socket){ 
       dataSocket = socket; 
       console.log('new connection'); 
       socket.setKeepAlive(true, 5000); 

       socket.on('connect', function(){ 
        console.log('socket connect'); 
       }); 

       socket.on('data', function(d){ 
        console.log('socket data: ' + d); 
       }); 

       socket.on('error', function(err){ 
        console.log('socket error: ' + err); 
       }); 

       socket.on('end', function() { 
        console.log('socket end'); 
       }); 

       socket.on('drain', function(){ 
        console.log('socket drain'); 
       }); 

       socket.on('timeout', function(){ 
        console.log('socket timeout'); 
       }); 

       socket.on('close', function(){ 
        console.log('socket close'); 
       }); 
      }); 

      server.on('error', function(e){ 
      console.log(e); 
      }); 

      server.on('close', function(){ 
       console.log('server close'); 
      }); 

      server.listen(function(){ 
       console.log('listening'); 

       var address = server.address(); 
       var port = address.port; 
       var p1 = Math.floor(port/256); 
       var p2 = port % 256; 

       _send('PORT 127,0,0,1,' + p1 + ',' + p2, function(){ 

       }); 
      }); 
     }else{ 
      _send('PASV', function(){ 

      }); 
     } 
    } 
    else if(code == '200'){ 
     _send('STOR file.txt', function(){ 

     }); 
    } 
    //ready for data 
    else if (code == '150') { 
    dataSocket.write('some wonderful file contents\r\n', function(){}); 
    dataSocket.end(null, function(){}); 
    } 

    //transfer finished 
    else if (code == '226') { 
    _send('QUIT', function(){ console.log("Saying Goodbye");}); 
    } 

    //session end 
    else if (code == '221') { 
    cmdSocket.end(null, function(){}); 
    if(!!server){ server.close(); } 
    } 
} 

function onEnd() { 
    console.log('half closed'); 
} 

function onClose(){ 
    console.log('closed'); 
} 

cmdSocket.once('connect', onConnect); 
cmdSocket.on('data', onData); 
cmdSocket.on('end', onEnd); 
cmdSocket.on('close', onClose); 

cmdSocket.connect(port, host); 

function _send(cmd, callback){ 
    cmdSocket.write(cmd + '\r\n', 'binary', callback); 
} 
Смежные вопросы