我正在尝试使用node.js在Filezilla上编写支持活动模式的ftp客户端。我是ftp和node.js的新手。我认为通过这个练习我可以很好地理解tcp套接字通信和ftp协议。此外,node-ftp和jsftp似乎不支持活动模式,所以我认为这对npm来说是一个很好的(虽然很少使用)。
我有一些概念代码证明至少有时会起作用,但并非总是如此。在它工作的情况下,客户端上传名为file.txt
的文件,文本为“hi”。
当它工作时,我明白了:
220-FileZilla Server version 0.9.41 beta
220-written by Tim Kosse (Tim.Kosse@gmx.de)
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 (Tim.Kosse@gmx.de)
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
是否合适,或者我应该采取其他方式吗?
编辑: 我在server.listen中更改了回调以使用随机端口。这已经删除了我之前获得的425。但是,我仍然没有获得文件传输的一致行为。
答案 0 :(得分:1)
活动模式FTP传输的流程大致如下:
USER
/ PASS
)PORT
)STOR
)socket.write()
)socket.end()
)以结束文件传输QUIT
)所以一旦你完成了这个:
else if(sendFile){
sendFile = false;
_send('STOR file.txt', function(){
});
}
服务器将以150
回复说它已连接到您建立的数据套接字并准备接收数据。
此时更容易推断执行的一个改进是更改控制流以对解析的响应代码进行操作,而不是预先定义的bool。
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(); }
}
显然,如果要发送多个文件等,事情会变得更复杂,但这样可以让您的概念证明更可靠地运行:
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);
}