网络编程--简单实现javaftp服务器

       最近有计算机网络课让完成ftp服务器的实现,其实ftp的实现原理非常简单:
在主函数中建立一个服务器套接字端口,等待客户端请求,一旦客户端请求被接受,服务器程序就建立一个服务器分线程,处理客户端的命令。如果客户端需要和服务器端进行文件的传输,则建立一个新的套接字连接来完成文件的操作。
下面看我的实现方法,我们先来定义一个用户类,用来存放登陆用户的信息:
class UserInfo
{
    String user;
    String password;
    String workDir;
}
主函数中监听21号端口:

            
//监听21号端口,21口用于控制,20口用于传数据
            ServerSocket s = new ServerSocket(21);
            
for(;;)
            
{
                
//接受客户端请求
                Socket incoming = s.accept();
                BufferedReader in 
= new BufferedReader(new InputStreamReader(incoming.getInputStream()));
                PrintWriter out 
= new PrintWriter(incoming.getOutputStream(),true);//文本文本输出流
                out.println("220 准备为您服务"+",你是当前第  "+counter+" 个登陆者!");//命令正确的提示

                
//创建服务线程
                FtpHandler h = new FtpHandler(incoming,i);
                h.start();
                users.add(h);   
//将此用户线程加入到这个 ArrayList 中
                counter++;
                i
++;
            }
