好久没更新了,实在是太忙了,各种活动,可什么都不给力呐,各种郁闷唉.
可博客不更新也不行呐,就把计算机网络第一次大作业拿出来,是基于JavaSocket网络编程,实现简单的FTP协议,包括主动模式(port)和被动模式(pasv)还挺不错的,对于理解FTP协议效果非常好!
普通FTP命令实现非常简单,如:”pwd“,”mkdir“,”rmdir“,”cwd“,”cd“,”dele“,”syst“,”noop“,”quit“及”help“,这些命令实现完全一致.
难点在于主动模式和被动模式的实现,上传(stor)下载(retr),list,nlst都要用到主动模式或者是被动模式.
代码如下:
//import 省略
public class FtpClient {
Socket socket; // 客户端命令socket
BufferedReader in; // 客户端命令输入流
PrintWriter out; // 客户端命令输出流
Socket datasocket; // 客户端数据socket
BufferedReader dataIn; // 客户端数据输入流
PrintWriter dataOut; // 客户端数据输出流
BufferedReader line;
String ftpServerIP; // 服务器地址
ServerSocket serverSocket;
public FtpClient(String ftpServerIP) {
try {
/**
* 初始化socket连接
* 根据服务器端IP地址和命令端口初始化命令socket连接
* 并获取命令输入输出流
*/
this.ftpServerIP = ftpServerIP;
socket = new Socket(ftpServerIP, 21);
in = new BufferedReader(new InputStreamReader(socket.getInputStream()));
out = new PrintWriter(socket.getOutputStream());
// 初始化命令接口
String s = in.readLine();
System.out.println(s);
line = new BufferedReader(new InputStreamReader(System.in));
System.out.println(“请输入用户名”);
// 从 控制台读入用户名密码
String username = line.readLine();
System.out.println(“请输入密码”);
String passwd = line.readLine();
// 用户登录操作
user(username, passwd);
}// try
catch (Exception e) {
e.printStackTrace();
}
}
public void run() {
while (true) {
try {
System.out.println(“请输入命令”);
String methodName = line.readLine();
Method method = this.getClass().getMethod(methodName, null);
method.invoke(this, null);
} catch (Exception e) {
e.printStackTrace();
}
}
}
/** 登录
* 命令格式为 user <username>
* 命令格式为 pass <password>
*/
public void user(String username, String passwd) {
try {
// 用户名登录
out.println(“user “ + username);
out.flush();
// 使用字符串S从服务器端读入返回状态码,并在控制台打印
String s = null;
s = in.readLine();
System.out.println(s);
// 密码输出
out.println(“pass “ + passwd);
// pass命令
// s = 读入返回状态并打印
out.flush();
s = in.readLine();
System.out.println(s);
} catch (Exception e) {
e.printStackTrace();
}
}// user
//只实现一个命令 其他命令完全一致!
// mkdir 命令- 实现在服务器端创建文件夹命令,
public void mkdir() {
try {
System.out.println(“请输入文件夹名称”);
String dirname = line.readLine();
out.println(“mkd “ + dirname);
out.flush();
String s = null;
s = in.readLine();
System.out.println(s);
} catch (IOException e) {
e.printStackTrace();
}
}// mkdir
// list 命令 — 实现目录文件查询功能
// 提示: 需要用到数据连接,请编码实现并打印返回目录下文件信息
public void list() {
try {
System.out.println(“请输入文件夹名称”);
String dirname = line.readLine();
pasv();//被动链接
/**
* 数据连接命令
*/
out.println(“list “ + dirname);
out.flush();
/**
* 数据获取,并打印
*/
char[] buf = new char[10000];
dataIn.read(buf, 0, buf.length);
System.out.println(buf);
/**
* 关闭数据读入和输出流
*/
datasocket.close();
dataOut.close();
dataIn.close();
} catch (IOException e) {
e.printStackTrace();
}
}
// nlst 命令 — 与List命令相同,请使用和list命令不同的数据连接命令,并使用nlst命令
public void nlst() {
try {
port();//主动连接
System.out.println(“请输入文件夹名称”);
String dirname = line.readLine();
out.println(“nlst “ + dirname);
out.flush();
datasocket = serverSocket.accept();
dataIn = new BufferedReader(new InputStreamReader(datasocket.getInputStream()));
/**
* 数据获取,并打印
*/
char[] buf = new char[20000];
dataIn.read(buf, 0, buf.length);
System.out.println(buf);
/**
* 关闭数据读入和输出流
*/
datasocket.close();
dataIn.close();
} catch (IOException e) {
e.printStackTrace();
}
}
// stor 命令 — 文件上传命令
public void stor() {
try {
System.out.println(“请输入文件名称”);
String filename = line.readLine();
/**
* 建立数据连接
*/
// port();//住动连接
pasv();
// 文件操作
File file = new File(filename);
FileInputStream fi = new FileInputStream(file);
byte[] readByte = new byte[1024];
out.println(“stor “ + file.getName());
out.flush();
OutputStream bo = datasocket.getOutputStream();
while (fi.read(readByte) > 0) {
bo.write(readByte);
}
/**
* 文件输入流关闭,数据socket端口关闭
*/
fi.close();
bo.close();
System.out.println(in.readLine());
dataOut.close();
dataIn.close();
datasocket.close();
System.out.println(in.readLine());
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}// retr
// retr 命令 — 文件下载,请参照stor命令实现,并使用和stor不同的数据连接建立命令
// 并下载到自定义文件
public void retr() {
try {
System.out.println(“请输入要下载的文件名”);
String filename = line.readLine();
System.out.println(“请输入保存的地址”);
String saveAdd = line.readLine();
port();
// 文件操作
File file = new File(saveAdd + filename);
if (!file.exists()) {
file.createNewFile();
}
FileOutputStream fo = new FileOutputStream(file);
System.out.println(“”);
out.println(“retr “ + filename);
out.flush();
datasocket = serverSocket.accept();
byte[] readByte = new byte[2000];
BufferedInputStream dataInput = new BufferedInputStream(datasocket.getInputStream());
int n;
while ((n = dataInput.read(readByte)) > 0) {
fo.write(readByte, 0, n);
}
fo.close();
dataInput.close();
System.out.println(in.readLine());
datasocket.close();
System.out.println(in.readLine());
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}// retr
// port 命令 — 主动建立数据连接命令请编码实现,参照pasv命令
// 提示: 命令格式为prot (ip,ip,ip,ip,port>>8,prot&&0xff),根据服务器情况调试
//port1*256+port
public void port() {
String cmd = “port “; // PORT存放用PORT命令传递数据的变量
int i;
try {
// 得到自己的地址InetAddress.getLocalHost().getAddress()
byte[] address = InetAddress.getLocalHost().getAddress();
// 用适当的端口号构造服务器
serverSocket = new ServerSocket(0);
// 准备传送PORT命令用的数据
for (i = 0; i < 4; ++i) {
cmd = cmd + (address[i] & 0xff) + “,”;
}
System.out.println(cmd);
// 利用控制用的流传送PORT命令
int port1 = serverSocket.getLocalPort() >> 8;
int port2 = serverSocket.getLocalPort() & 0xff;
out.println(cmd + port1 + “,” + port2);
out.flush();
String s;
s = in.readLine();
System.out.println(s);
} catch (Exception e) {
e.printStackTrace();
}
}
// pasv 命令 — 被动建立数据连接命令
public void pasv() {
try {
out.println(“pasv”);
out.flush();
String s;
s = in.readLine();
System.out.println(s);
Pattern pattern = Pattern.compile(“.+\\(\\d+,\\d+,\\d+,\\d+,(\\d+),(\\d+)\\)”);
Matcher matcher = pattern.matcher(s);
int port1 = 0;
int port2 = 0;
if (matcher.find()) {
port1 = Integer.parseInt(matcher.group(1));
port2 = Integer.parseInt(matcher.group(2));
}
int port = port1 * 256 + port2;
datasocket = new Socket(“localhost”, port);
dataIn = new BufferedReader(new InputStreamReader(datasocket.getInputStream()));
dataOut = new PrintWriter(datasocket.getOutputStream());
} catch (Exception e) {
e.printStackTrace();
}
}
public static void main(String[] args) {
FtpClient fc = new FtpClient(“127.0.0.1″);
fc.run();
}
}
你的stor命令都没用到pasv里的dataIn 和 dataOut,其实我跟你一样,还是康晓跟我说我才发觉的。
果然是的~
这样就绕开了一个将字节流转换成字符流的难题,具体实现起来还是会有Bug的。不知道哪里出问题了,Debug 不出来啊。。。
解决了发我一份:)
第一次调还没解决,有空再调吧。可能有些东西还是没考虑到。