FTP(File Transfer Protocol, 文件传输协议)是支持Internet文件传输的各种规则所组成的集合。
FTP工作原理:双方至少需要建立两个TCP连接,一个成为控制连接,用于传输FTP命令;另一个称为数据连接,用于传输文件数据。
通过Socket编写FTP客户端,首先要了解服务器的命令:
命令 | 描述 |
ABOR | 中断数据连接程序 |
ACCT <account> | 系统特权帐号 |
ALLO <bytes> | 为服务器上的文件存储器分配字节 |
APPE <filename> | 添加文件到服务器同名文件 |
CDUP <dir path> | 改变服务器上的父目录 |
CWD <dir path> | 改变服务器上的工作目录 |
DELE <filename> | 删除服务器上的指定文件 |
HELP <command> | 返回指定命令信息 |
LIST <name> | 如果是文件名列出文件信息,如果是目录则列出文件列表 |
MODE <mode> | 传输模式(S=流模式,B=块模式,C=压缩模式) |
MKD <directory> | 在服务器上建立指定目录 |
NLST <directory> | 列出指定目录内容 |
NOOP | 无动作,除了来自服务器上的承认 |
PASS <password> | 系统登录密码 |
PASV | 请求服务器等待数据连接 |
PORT <address> | IP 地址和两字节的端口 ID |
PWD | 显示当前工作目录 |
QUIT | 从 FTP 服务器上退出登录 |
REIN | 重新初始化登录状态连接 |
REST <offset> | 由特定偏移量重启文件传递 |
RETR <filename> | 从服务器上找回(复制)文件 |
RMD <directory> | 在服务器上删除指定目录 |
RNFR <old path> | 对旧路径重命名 |
RNTO <new path> | 对新路径重命名 |
SITE <params> | 由服务器提供的站点特殊参数 |
SMNT <pathname> | 挂载指定文件结构 |
STAT <directory> | 在当前程序或目录上返回信息 |
STOR <filename> | 储存(复制)文件到服务器上 |
STOU <filename> | 储存文件到服务器名称上 |
STRU <type> | 数据结构(F=文件,R=记录,P=页面) |
SYST | 返回服务器使用的操作系统 |
TYPE <data type> | 数据类型(A=ASCII,E=EBCDIC,I=binary) |
USER <username>> | 系统登录的用户名 |
响应代码 | 解释说明 |
110 | 新文件指示器上的重启标记 |
120 | 服务器准备就绪的时间(分钟数) |
125 | 打开数据连接,开始传输 |
150 | 打开连接 |
200 | 成功 |
202 | 命令没有执行 |
211 | 系统状态回复 |
212 | 目录状态回复 |
213 | 文件状态回复 |
214 | 帮助信息回复 |
215 | 系统类型回复 |
220 | 服务就绪 |
221 | 退出网络 |
225 | 打开数据连接 |
226 | 结束数据连接 |
227 | 进入被动模式(IP 地址、ID 端口) |
230 | 登录因特网 |
250 | 文件行为完成 |
257 | 路径名建立 |
331 | 要求密码 |
332 | 要求帐号 |
350 | 文件行为暂停 |
421 | 服务关闭 |
425 | 无法打开数据连接 |
426 | 结束连接 |
450 | 文件不可用 |
451 | 遇到本地错误 |
452 | 磁盘空间不足 |
500 | 无效命令 |
501 | 错误参数 |
502 | 命令没有执行 |
503 | 错误指令序列 |
504 | 无效命令参数 |
530 | 未登录网络 |
532 | 存储文件需要帐号 |
550 | 文件不可用 |
551 | 不知道的页类型 |
552 | 超过存储分配 |
553 | 文件名不允许 |
基本步骤:建立连接--->获取文件列表---->对文件进行操作
import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.net.Socket;
import java.util.StringTokenizer;
/**
* @description 没有图形用户界面, 只编写了连接,获取当前目录,上传文件功能。
*/
public class simpleFTP {
private Socket socket = null;
private BufferedReader reader = null;
private BufferedWriter writer = null;
private static boolean debug = false;
private String user = "a";
private String pass = "b";
public simpleFTP() {
}
/**
* connect to the ftp server
* @param host
* @throws Exception
*/
public synchronized void connect(String host) throws Exception {
connect(host, 21, user, pass);
}
public synchronized void connect(String host, int port, String user, String pass) throws Exception {
if(socket != null) {
throw new Exception("already connect!");
}
socket = new Socket(host, port);
reader = new BufferedReader(new InputStreamReader(socket.getInputStream()));
writer = new BufferedWriter(new OutputStreamWriter(socket.getOutputStream()));
String response = readLine();
if(!response.startsWith("220")) {
throw new Exception("unknow response after connect!");
}
sendLine("USER " + user);
response = readLine();
if(!response.startsWith("331")) {
throw new Exception("unknow response after send user");
}
sendLine("PASS " + pass);
response = readLine();
if(!response.startsWith("230")) {
throw new Exception("unknow response after send pass");
}
System.out.println("login!");
}
private void sendLine(String line) throws Exception {
if(socket == null) {
throw new Exception("not connect!");
}
writer.write(line + "\r\n");
writer.flush();
if(debug) {
System.out.println(">" + line);
}
}
private String readLine() throws IOException {
String line = reader.readLine();
if(debug) {
System.out.println("<" + line);
}
return line;
}
/**
* get the working directory of the FTP server
* @return
* @throws Exception
*/
public synchronized String pwd() throws Exception {
sendLine("PWD");
String dir = null;
String response = readLine();
if(response.startsWith("257")) {
int firstQuote = response.indexOf("/");
int secondQuote = response.indexOf("/", firstQuote + 1);
if(secondQuote > 0) {
dir = response.substring(firstQuote + 1, secondQuote);
}
}
return dir;
}
/**
* send a file to ftp server
* @param file
* @return
* @throws Exception
*/
public synchronized boolean stor(File file) throws Exception {
if(!file.isDirectory()) {
throw new Exception("cannot upload a directory!");
}
String fileName = file.getName();
return upload(new FileInputStream(file), fileName);
}
public synchronized boolean upload(InputStream inputStream, String fileName) throws Exception {
BufferedInputStream input = new BufferedInputStream(inputStream);
sendLine("PASV");
String response = readLine();
if(!response.startsWith("227")) {
throw new Exception("not request passive mode!");
}
String ip = null;
int port = -1;
int opening = response.indexOf('(');
int closing = response.indexOf(')', opening + 1);
if(closing > 0) {
String dataLink = response.substring(opening + 1, closing);
StringTokenizer tokenzier = new StringTokenizer(dataLink, ",");
try {
ip = tokenzier.nextToken() + "." + tokenzier.nextToken() + "."
+ tokenzier.nextToken() + "." + tokenzier.nextToken();
port = Integer.parseInt(tokenzier.nextToken()) * 256 +Integer.parseInt(tokenzier.nextToken());;
} catch (Exception e) {
// TODO Auto-generated catch block
throw new Exception("bad data link after upload!");
}
}
sendLine("STOR " + fileName);
Socket dataSocket = new Socket(ip, port);
response = readLine();
if(!response.startsWith("150")) {
throw new Exception("not allowed to send the file!");
}
BufferedOutputStream output = new BufferedOutputStream(dataSocket.getOutputStream());
byte[] buffer = new byte[4096];
int bytesRead = 0;
while((bytesRead = input.read(buffer)) != -1) {
output.write(buffer, 0, bytesRead);
}
output.flush();
output.close();
input.close();
response = readLine();
return response.startsWith("226");
}
/**
* @param args
* @throws Exception
*/
public static void main(String[] args) throws Exception {
// TODO Auto-generated method stub
simpleFTP ftp = new simpleFTP();
ftp.connect("192.168.2.6");
}
}