现在我们来看ftp的主要处理方法
class FtpHandler extends Thread
{
    Socket ctrlSocket;        
//用于控制的套接字
    Socket dataSocket;        //用于传输的套接字
    int id;
    String cmd 
= "";        //存放指令(空格前)
    String param = "";        //放当前指令之后的参数(空格后)
    String user;
    String remoteHost 
= " ";       //客户IP
    int remotePort = 0;               //客户TCP 端口号
    String dir = FtpServer.initDir;//当前目录
    String rootdir = "c:/";           //默认根目录,在checkPASS中设置
    int state = 0 ;                   //用户状态标识符,在checkPASS中设置
    String reply;                   //返回报告
    PrintWriter ctrlOutput; 
    
int type = 0;                   //文件类型(ascII 或 bin)
    String requestfile = "";
    
boolean isrest = false;
    
    
//FtpHandler方法
    
//构造方法
    public FtpHandler(Socket s,int i)
    
{
        ctrlSocket 
= s;
        id 
= i;    
    }


    
//run 方法
    public void run()
    
{
        String str 
= "";
        
int parseResult;                            //与cmd 一一对应的号
        
        
try
        
{
            BufferedReader ctrlInput 
= new BufferedReader
                                (
new InputStreamReader(ctrlSocket.getInputStream()));
            ctrlOutput 
= new PrintWriter(ctrlSocket.getOutputStream(),true);
            state  
= FtpState.FS_WAIT_LOGIN;          //0
            boolean finished = false;
            
while(!finished)    
            
{
                str 
= ctrlInput.readLine();            ///
                if(str == null) finished = true;    //跳出while
                else
                
{
                    parseResult 
= parseInput(str);  //指令转化为指令号
                    System.out.println("指令:"+cmd+" 参数:"+param);
                    System.out.print(
"->");
                    
switch(state)                    //用户状态开关
                    {
                        
case FtpState.FS_WAIT_LOGIN:
                                finished 
= commandUSER();
                                
break;
                        
case FtpState.FS_WAIT_PASS:
                                finished 
= commandPASS();
                                
break;
                        
case FtpState.FS_LOGIN:
                        
{
                            
switch(parseResult)//指令号开关,决定程序是否继续运行的关键
                            {
                                
case -1:
                                    errCMD();                    
//语法错
                                    break;
                                
case 4:
                                    finished 
= commandCDUP();   //到上一层目录
                                    break;
                                
case 6:
                                    finished 
= commandCWD();    //到指定的目录
                                    break;
                                
case 7:
                                    finished 
= commandQUIT();    //退出
                                    break;
                                
case 9:
                                    finished 
= commandPORT();    //客户端IP:地址+TCP 端口号
                                    break;
                                
case 11:
                                    finished 
= commandTYPE();    //文件类型设置(ascII 或 bin)
                                    break;
                                
case 14:
                                    finished 
= commandRETR();    //从服务器中获得文件
                                    break;
                                
case 15:
                                    finished 
= commandSTOR();    //向服务器中发送文件
                                    break;
                                
case 22:
                                    finished 
= commandABOR();    //关闭传输用连接dataSocket
                                    break;
                                
case 23:
                                    finished 
= commandDELE();    //删除服务器上的指定文件
                                    break;
                                
case 25:
                                    finished 
= commandMKD();    //建立目录
                                    break;
                                
case 27:
                                    finished 
= commandLIST();    //文件和目录的列表
                                    break;
                                
case 26:
                                
case 33:
                                    finished 
= commandPWD();    //"当前目录" 信息
                                    break;
                                
case 32:
                                    finished 
= commandNOOP();    //"命令正确" 信息
                                    break;
                                
                            }

                        }

                            
break;
                        

                    }

                }
 
                ctrlOutput.println(reply);
                ctrlOutput.flush();
////////////////////////////////////
                
            }
 
            ctrlSocket.close();
        }
 
        
catch(Exception e)
        
{
            e.printStackTrace();
        }

    }


    
//parseInput方法    
    int parseInput(String s)
    
{
        
int p = 0;
        
int i = -1;
        p 
= s.indexOf(" ");
        
if(p == -1)                  //如果是无参数命令(无空格)
            cmd = s;
        
else 
            cmd 
= s.substring(0,p);  //有参数命令,过滤参数
        
        
if(p >= s.length() || p ==-1)//如果无空格,或空格在读入的s串最后或之外
            param = "";
        
else
            param 
= s.substring(p+1,s.length());
        cmd 
= cmd.toUpperCase();     //转换该 String 为大写
        
          
if(cmd.equals("CDUP"))
                i 
= 4;
        
if(cmd.equals("CWD"))
                i 
= 6;
        
if(cmd.equals("QUIT"))
                i 
= 7;
        
if(cmd.equals("PORT"))
                i 
= 9;
        
if(cmd.equals("TYPE"))
                i 
= 11;
        
if(cmd.equals("RETR"))
                i 
= 14;
        
if(cmd.equals("STOR"))
                i 
= 15;
        
if(cmd.equals("ABOR"))
                i 
= 22;
        
if(cmd.equals("DELE"))
                i 
= 23;
        
if(cmd.equals("MKD"))
                i 
= 25;
        
if(cmd.equals("PWD"))
                i 
= 26;
        
if(cmd.equals("LIST"))
                i 
= 27;
          
if(cmd.equals("NOOP"))
                i 
= 32;
        
if(cmd.equals("XPWD"))
                i 
= 33;
     
return i;
    }

    
    
//validatePath方法
    
//判断路径的属性,返回 int 
    int validatePath(String s)
    
{
        File f 
= new File(s);        //相对路径
        if(f.exists() && !f.isDirectory())
        
{
            String s1 
= s.toLowerCase();
            String s2 
= rootdir.toLowerCase();
            
if(s1.startsWith(s2))    
                
return 1;            //文件存在且不是路径,且以rootdir 开始
            else
                
return 0;            //文件存在且不是路径,不以rootdir 开始
        }

        f 
= new File(addTail(dir)+s);//绝对路径
        if(f.exists() && !f.isDirectory())
        
{
            String s1 
= (addTail(dir)+s).toLowerCase();
            String s2 
= rootdir.toLowerCase();
            
if(s1.startsWith(s2))
                
return 2;            //文件存在且不是路径,且以rootdir 开始
            else 
                
return 0;            //文件存在且不是路径,不以rootdir 开始
        }

        
return 0;                    //其他情况
    }

    
    
boolean checkPASS(String s) //检查密码是否正确,从文件中找
    {
        
for(int i = 0; i<FtpServer.usersInfo.size();i++)
        
{
            
if(((UserInfo)FtpServer.usersInfo.get(i)).user.equals(user) && 
                ((UserInfo)FtpServer.usersInfo.get(i)).password.equals(s))
            
{
                rootdir 
= ((UserInfo)FtpServer.usersInfo.get(i)).workDir;
                dir 
= ((UserInfo)FtpServer.usersInfo.get(i)).workDir;
                
return true;
            }

        }

        
return false;
    }


    
//commandUSER方法
    
//用户名是否正确
    boolean commandUSER()
    
{
        
if(cmd.equals("USER"))
        
{
            reply 
= "331 用户名正确,需要口令";
            user 
= param;
              state 
= FtpState.FS_WAIT_PASS;
            
return false;
        }

        
else
        
{
            reply 
= "501 参数语法错误,用户名不匹配";
            
return true;
        }


    }


    
//commandPASS 方法
    
//密码是否正确
    boolean commandPASS()
    
{
        
if(cmd.equals("PASS"))
        
{
            
if(checkPASS(param))
            
{
                reply 
= "230 用户登录了";
                state 
= FtpState.FS_LOGIN;
                System.out.println(
"新消息: 用户: "+user+" 来自于: "+ remoteHost +"登录了");
                System.out.print(
"->");