网络编程--简单实现javaftp服务器
最近有计算机网络课让完成ftp服务器的实现,其实ftp的实现原理非常简单:
在主函数中建立一个服务器套接字端口,等待客户端请求,一旦客户端请求被接受,服务器程序就建立一个服务器分线程,处理客户端的命令。如果客户端需要和服务器端进行文件的传输,则建立一个新的套接字连接来完成文件的操作。
下面看我的实现方法,我们先来定义一个用户类,用来存放登陆用户的信息:
//监听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的主要处理方法
感谢中国站长站提供思路http://www.chinaz.com/Server/FTP/032Ac12007.html
在主函数中建立一个服务器套接字端口,等待客户端请求,一旦客户端请求被接受,服务器程序就建立一个服务器分线程,处理客户端的命令。如果客户端需要和服务器端进行文件的传输,则建立一个新的套接字连接来完成文件的操作。
下面看我的实现方法,我们先来定义一个用户类,用来存放登陆用户的信息:
class UserInfo
{
String user;
String password;
String workDir;
}
主函数中监听21号端口:{
String user;
String password;
String workDir;
}
//监听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++;
}
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("->");
return false;
}
else
{
reply = "530 没有登录";
return true;
}
}
else
{
reply = "501 参数语法错误,密码不匹配";
return true;
}
}
void errCMD()
{
reply = "500 语法错误";
}
boolean commandCDUP()//到上一层目录
{
dir = FtpServer.initDir;
File f = new File(dir);
if(f.getParent()!=null &&(!dir.equals(rootdir)))//有父路径 && 不是根路径
{
dir = f.getParent();
reply = "200 命令正确";
}
else
{
reply = "550 当前目录无父路径";
}
return false;
}// commandCDUP() end
boolean commandCWD()// CWD (CHANGE WORKING DIRECTORY)
{ //该命令改变工作目录到用户指定的目录
File f = new File(param);
String s = "";
String s1 = "";
if(dir.endsWith("/"))
s = dir;
else
s = dir + "/";
File f1 = new File(s+param);
if(f.isDirectory() && f.exists())
{
if(param.equals("..") || param.equals("..\\"))
{
if(dir.compareToIgnoreCase(rootdir)==0)
{
reply = "550 此路径不存在";
//return false;
}
else
{
s1 = new File(dir).getParent();
if(s1!=null)
{
dir = s1;
reply = "250 请求的文件处理结束, 当前目录变为: "+dir;
}
else
reply = "550 此路径不存在";
}
}
else if(param.equals(".") || param.equals(".\\"))
{}
else
{
dir = param;
reply = "250 请求的文件处理结束, 工作路径变为 "+dir;
}
}
else if(f1.isDirectory() && f1.exists())
{
dir = s+param;
reply = "250 请求的文件处理结束, 工作路径变为 "+dir;
}
else
reply = "501 参数语法错误";
return false;
} // commandCDW() end
boolean commandQUIT()
{
reply = "221 服务关闭连接";
return true;
}// commandQuit() end
/*使用该命令时,客户端必须发送客户端用于接收数据的32位IP 地址和16位 的TCP 端口号。
*这些信息以8位为一组,使用十进制传输,中间用逗号隔开。
*/
boolean commandPORT()
{
int p1 = 0;
int p2 = 0;
int[] a = new int[6];//存放ip+tcp
int i = 0; //
try
{
while((p2 = param.indexOf(",",p1))!=-1)//前5位
{
a[i] = Integer.parseInt(param.substring(p1,p2));
p2 = p2+1;
p1 = p2;
i++;
}
a[i] = Integer.parseInt(param.substring(p1,param.length()));//最后一位
}
catch(NumberFormatException e)
{
reply = "501 参数语法错误";
return false;
}
remoteHost = a[0]+"."+a[1]+"."+a[2]+"."+a[3];
remotePort = a[4] * 256+a[5];
reply = "200 命令正确";
return false;
}//commandPort() end
/*LIST 命令用于向客户端返回服务器中工作目录下的目录结构,包括文件和目录的列表。
*处理这个命令时,先创建一个临时的套接字向客户端发送目录信息。这个套接字的目的
*端口号缺省为,然后为当前工作目录创建File 对象,利用该对象的list()方法得到一
*个包含该目录下所有文件和子目录名称的字符串数组,然后根据名称中是否含有文件名
*中特有的"."来区别目录和文件。最后,将得到的名称数组通过临时套接字发送到客户端。
**/
boolean commandLIST()//文件和目录的列表
{
try
{
dataSocket = new Socket(remoteHost,remotePort,InetAddress.getLocalHost(),20);
PrintWriter dout = new PrintWriter(dataSocket.getOutputStream(),true);
if(param.equals("") || param.equals("LIST"))
{
ctrlOutput.println("150 文件状态正常,ls以 ASCII 方式操作");
File f = new File(dir);
String[] dirStructure = f.list();//指定路径中的文件名数组,不包括当前路径或父路径
String fileType;
for(int i =0; i<dirStructure.length;i++)
{
if(dirStructure[i].indexOf(".")!=-1)
{
fileType = "- "; //父目录(在linux下)
}
else
{
fileType = "d "; //本目录的文件和子目录
}
dout.println(dirStructure[i]);//(fileType+dirStructure[i]);
}
}
dout.close();
dataSocket.close();
reply = "226 传输数据连接结束";
}
catch(Exception e)
{
e.printStackTrace();
reply = "451 Requested action aborted: local error in processing";
return false;
}
return false;
}// commandLIST() end
boolean commandTYPE() //TYPE 命令用来完成类型设置
{
if(param.equals("A"))
{
type = FtpState.FTYPE_ASCII;//0
reply = "200 命令正确 ,转 ASCII 模式";
}
else if(param.equals("I"))
{
type = FtpState.FTYPE_IMAGE;//1
reply = "200 命令正确 转 BINARY 模式";
}
else
reply = "504 命令不能执行这种参数";
return false;
}
//connamdRETR 方法
//从服务器中获得文件
boolean commandRETR()
{
requestfile = param;
File f = new File(requestfile);
if(!f.exists())
{
f = new File(addTail(dir)+param);
if(!f.exists())
{
reply = "550 文件不存在";
return false;
}
requestfile = addTail(dir)+param;
}
if(isrest)
{
}
else
{
if(type==FtpState.FTYPE_IMAGE) //bin
{
try
{
ctrlOutput.println("150 文件状态正常,以二进治方式打开文件: "+ requestfile);
dataSocket = new Socket(remoteHost,remotePort,InetAddress.getLocalHost(),20);
BufferedInputStream fin = new BufferedInputStream(new FileInputStream(requestfile));
PrintStream dataOutput = new PrintStream(dataSocket.getOutputStream(),true);
byte[] buf = new byte[1024]; //目标缓冲区
int l = 0;
while((l=fin.read(buf,0,1024))!=-1) //缓冲区未读满
{
dataOutput.write(buf,0,l); //写入套接字
}
fin.close();
dataOutput.close();
dataSocket.close();
reply ="226 传输数据连接结束";
}
catch(Exception e)
{
e.printStackTrace();
reply = "451 请求失败: 传输出故障";
return false;
}
}
if(type==FtpState.FTYPE_ASCII)//ascII
{
try
{
ctrlOutput.println("150 Opening ASCII mode data connection for "+ requestfile);
dataSocket = new Socket(remoteHost,remotePort,InetAddress.getLocalHost(),20);
BufferedReader fin = new BufferedReader(new FileReader(requestfile));
PrintWriter dataOutput = new PrintWriter(dataSocket.getOutputStream(),true);
String s;
while((s=fin.readLine())!=null)
{
dataOutput.println(s); ///???
}
fin.close();
dataOutput.close();
dataSocket.close();
reply ="226 传输数据连接结束";
}
catch(Exception e)
{
e.printStackTrace();
reply = "451 请求失败: 传输出故障";
return false;
}
}
}
return false;
}
//commandSTOR 方法
//向服务器中发送文件STOR
boolean commandSTOR()
{
if(param.equals(""))
{
reply = "501 参数语法错误";
return false;
}
requestfile = addTail(dir)+param;
if(type == FtpState.FTYPE_IMAGE)//bin
{
try
{
ctrlOutput.println("150 Opening Binary mode data connection for "+ requestfile);
dataSocket = new Socket(remoteHost,remotePort,InetAddress.getLocalHost(),20);
BufferedOutputStream fout = new BufferedOutputStream(new FileOutputStream(requestfile));
BufferedInputStream dataInput = new BufferedInputStream(dataSocket.getInputStream());
byte[] buf = new byte[1024];
int l = 0;
while((l = dataInput.read(buf,0,1024))!=-1)
{
fout.write(buf,0,l);
}
dataInput.close();
fout.close();
dataSocket.close();
reply = "226 传输数据连接结束";
}
catch(Exception e)
{
e.printStackTrace();
reply = "451 请求失败: 传输出故障";
return false;
}
}
if(type == FtpState.FTYPE_ASCII)//ascII
{
try
{
ctrlOutput.println("150 Opening ASCII mode data connection for "+ requestfile);
dataSocket = new Socket(remoteHost,remotePort,InetAddress.getLocalHost(),20);
PrintWriter fout = new PrintWriter(new FileOutputStream(requestfile));
BufferedReader dataInput = new BufferedReader(new InputStreamReader(dataSocket.getInputStream()));
String line;
while((line = dataInput.readLine())!=null)
{
fout.println(line);
}
dataInput.close();
fout.close();
dataSocket.close();
reply = "226 传输数据连接结束";
}
catch(Exception e)
{
e.printStackTrace();
reply = "451 请求失败: 传输出故障";
return false;
}
}
return false;
}
boolean commandPWD()
{
reply = "257 " + dir + " 是当前目录.";
return false;
}
boolean commandNOOP()
{
reply = "200 命令正确.";
return false;
}
//强关dataSocket 流
boolean commandABOR()
{
try
{
dataSocket.close();
}
catch(Exception e)
{
e.printStackTrace();
reply = "451 请求失败: 传输出故障";
return false;
}
reply = "421 服务不可用, 关闭数据传送连接";
return false;
}
//删除服务器上的指定文件
boolean commandDELE()
{
int i = validatePath(param);
if(i == 0)
{
reply = "550 请求的动作未执行,文件不存在,或目录不对,或其他";
return false;
}
if(i == 1)
{
File f = new File(param);
f.delete();
}
if(i == 2)
{
File f= new File(addTail(dir)+param);
f.delete();
}
reply = "250 请求的文件处理结束,成功删除服务器上文件";
return false;
}
//建立目录,要绝对路径
boolean commandMKD()
{
String s1 = param.toLowerCase();
String s2 = rootdir.toLowerCase();
if(s1.startsWith(s2))
{
File f = new File(param);
if(f.exists())
{
reply = "550 请求的动作未执行,目录已存在";
return false;
}
else
{
f.mkdirs();
reply = "250 请求的文件处理结束, 目录建立";
}
}
else
{
File f = new File(addTail(dir)+param);
if(f.exists())
{
reply = "550 请求的动作未执行,目录已存在";
return false;
}
else
{
f.mkdirs();
reply = "250 请求的文件处理结束, 目录建立";
}
}
return false;
}
String addTail(String s)
{
if(!s.endsWith("/"))
s = s + "/";
return s;
}
}
这样我们基本功能就实现了,其中ftpstate为状态对象 {
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("->");
return false;
}
else
{
reply = "530 没有登录";
return true;
}
}
else
{
reply = "501 参数语法错误,密码不匹配";
return true;
}
}
void errCMD()
{
reply = "500 语法错误";
}
boolean commandCDUP()//到上一层目录
{
dir = FtpServer.initDir;
File f = new File(dir);
if(f.getParent()!=null &&(!dir.equals(rootdir)))//有父路径 && 不是根路径
{
dir = f.getParent();
reply = "200 命令正确";
}
else
{
reply = "550 当前目录无父路径";
}
return false;
}// commandCDUP() end
boolean commandCWD()// CWD (CHANGE WORKING DIRECTORY)
{ //该命令改变工作目录到用户指定的目录
File f = new File(param);
String s = "";
String s1 = "";
if(dir.endsWith("/"))
s = dir;
else
s = dir + "/";
File f1 = new File(s+param);
if(f.isDirectory() && f.exists())
{
if(param.equals("..") || param.equals("..\\"))
{
if(dir.compareToIgnoreCase(rootdir)==0)
{
reply = "550 此路径不存在";
//return false;
}
else
{
s1 = new File(dir).getParent();
if(s1!=null)
{
dir = s1;
reply = "250 请求的文件处理结束, 当前目录变为: "+dir;
}
else
reply = "550 此路径不存在";
}
}
else if(param.equals(".") || param.equals(".\\"))
{}
else
{
dir = param;
reply = "250 请求的文件处理结束, 工作路径变为 "+dir;
}
}
else if(f1.isDirectory() && f1.exists())
{
dir = s+param;
reply = "250 请求的文件处理结束, 工作路径变为 "+dir;
}
else
reply = "501 参数语法错误";
return false;
} // commandCDW() end
boolean commandQUIT()
{
reply = "221 服务关闭连接";
return true;
}// commandQuit() end
/*使用该命令时,客户端必须发送客户端用于接收数据的32位IP 地址和16位 的TCP 端口号。
*这些信息以8位为一组,使用十进制传输,中间用逗号隔开。
*/
boolean commandPORT()
{
int p1 = 0;
int p2 = 0;
int[] a = new int[6];//存放ip+tcp
int i = 0; //
try
{
while((p2 = param.indexOf(",",p1))!=-1)//前5位
{
a[i] = Integer.parseInt(param.substring(p1,p2));
p2 = p2+1;
p1 = p2;
i++;
}
a[i] = Integer.parseInt(param.substring(p1,param.length()));//最后一位
}
catch(NumberFormatException e)
{
reply = "501 参数语法错误";
return false;
}
remoteHost = a[0]+"."+a[1]+"."+a[2]+"."+a[3];
remotePort = a[4] * 256+a[5];
reply = "200 命令正确";
return false;
}//commandPort() end
/*LIST 命令用于向客户端返回服务器中工作目录下的目录结构,包括文件和目录的列表。
*处理这个命令时,先创建一个临时的套接字向客户端发送目录信息。这个套接字的目的
*端口号缺省为,然后为当前工作目录创建File 对象,利用该对象的list()方法得到一
*个包含该目录下所有文件和子目录名称的字符串数组,然后根据名称中是否含有文件名
*中特有的"."来区别目录和文件。最后,将得到的名称数组通过临时套接字发送到客户端。
**/
boolean commandLIST()//文件和目录的列表
{
try
{
dataSocket = new Socket(remoteHost,remotePort,InetAddress.getLocalHost(),20);
PrintWriter dout = new PrintWriter(dataSocket.getOutputStream(),true);
if(param.equals("") || param.equals("LIST"))
{
ctrlOutput.println("150 文件状态正常,ls以 ASCII 方式操作");
File f = new File(dir);
String[] dirStructure = f.list();//指定路径中的文件名数组,不包括当前路径或父路径
String fileType;
for(int i =0; i<dirStructure.length;i++)
{
if(dirStructure[i].indexOf(".")!=-1)
{
fileType = "- "; //父目录(在linux下)
}
else
{
fileType = "d "; //本目录的文件和子目录
}
dout.println(dirStructure[i]);//(fileType+dirStructure[i]);
}
}
dout.close();
dataSocket.close();
reply = "226 传输数据连接结束";
}
catch(Exception e)
{
e.printStackTrace();
reply = "451 Requested action aborted: local error in processing";
return false;
}
return false;
}// commandLIST() end
boolean commandTYPE() //TYPE 命令用来完成类型设置
{
if(param.equals("A"))
{
type = FtpState.FTYPE_ASCII;//0
reply = "200 命令正确 ,转 ASCII 模式";
}
else if(param.equals("I"))
{
type = FtpState.FTYPE_IMAGE;//1
reply = "200 命令正确 转 BINARY 模式";
}
else
reply = "504 命令不能执行这种参数";
return false;
}
//connamdRETR 方法
//从服务器中获得文件
boolean commandRETR()
{
requestfile = param;
File f = new File(requestfile);
if(!f.exists())
{
f = new File(addTail(dir)+param);
if(!f.exists())
{
reply = "550 文件不存在";
return false;
}
requestfile = addTail(dir)+param;
}
if(isrest)
{
}
else
{
if(type==FtpState.FTYPE_IMAGE) //bin
{
try
{
ctrlOutput.println("150 文件状态正常,以二进治方式打开文件: "+ requestfile);
dataSocket = new Socket(remoteHost,remotePort,InetAddress.getLocalHost(),20);
BufferedInputStream fin = new BufferedInputStream(new FileInputStream(requestfile));
PrintStream dataOutput = new PrintStream(dataSocket.getOutputStream(),true);
byte[] buf = new byte[1024]; //目标缓冲区
int l = 0;
while((l=fin.read(buf,0,1024))!=-1) //缓冲区未读满
{
dataOutput.write(buf,0,l); //写入套接字
}
fin.close();
dataOutput.close();
dataSocket.close();
reply ="226 传输数据连接结束";
}
catch(Exception e)
{
e.printStackTrace();
reply = "451 请求失败: 传输出故障";
return false;
}
}
if(type==FtpState.FTYPE_ASCII)//ascII
{
try
{
ctrlOutput.println("150 Opening ASCII mode data connection for "+ requestfile);
dataSocket = new Socket(remoteHost,remotePort,InetAddress.getLocalHost(),20);
BufferedReader fin = new BufferedReader(new FileReader(requestfile));
PrintWriter dataOutput = new PrintWriter(dataSocket.getOutputStream(),true);
String s;
while((s=fin.readLine())!=null)
{
dataOutput.println(s); ///???
}
fin.close();
dataOutput.close();
dataSocket.close();
reply ="226 传输数据连接结束";
}
catch(Exception e)
{
e.printStackTrace();
reply = "451 请求失败: 传输出故障";
return false;
}
}
}
return false;
}
//commandSTOR 方法
//向服务器中发送文件STOR
boolean commandSTOR()
{
if(param.equals(""))
{
reply = "501 参数语法错误";
return false;
}
requestfile = addTail(dir)+param;
if(type == FtpState.FTYPE_IMAGE)//bin
{
try
{
ctrlOutput.println("150 Opening Binary mode data connection for "+ requestfile);
dataSocket = new Socket(remoteHost,remotePort,InetAddress.getLocalHost(),20);
BufferedOutputStream fout = new BufferedOutputStream(new FileOutputStream(requestfile));
BufferedInputStream dataInput = new BufferedInputStream(dataSocket.getInputStream());
byte[] buf = new byte[1024];
int l = 0;
while((l = dataInput.read(buf,0,1024))!=-1)
{
fout.write(buf,0,l);
}
dataInput.close();
fout.close();
dataSocket.close();
reply = "226 传输数据连接结束";
}
catch(Exception e)
{
e.printStackTrace();
reply = "451 请求失败: 传输出故障";
return false;
}
}
if(type == FtpState.FTYPE_ASCII)//ascII
{
try
{
ctrlOutput.println("150 Opening ASCII mode data connection for "+ requestfile);
dataSocket = new Socket(remoteHost,remotePort,InetAddress.getLocalHost(),20);
PrintWriter fout = new PrintWriter(new FileOutputStream(requestfile));
BufferedReader dataInput = new BufferedReader(new InputStreamReader(dataSocket.getInputStream()));
String line;
while((line = dataInput.readLine())!=null)
{
fout.println(line);
}
dataInput.close();
fout.close();
dataSocket.close();
reply = "226 传输数据连接结束";
}
catch(Exception e)
{
e.printStackTrace();
reply = "451 请求失败: 传输出故障";
return false;
}
}
return false;
}
boolean commandPWD()
{
reply = "257 " + dir + " 是当前目录.";
return false;
}
boolean commandNOOP()
{
reply = "200 命令正确.";
return false;
}
//强关dataSocket 流
boolean commandABOR()
{
try
{
dataSocket.close();
}
catch(Exception e)
{
e.printStackTrace();
reply = "451 请求失败: 传输出故障";
return false;
}
reply = "421 服务不可用, 关闭数据传送连接";
return false;
}
//删除服务器上的指定文件
boolean commandDELE()
{
int i = validatePath(param);
if(i == 0)
{
reply = "550 请求的动作未执行,文件不存在,或目录不对,或其他";
return false;
}
if(i == 1)
{
File f = new File(param);
f.delete();
}
if(i == 2)
{
File f= new File(addTail(dir)+param);
f.delete();
}
reply = "250 请求的文件处理结束,成功删除服务器上文件";
return false;
}
//建立目录,要绝对路径
boolean commandMKD()
{
String s1 = param.toLowerCase();
String s2 = rootdir.toLowerCase();
if(s1.startsWith(s2))
{
File f = new File(param);
if(f.exists())
{
reply = "550 请求的动作未执行,目录已存在";
return false;
}
else
{
f.mkdirs();
reply = "250 请求的文件处理结束, 目录建立";
}
}
else
{
File f = new File(addTail(dir)+param);
if(f.exists())
{
reply = "550 请求的动作未执行,目录已存在";
return false;
}
else
{
f.mkdirs();
reply = "250 请求的文件处理结束, 目录建立";
}
}
return false;
}
String addTail(String s)
{
if(!s.endsWith("/"))
s = s + "/";
return s;
}
}
/***************************************************************************/
class FtpState
{
final static int FS_WAIT_LOGIN = 0; //等待输入用户名状态
final static int FS_WAIT_PASS = 1; //等待输入密码状态
final static int FS_LOGIN = 2; //已经登陆状态
final static int FTYPE_ASCII = 0;
final static int FTYPE_IMAGE = 1;
final static int FMODE_STREAM = 0;
final static int FMODE_COMPRESSED = 1;
final static int FSTRU_FILE = 0;
final static int FSTRU_PAGE = 1;
}
psclass FtpState
{
final static int FS_WAIT_LOGIN = 0; //等待输入用户名状态
final static int FS_WAIT_PASS = 1; //等待输入密码状态
final static int FS_LOGIN = 2; //已经登陆状态
final static int FTYPE_ASCII = 0;
final static int FTYPE_IMAGE = 1;
final static int FMODE_STREAM = 0;
final static int FMODE_COMPRESSED = 1;
final static int FSTRU_FILE = 0;
final static int FSTRU_PAGE = 1;
}
感谢中国站长站提供思路http://www.chinaz.com/Server/FTP/032Ac12007.html