2013-09-04 8 views
8

Staram się pisać o kliencie ftp przeciwko Filezilli, który obsługuje tryb aktywny za pomocą node.js. Jestem nowy w ftp i node.js. Pomyślałem, że dzięki temu ćwiczeniu mogę uzyskać dobre zrozumienie komunikacji gniazd tcp i protokołu ftp. Ponadto, node-ftp i jsftp wydaje się nie obsługiwać trybu aktywnego, więc myślę, że będzie to miły (choć rzadko używany) dodatek do npm.Aktywny klient FTP dla Node.js

Mam kod dowodu z koncepcji, który działa co najmniej czasami, ale nie cały czas. W przypadku, gdy to działa, klient przesyła plik o nazwie file.txt z tekstem "hi". Kiedy to działa, mam to:

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 

Kiedy to nie działa, mam to:

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 

Więc ja nie dostaję 226, a nie jestem pewien dlaczego dostaję niespójne wyniki.

Przebacz źle napisany kod. Będę byłaby raz jestem pewien rozumiem, jak to powinno działać .:

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); 
} 

Również jest server właściwe, albo zrobić to w jakiś inny sposób?

EDYTOWANIE: Zmieniono wywołanie zwrotne w pliku server.listen, aby użyć losowego portu. To usunęło wcześniej 425. Jednak nadal nie uzyskuję spójnego zachowania podczas przesyłania plików.

+0

Czy sprawdziłeś ten? https://github.com/sergi/jsftp. – yeya

Odpowiedz

1

Przepływ transferów aktywnym trybie FTP idzie mniej więcej tak:

  • Connection preambuła (USER/PASS)
  • Ustanowienie klienta lokalnego gniazda dla danych
  • poinformować serwer tego gniazda (PORT)
  • Poinformuj serwer, aby otworzył zdalny plik do zapisu (STOR)
  • Zacznij pisać Dane z gniazdka danych ustanowionego powyżej (socket.write())
  • zamknąć strumienia od strony klienta (socket.end()), aby zakończyć transfer plików
  • poinformować serwer jesteś gotowe (QUIT)
  • oczyścić wszystkie otwarte gniazda i serwerów na kliencie

Więc raz zrobiłeś to:

else if(sendFile){ 
    sendFile = false; 

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

    }); 
} 

serwer odpowie z 150 mówi, że podłączyło się do ustanowionego gniazda danych i jest gotowe do odbioru danych.

Jedną z ulepszeń, które ułatwiają uzasadnianie wykonania w tym momencie, byłaby zmiana sterowania, aby działał na sparsowanym kodzie odpowiedzi, a nie na wstępnie zdefiniowanych wartościach.

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

    if(code == '220'){ 

zamiast:

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

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

Następnie można dodać sekcję do przesyłania danych:

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

i trochę więcej posprzątać:

//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(); } 
} 

Oczywiście sprawy będą bardziej skomplikowane, jeśli będziesz w se ning wielu plików itp., ale powinno to sprawić, że twoja koncepcja koncepcji będzie działać bardziej niezawodnie:

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); 
}