免费注册 查看新帖 |

Chinaunix

  平台 论坛 博客 文库
最近访问板块 发新帖
查看: 1589 | 回复: 0
打印 上一主题 下一主题

JAVA实现FTP协议中的服务器和客户端 [复制链接]

论坛徽章:
0
跳转到指定楼层
1 [收藏(0)] [报告]
发表于 2008-04-15 15:59 |只看该作者 |倒序浏览

                                一、实验环境
编程语言:Java2.0(运行在JVM(Java Virsual Machine))
开发工具:eclipce3.2
测试环境:局域网
二、实验目的
FTP最早出现于ARPANET,至今仍是Internet中相当重要的应用之一.用户可以通过浏览器下载或上传文件.
本实验涵盖了C/S体系结构和Socket编程.通过本实验深入地了解FTP的服务器端和客户端的工作流程,学习Socket在网络编程中的各种应用.。
三、Ftp服务器的编写如下:
/**
* 此程序是Ftp服务端程序
* 步骤如下:
* 1、用ServerSocket建立服务器端
* 2、设置Ftp的文件共享目录,所有该目录下的文件对客户端是可操作的
* 3、为每一个连接服务器的客户端建立一个线程
* 4、发送服务器的操作消息到客户端,等待客户端的命令输入
* 5、如果是“ls”则将共享的文件目录通过File类的list()方法发送给客户端
* 6、如果是“get”则首先等待一秒钟来保持同步,然后等待客户端的“ready”
*    收到后再发送“accept”到客户端进行控制流到数据流的交换
* 7、当进行完握手后,进行文件数据的传输,再接收客户端的“finished”确定文件传输完毕
* 8、如果是“quit”则断开该客户端的连接
*
* 作者:蓝蔡
*/
import
java.net.*;
import
java.io.*;
public
class FtpServer
{
         public static void main(String[] args)
         {
                   try
                   {
                            ServerSocket server
= new ServerSocket(6500); //建立FTP服务器邦定6500端口
                            BufferedReader
readPath=new BufferedReader(new InputStreamReader(System.in));
                            System.out.println("/*********************************************************/");
                            System.out.println("The
Ftp is started successful!");
                            System.out.println("The
first thing need to do is set the shared filePath!");
                            System.out.println("For
example:Input: c:/");
                            System.out.println("/*********************************************************/");
                            System.out.print("==>Please
Input:");
                            String
path=readPath.readLine();
                            System.out.println("Tips:The
Ftp is started successfull!");
                            boolean judge=true;
                            while(judge)
                            {
                                     //接收多个客户端为每个客户端建立连接
                                     Socket
client=server.accept();
                                     Thread
th=new Thread(new guest(client,path));
                                     th.start();
                            }
                            server.close();
                   }
                   catch(IOException ioe)
                   {
                            System.out.println("The
port has been used");
                   }
         }
}
//实现多客户连接的线程类编写
class
guest implements Runnable
{
         Socket clientConnect;
         BufferedReader receiveFromClient;
         PrintWriter sentToClient;
         String path;
         public guest(Socket
clientConnect,String path)
         {
                   this.clientConnect=clientConnect;
                   this.path=path;
         }
         //显示共享的所有文件
         public void menu(String path)
         {
                   //传递共享的路径名
                   File menu =new File (path);
                   String[] subFile=menu.list();
                   sentToClient.println("/***************The
shared files is followed***************/");
                  for(int
i=0;i
                   {
                            sentToClient.println(subFile);
                   }
                   sentToClient.println("/***************The
shared files is all***************/");
         }
         //发送给客户端的提示信息
         public void tips()
         {
                   sentToClient.println("/************************************************************/");
                   sentToClient.println("/*************||There
are three commands can be used!||******/");
                   sentToClient.println("/**  ls:  
To show the shared files!                      **/");
                   sentToClient.println("/**  get:
To download the file from server to client!    **/");
                   sentToClient.println("/**  put:
To Upload the file from client to server!      **/");
                   sentToClient.println("/**  quit: To exit the ftpServer!                         **/");
                   sentToClient.println("/************************************************************/");
         }
         public void run()
         {
                   try
                   {
                            //服务器与客户端实现输入输出流的连接
                            InputStream
is=clientConnect.getInputStream();
                            OutputStream
os=clientConnect.getOutputStream();
                            receiveFromClient=new
BufferedReader(new InputStreamReader(is));
                            //PrintWriter 的println会产生适合系统的换行符 比PrintStream好
                            sentToClient=new
PrintWriter(os,true);
                            sentToClient.println("/**************Ftp
log in successful!****************/");
                            while(true)
                            {                                    
                                     tips();//显示所有的命令提示信息
                                     sentToClient.println("==>Please
input command:");
                                     String
text=receiveFromClient.readLine();
                                     //退出Ftp服务器
                                     if(text.equalsIgnoreCase("quit"))
                                     {
                                               sentToClient.println("/**************Ftp
logout successful!****************/");
                                               break;
                                     }
                                     //显示共享的文件
                                     else
if(text.equalsIgnoreCase("ls"))
                                     {
                                               menu(path);
                                     }
                                     //传递文件流到客户端
                                     else
if(text.equalsIgnoreCase("get"))
                                     {
                                               int
content;        
                                               String
fileName=null;
                                               FileInputStream
checkFile = null;
                                       new Thread().sleep(1000);//此处主线程休眠一秒钟,是为了与客户端保持传输同步
                                               sentToClient.println("==>Please
Input the file name:");
                                               //接收客户端的准备标志“ready”
                                               String
handShake=receiveFromClient.readLine();
                                               if(handShake.equalsIgnoreCase("ready"))
                                               {
                                                     sentToClient.println("accept");//告诉客户端已经准备好传输文件
                                                     fileName=receiveFromClient.readLine();//接收待下载的文件名
                                                   String pathName=path+fileName;
                                                   checkFile=new FileInputStream(pathName);
                                                   try
                                                   {
                                                    int
fileLength = 0;
                                                    //读写文件的大小
                                                    while((content=checkFile.read())!=-1)
                                                    {
                                                              fileLength++;
                                                    }
                                                    sentToClient.println(fileLength);//获取待下载文件的大小并发送到客户端
                                                    checkFile.close();
                                                    FileInputStream
downFile = new FileInputStream(pathName);
                                                    //按字节的形式将文件传送到客户端
                                                    while((content=downFile.read())!=-1)
                                                    {
                                                              os.write(content);
                                                              //sentToClient.println(content);
                                                    }
                                                    handShake=receiveFromClient.readLine();//接收客户端的接收完毕信号
                                                    if(handShake.equalsIgnoreCase("finished"))//发送文件下载完毕信号
                                                     sentToClient.println("The
file:"+"\" "+fileName+"\""+"is download
completely!");
                                                    }
                                                     catch(Exception ex)
                                                     {
                                                     sentToClient.println("**warnning**:This
file can'n be download! ");
                                                     }
                                                        }
                                               }
                                     sentToClient.println("==>\""+text+"\"is
done successfully!");
                             }
                            receiveFromClient.close();
                            sentToClient.close();
                            clientConnect.close();                                             
                    }                                                     
                            catch(Exception e)
                            {
                                     try
                                     {
                                               receiveFromClient.close();
                                               sentToClient.close();
                                               clientConnect.close();        
                                     }
                                     catch(IOException
ioe)
                                     {
                                               System.out.println("io
error");
                                               ioe.printStackTrace();
                                     }
                                               
                            }
         }        
}


四、  
Ftp客户端的编写如下:
/*
* 此程序为Ftp客户端程序
* 步骤如下:
* 1、从程序的运行参数中读取服务器ip和port 否则提示错误
* 2、与服务器建立输入输出流并封装起来“按行”读取信息
* 3、从键盘输入:发送控制信息如:“quit”,“ls”,“get”
* 4、当发送“get”控制信息时,先于服务器端进行"握手协商"
然后将"接收服务器控制数据的线程"暂停运行,
*   把控制信息流转换成为数据传输流,方便文件信息的无干扰传输,方法是:利用线程之间的同步
* 5、当文件传输完毕后,发送完毕信号“finished”,然后切换回控制流,继续接受服务器的其他控制信息
* 6、发送“quit”退出与服务器的连接
*
* 作者:蔡青庆
*/
package ftp;
import java.net.*;
import java.io.*;
public
class FtpClient
{
         public static void main(String[] args)
         {
                   Socket client=null;
                   String object="";                  
                   if(args.length>0)
                   {
                            try
                            {
                                     //接收参数,传递服务器ip和 port
                                     InetAddress
ip=InetAddress.getByName(args[0]);
                                     int port = Integer.parseInt(args[1]);
                                     client=new
Socket(ip,port);
                            }
                            catch(Exception ex)
                            {
                                     System.out.println("error");
                                     ex.printStackTrace();
                            }
                   }
                   else
                            System.out.println("Tip:the
usage of the program is set the 'ip' and 'port';");

                   try
                   {
                            OutputStream
os=client.getOutputStream();
                            InputStream
is=client.getInputStream();
                            PrintWriter
sentToServer=new PrintWriter(os,true);//发送给服务端的控制流的封装
                            //;//接收服务器发来的信息
                            BufferedReader
netIn=new BufferedReader(new InputStreamReader(is))        
                            //封装从键盘输入到网络的发送流
                        BufferedReader keyBoardIn=new
BufferedReader(new InputStreamReader(System.in));                          
                            receive recv=new receive(is,object);//“object” 是个用于同步的资源
                            Thread
recvThread=new Thread(recv); // 建立接收服务数据的线程
                            recvThread.setDaemon(true);//将线程设置成后台的以便于随主程序终止而终止
                            recvThread.start();
                            String out=null; //定义待发送的控制信息              
                            while(true)
                            {
                                     sentToServer.println(out=keyBoardIn.readLine());
                                     //退出和服务器的连接
                                     if(out.equalsIgnoreCase("quit"))
                                     {
                                               break;                                             
                                     }
                                     //下载服务器上共享的文件
                                     else
if(out.equalsIgnoreCase("get"))
                                     {                                             
                                               recv.stopNow(true);//停止接收线程                                
                                               sentToServer.println("ready");//发送第一轮握手标志“ready”
                                               //下面的一个同步块就是用来将控制信息传输流转换成数据传输流
                                               synchronized(object){
                                               while(true)
                                               {
                                                        //双方达成协议 开始传发文件
                                                        if((netIn.readLine()).equalsIgnoreCase("accept"))
                                                        {
                                                                 String
fileName=null;
                                                                 sentToServer.println(fileName=keyBoardIn.readLine());
                                                                 int
fileLength=Integer.parseInt(netIn.readLine());
                                                            //在本地新建服务器下载文件的文件名
                                                                 FileOutputStream
fileCopy=new FileOutputStream(fileName);       //通过文件大小实现文件的同步传输
                                                                 for(int
i=0;i
                                                                 {
                                                                           fileCopy.write(is.read());    //将文件下载到本地                                                           
                                                                 }
                                                                 fileCopy.close();//关闭文件
                                                                 sentToServer.println("finished");//发送文件下载完毕信号
                                                                 object.notify();//将数据流又切换到控制流
                                                                 recv.startNow(true);//打开接收线程
                                                                 break;
                                                        }
                                               }
                                               }
                                     }
                            }
                            netIn.close();
                            keyBoardIn.close();
                           
                            sentToServer.close();
                            client.close();
                   }
                   catch(Exception ex)
                   {
                            ex.printStackTrace();
                   }
         }
}
//用于接收服务器的所有控制提示信息,注意:除了文件传输数据
class
receive implements Runnable
{
         String object=null;
         BufferedReader sendFromServer;
         InputStream is;
         boolean isStop=false;//控制线程的状态变量
         public receive(InputStream is,String
object)
         {
                   this.is=is;
                   this.object=object;
         }
         //将线程暂停运行
         public void stopNow(boolean isStop)
         {
                   this.isStop=isStop;
         }
         //将线程恢复运行
         public void startNow(boolean isStart)
         {
                   this.isStop=!isStart;
         }
         public void run()
         {
                   try
                   {
                            String echo=null;
                            sendFromServer=new
BufferedReader(new InputStreamReader(is));
                            synchronized(object){
                            while(true)
                            {
                                     if(!isStop)
                                     {
                                               echo=sendFromServer.readLine();//接收服务器的控制信息
                                               if(echo.isEmpty())
                                                        break;
                                               else
System.out.println(echo);//显示服务器的控制信息
                                               
                                     }
                                     else
                                               object.wait();     //如果转换成数据流的信号开始了就将线程等待                     
                            }
                            }
                   }
                   catch(Exception e)
                   {
                            e.printStackTrace();
                   }
         }
}
程序运行结果如下:
首先:启动Ftp服务器并设置共享文件路径为:“I:/”;

然后:打开一个Ftp客户端 如下图 设置Ftp服务器的ip:127.0.0.1 port:6500
服务器会发送欢迎信息和帮助信息,并提示输入命令;



输入:“ls”命令显示服务器的共享信息,如下图所示

输入:“get”下载文件,然后输入下载的文件名,如果不存在的话会提示出错

  

下载的文件“hello.txt”保存在当前客户端程序的所在目录,如下图所示

最后输入“quit”安全退出服务器 如下图所示:

实验总结:
   当时我写这个程序的之前,对很多概念还是不清楚,不知道怎么实现控制信息和文件数据信息进行分流,因为该程序只开设了一个端口,并没有像真正的Ftp服务器那样,有数据传输和控制传输两个端口。可能这也是我当初动手的时候没有设计好的缘故,所以后来在处理这个问题的时候花了我打量的时间。最后我想了一个办法,就是模拟“三次握手”的方式进行服务器和客户端的协商,从而将客户端装换成只接收数据,实现了控制信息和数据信息的切换接受。当然其中只有一个端口,所有的信息都是在这个端口上进行接受和发送,在转换控制方式的时候,线程的同步是最关键的。
通过这个实验,大概花了我两个晚上的时间,我学到了很多关于“线程安全”的相关知识。还有网络编程需要借助的网络知识,文件“I/O”流的封装和灵活运用。可谓是出了汗,但又有收获。但是这个程序只是实现了文件从服务器端下载到客户端。因为考试的原因,还没来的及添加上传功能,但原理是一样的。
               
               
               
               
               
               
               
               
               
               
               

本文来自ChinaUnix博客,如果查看原文请点:http://blog.chinaunix.net/u1/35830/showart_538043.html
您需要登录后才可以回帖 登录 | 注册

本版积分规则 发表回复

  

北京盛拓优讯信息技术有限公司. 版权所有 京ICP备16024965号-6 北京市公安局海淀分局网监中心备案编号:11010802020122 niuxiaotong@pcpop.com 17352615567
未成年举报专区
中国互联网协会会员  联系我们:huangweiwei@itpub.net
感谢所有关心和支持过ChinaUnix的朋友们 转载本站内容请注明原作者名及出处

清除 Cookies - ChinaUnix - Archiver - WAP - TOP