【Java实现FTP服务】笔记6.30——PORT和PASV,还有LIST
之前忙着备考,FTP虽然也有敲,但是没有能每次总结。
这两天考试也差不多了,今天好好写了一些,感觉有很大进展和收获,有必要记录一下。
FTP服务端程序的运行流程主要是这样的。主类监听21号端口,当有用户连接是创建一个新线程。线程体run()通过socket(套接字)的输入流接受命令,再通过一个分析命令的函数,将命令转换成int型的标识(总共33个命令),同时把命令所带的参数进行适当的规范化。然后根据最近接收的命令的标识调用相应的函数,完成相应的功能,然后run()向客户端返回执行过程中产生的返回消息(如:“200 command okay.”)。
其实FTP的实现,很多都在使用Java中File类的一些方法。其中一些常用的可以参看->http://shhider.blog.163.com/blog/static/21317619620135227645696/
先来讲一下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
差不多这样吧,Java仍在学习中……欢迎交流!新浪微博:@施浩宏GT