之前忙着备考,FTP虽然也有敲,但是没有能每次总结。
这两天考试也差不多了,今天好好写了一些,感觉有很大进展和收获,有必要记录一下。
FTP服务端程序的运行流程主要是这样的。主类监听21号端口,当有用户连接是创建一个新线程。线程体run()通过socket(套接字)的输入流接受命令,再通过一个分析命令的函数,将命令转换成int型的标识(总共33个命令),同时把命令所带的参数进行适当的规范化。然后根据最近接收的命令的标识调用相应的函数,完成相应的功能,然后run()向客户端返回执行过程中产生的返回消息(如:“200 command okay.”)。
先来讲一下LIST命令的实现。
其实,LIST方法是要重新在服务端和客户端之间建立一个连接,专门来传输文件列表的数据(端口21就让它专心监听和返回命令、消息)。
不仅是LIST需要建立新的连接,其他许多命令也同样需要。那么,怎么在两者之间建立新的连接才能保证数据的顺利传输呢?这时候,就要谈一谈PORT和PASV的异同了。
在PASV出现之前,就是用的PORT的连接方式,也就是主动连接,客户端发送一个大于1024的端口号给服务端,服务端“主动”使用服务器的20号端口连接客户端的指定端口,从而传输数据。
服务端接受PORT命令后,要记录下这个指定的IP和端口。PORT命令的规定参数形式是:PORT h1,h2,h3,h4,p1,p2 ,全都是整数。h1-h4把逗号换成句号,就是IP地址;p1和p2,通过公式p1*256+p2,得到的就是端口号。一般是要1024<port<65536。
但是,当你是在一个局域网中,你的主机的IP地址只有局域网内的计算机才能识别。如果你这时候发送ip地址给服务端,服务端就无法连接到你的计算机了。这时候,就需要服务端“被动”地与客户端连接,就是客户端告诉服务端,我要你被动连接,服务端就发送给客户端自己的IP地址和一个可用的端口号。客户端通过返回的消息中取到两者,然后向服务端发起连接,进行数据的传输。
这就是PASV,被动方式。简单的说,就是和PORT做了相反的事。
再看LIST命令。这时候有一个标记表明是PORT还是PASV方式。LIST方法判断这个标识,创建不同的socket套接字。贴个代码吧
boolean cmdLIST(){
//区分主动与被动模式,在这里FTRANS_PORT表示主动,FTRANS_PASV是被动
try{
if(trans==FtpState.FTRANS_PORT){
dsocket = new Socket(remoteHost,remotePort,InetAddress.getLocalHost(),20);
//前面两个是port命令指定,记录下来的ip和端口号
}else if(trans==FtpState.FTRANS_PASV){
ssocket=new ServerSocket(localPort);//pasv时记录下来的端口号
dsocket = ssocket.accept();
}else
return false;
}catch(IOException e){
//监听出错,做出反应
return false;
}
//--------------
try{
PrintWriter dout = new PrintWriter(dsocket.getOutputStream(),true);
out.println("125 Data connection already open; Transfer starting.");
File f=new File(dir); // 当前的目录位置
String[] files=f.list(); //取到当前目录下的文件和文件夹
String fileInfo;
for(int i =0; i<files.length;i++){
File f1=new File(rootdir+dir+"//"+files[i]);
//规范文件修改时间的格式
long flm=f1.lastModified();
Date fDate=new Date(flm);
SimpleDateFormat time=new SimpleDateFormat("MM-dd-yyyy HH:mm:ss");
fileInfo=time.format(fDate)+" ";
//------------
if(f1.isDirectory()){
fileInfo=fileInfo+"<DIR> ";
}else{
fileInfo=fileInfo+f1.length()+" ";
}
dout.println(fileInfo+files[i]);
//在Windows下文件信息的数组的格式是“日期 时间 文件大小(如果是文件夹就是<DIR>) 文件名”
}
dout.close();
dsocket.close();
reply = "226 Transfer complete !";
return true;
}catch(IOException e){
System.out.println("User don't receive imformation");
reply = "451 Requested action aborted: local error in processing";
return false;
}
}//end of cmdLIST