FTP主要要点:
发送消息通过socket.getInputStream()/getOutputStream 的接口,比如发送USER数据
java
writer = new BufferedWriter(new OutputStreamWriter(socket.getOutputStream()));
writer.writer("USER "+user+"\r\n");
注意\r\n,和USER后的空格
ftp基于TCP的服务,使用2个端口,21->用于命令端口,20->用于数据端口但是数据端口并不总是20。这就是主动模式与被动模式的区别
主动FTP:客户端从非特定端口N向客户端21端口发送请求,客户端监听N+1 的端口,并发送“port N+1”到服务器,绑定自己的数据端口。
N端口到FTP服务器21端口,客户端初始化
- 服务器21端口客户端N端口,服务器响应客户端
- 服务器20端口到客户机N+1端口,服务器初始数据到数据端口
- N+1端口到服务器20端口,客户端发送ACK到服务器数据端口
被动模式:在被动模式下,命令连接和数据连接都有客户端发起。服务端只响应客户端的请求即可。
在被动模式下。客户端不会主动提交PORT命令来允许服务器回连它的数据端口,而是由服务器发送PORT P给客户端。
xml
227 Entering Passive Mode (127,0,0,1,212,237)
这是进入PASV模式后的响应,127.0.0.1是本地主机,后面212,237是端口后,实际端口是212* 256 + 237 = 54509,这才是服务器分给我的数据端口后,每次上传,下载,获取服务文件列表,都需要通过这个端口,建立新的socket,获取数据。
这样做的好处是避免服务器到客户端数据端口被防火墙过滤掉
- N端口到FTP服务器21端口,客户端初始化
- 服务器21端口客户端N端口,服务器响应客户端
- N+1端口到FTP服务器P(这里不是20端口了)端口,客户端初始化数据链路
- 服务器响应ACK给客户端
优缺点:主动模式对服务器有利,端口固定,对客户端来说,容易被客户端的防火墙柱塞数据端口。被动模式,对服务器不利,用到的高位数据端口可能被服务器的防火墙屏蔽。反正主动与否看数据端口是否由服务端发起,产生这个模式的锅防火墙背定了!
本文章参考这篇文章
FTP数据传输:
ftp的数据传输通过数据端口,进入被动模式,客户端主动连接数据端口,当通信端口21发送
writeLine("LIST")
后,在数据端口准备接受返回回来的数据,同理,上传和下载文件也是这么回事
其他的貌似我也没有研究了,反正作业要求就这么多,就到此位置了。
代码如下:
package ftpCient;
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.InputStreamReader;
import java.io.OutputStreamWriter;
import java.net.Socket;
import java.util.LinkedList;
import java.util.StringTokenizer;
//trylock /lock 是我在类外为了获取这个类的信息的锁,读者可以忽略。
public class SimpleFTP {
Socket socket = null;
BufferedReader reader = null;
BufferedWriter writer = null;
private static boolean DEBUG = false;
LinkedList<String> strlist = new LinkedList<String>();
boolean lock = false;
public SimpleFTP() throws IOException, InterruptedException {
}
public synchronized void connect(String host) throws IOException, InterruptedException {
connect(host, 21);
}
public synchronized void connect(String host, int port) throws IOException, InterruptedException {
connect(host, port, "anonymous", "anonymous");
}
public synchronized void connect(String host, int port, String user, String pass)
throws IOException, InterruptedException {
if (socket != null) {
throw new IOException("FTP is already connected!");
}
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 ")) {
trylock();
strlist.add("SimpleFTP received an unknown response when connecting to the FTP server: " + response);
unlock();
}
sendLine("USER " + user);
response = readLine();
if (!response.startsWith("331 ")) {
trylock();
strlist.add("SimpleFTP received an unknown response after sending the user: " + response);
unlock();
}
sendLine("PASS " + pass);
response = readLine();
if (!response.startsWith("230 ")) {
trylock();
strlist.add("SimpleFTP was unable to log in with the supplied password: " + response);
unlock();
}
// now logged in
}
public synchronized void disconnect() throws IOException {
try {
sendLine("QUIT");
readLine();
} catch (Exception e) {
// TODO: handle exception
} finally {
socket = null;
}
}
public synchronized String pwd() throws IOException, InterruptedException {
sendLine("PWD");
String dir = null;
Thread.sleep(100);
String respone = readLine();
if (respone.startsWith("257 ")) {
int firstQuote = respone.indexOf('\"');
int secondQuote = respone.indexOf('\"', firstQuote + 1);
if (secondQuote > 0) {
dir = respone.substring(firstQuote + 1, secondQuote);
}
}
return dir;
}
public String list() throws IOException, InterruptedException {
sendLine("PASV");
String respone = readLine();
sendLine("LIST");
respone = readLine();
String ip = null;
int port = -1;
int opening = respone.indexOf('(');
int closing = respone.indexOf(')', opening + 1);
if (closing > 0) {
String dataLink = respone.substring(opening + 1, closing);
StringTokenizer tokenizer = new StringTokenizer(dataLink, ",");
try {
ip = tokenizer.nextToken() + "." + tokenizer.nextToken() + "." + tokenizer.nextToken() + "."
+ tokenizer.nextToken();
port = Integer.parseInt(tokenizer.nextToken()) * 256 + Integer.parseInt(tokenizer.nextToken());
} catch (Exception e) {
trylock();
strlist.add("data link information:" + respone);
unlock();
}
}
Socket dataSocket = new Socket(ip, port);
respone = readLine();
BufferedInputStream input = new BufferedInputStream(dataSocket.getInputStream());
// maybe out limit ,but I just want easy
byte[] buffer = new byte[10240];
int byteRead = 0;
byteRead = input.read(buffer);
String string = new String(buffer, 0, byteRead);
return string;
}
public synchronized boolean cwd(String dir) throws IOException, InterruptedException {
sendLine("CMD " + dir);
String respone = readLine();
return (respone.startsWith("250 "));
}
public synchronized boolean stor(File file) throws IOException, InterruptedException {
if (file.isDirectory()) {
trylock();
strlist.add("FTP canot upload dir");
unlock();
}
String filename = file.getName();
return stor(filename, filename);
}
public synchronized boolean stor(String uploadFilename, String filename) throws IOException, InterruptedException {
BufferedInputStream input = new BufferedInputStream(new FileInputStream(uploadFilename));
sendLine("PASV");
String respone = readLine();
if (!respone.startsWith("227 ")) {
trylock();
strlist.addLast("SimpleFTP could not request passive mode: " + respone);
unlock();
}
String ip = null;
int port = -1;
int opening = respone.indexOf('(');
int closing = respone.indexOf(')', opening + 1);
if (closing > 0) {
String dataLink = respone.substring(opening + 1, closing);
StringTokenizer tokenizer = new StringTokenizer(dataLink, ",");
ip = tokenizer.nextToken() + "." + tokenizer.nextToken() + "." + tokenizer.nextToken() + "."
+ tokenizer.nextToken();
port = Integer.parseInt(tokenizer.nextToken()) * 256 + Integer.parseInt(tokenizer.nextToken());
}
trylock();
sendLine("STOR " + filename);
Socket dataSocket = new Socket(ip, port);
String response = readLine();
unlock();
if (!response.startsWith("125 ")) {
if (!response.startsWith("150 ")) {
trylock();
strlist.add("SimpleFTP was not allowed to send the file: " + response);
unlock();
}
}
BufferedOutputStream outputStream = new BufferedOutputStream(dataSocket.getOutputStream());
byte[] buffer = new byte[4096];
int byteRead = 0;
while ((byteRead = input.read(buffer)) != -1) {
outputStream.write(buffer, 0, byteRead);
}
outputStream.flush();
outputStream.close();
input.close();
return true;
}
public synchronized boolean pasv() throws IOException, InterruptedException {
sendLine("PASV");
return true;
}
public synchronized boolean bin() throws IOException, InterruptedException {
sendLine("TYPE I");
String response = readLine();
return (response.startsWith("200 "));
}
public synchronized boolean ascii() throws IOException, InterruptedException {
sendLine("TYPE A");
String response = readLine();
return (response.startsWith("200 "));
}
private void sendLine(String line) throws IOException {
if (socket == null) {
throw new IOException("FTP not connected");
}
try {
writer.write(line + "\r\n");
writer.flush();
if (DEBUG)
System.out.println(">" + line);
} catch (IOException e) {
socket = null;
throw e;
}
}
public String readLine() throws IOException, InterruptedException {
String line = reader.readLine();
trylock();
strlist.add(line);
System.out.println(line);
unlock();
return line;
}
public String[] getOutput() throws InterruptedException {
if (strlist.isEmpty())
return null;
trylock();
String[] tmp = new String[strlist.size()];
for (int i = 0; i < strlist.size(); i++)
tmp[i] = strlist.get(i);
strlist.clear();
unlock();
return tmp;
}
private void trylock() throws InterruptedException {
while (lock) {
Thread.sleep(50);
}
lock = true;
}
private void unlock() {
lock = false;
}
public boolean isLock() {
return lock;
}
}