需求分析:
在学习到与linux相关的知识时,不可避免要使用到虚拟机,但是虚拟机与我们的物理机是相对独立的,两个系统的文件不能实现很好的共享。虽然可以通过共享文件夹或Xftp工具来帮助我们解决这个问题,但是使用这些方法也会带来不少问题,例如前者不仅操作繁琐,而且容易失败,二后者需要付费或者注册。基于这些我开发了这个程序用于日常的文件传输。
架构设计:
由于采用了c/s模式,我们需要分别在自己的物理机和虚拟机上运行两个程序。
这里我采用的是物理机运行客户机程序,而虚拟机运行服务器程序,因为有些虚拟机并没有图形化界面,很难通过网络传输源文件,代码就需要手敲,服务器程序的代码相对较少。
客户机程序设计:
服务器程序设计:
实现代码:
服务器程序
import java.io.*;
import java.net.ServerSocket;
import java.net.Socket;
public class FtpServer {
FtpServer(){
try{
ServerSocket server = new ServerSocket(8888);
Socket socket = new Socket();
//表示接收文件时的临时编号
int count=1;
//监听请求,用户客户机判断是否能连接
socket = server.accept();
socket.close();
while(true){
//接收后缀
socket = server.accept();
String suffix = null;
InputStream is = socket.getInputStream();
BufferedReader br = new BufferedReader(new InputStreamReader(is));
suffix = br.readLine();
br.close();
socket.close();
//创建File实例对象
File file = new File("/home/tmp_download/tmp"+ count +"."+suffix);
count ++;
if(!file.exists()){
file.createNewFile();
}
OutputStream os = new FileOutputStream(file);
//接收文件信息
socket = server.accept();
byte[] b = new byte[1024];
int len = 0;
is = socket.getInputStream();
while((len = is.read(b)) > 0){
os.write(b,0,len);
}
System.out.println("成功接收文件,临时文件名为tmp" + (count-1) + "." + suffix);
}
}catch (Exception e){
e.printStackTrace();
}
}
public static void main(String[] args) {
FtpServer ftpServer = new FtpServer();
}
}
客户机程序
import java.io.*;
import java.net.InetSocketAddress;
import java.net.Socket;
import java.net.SocketAddress;
import java.util.Scanner;
public class FtpClient {
Scanner scanner = new Scanner(System.in);
String path = null;
File file = null;
String ip = null;
String port = null;
//创建一个套接字实例对象
Socket socket = null;
FtpClient() throws Exception {
boolean run = true;
while(run){
try{
System.out.print("请输入要传输文件的目的主机ip:");
ip = scanner.next();
System.out.print("请输入端口:");
port = scanner.next();
socket = new Socket();
SocketAddress socketAddress = new InetSocketAddress(ip, Integer.valueOf(port));
socket.connect(socketAddress,3*3000);
run = false;
socket.close();
}catch (Exception e){
System.out.println("连接超时!");
}
}
ftpRun();
}
public String ftpRun() throws Exception {
path = "C:\\";
file = new File(path);
String command = null;
while(true){
System.out.print(path+" > ");
command = scanner.next();
if(command.equals("break")){//退出
break;
}else if(command.equals("ls")){//显示当前目录下的文件和目录
ls();
}else if(command.equals("cd")){//打开文件和目录
cd();
}else if(command.equals("change")){//输入盘符字符,盘符
change();
}else if(command.equals("path")){//输入绝对路径,进入该目录或打开该文件
path();
}else if(command.equals("send")){//发送文件
send();
}else if(command.equals("help")) {//获取帮助
help();
}
System.out.println();
}
return path;
}
//列出当前目录下的所有目录和文件
private void ls(){
//获取当前目录下的文件和目录
String[] directorys = file.list();
//打印输出
for(String dir:directorys){
if(new File(path+"\\"+dir).isDirectory()){//是目录
System.out.println("dir:"+dir);
}else{//是文件
System.out.println("file:"+dir);
}
}
}
//打开当前目录下目录或者文件,..为返回上一级目录
private void cd() throws Exception {
String open = null;
System.out.print("file or directory:");
open = scanner.next();
//判断是否返回上一级目录
if(open.equals("..")){
path = file.getParent();
}else{
path = path + "\\" + open;
}
file = new File(path);
//判断文件或目录是否存在
if(!file.exists()){
throw new Exception("文件不存在");
}
}
//输入盘符字符,改变盘符
private void change(){
String change = null;
System.out.println("drive letter:");
change = scanner.next();
path = change + ":\\";
file = new File(path);
}
//输入绝对路径,进入对应目录或打开对应文件
private void path() throws Exception {
String tmpPath = null;
System.out.println("absolute:");
tmpPath = scanner.next();
File tmpFile = new File(tmpPath);
if(!tmpFile.exists()){
throw new Exception("目录或文件不存在");
}
path = tmpPath;
file = tmpFile;
}
//发送文件
private void send() throws Exception {
//根据path创建File实例
file = new File(path);
//查看文件是否存在
if(!file.exists()){
throw new Exception("文件不存在!");
}
//确认是否是文件
if(!file.isFile()){
throw new Exception("这不是一个文件!");
}
//发送后缀
sendSuffix(path);
//发送文件
sendFile(file);
}
//帮助
private void help(){
//存储提示信息
String[] tips = {
"break 退出",
"help 帮助",
"ls 查看当前目录下的目录或文件",
"change 改变盘符(输入单个盘符字母就行,例如C)",
"cd 打开当前目录下的文件或目录(输入目录名或文件名即可,例如document,test.txt",
"send 发送文件",
"path 输入绝对路径即可进入该路径,或打开该文件"
};
//打印所有提示
for(String tip:tips){
System.out.println(tip);
}
}
//发送后缀
public void sendSuffix(String path){
try{
//文件的后缀
String suffix = null;
//获取后缀
suffix = getSuffix(path);
System.out.println(suffix);
//接收Socket
Socket tmpSocket = new Socket(ip,Integer.valueOf(port));
//获取输出流
OutputStream os = tmpSocket.getOutputStream();
//创建字符输出流
PrintWriter pw = new PrintWriter(os);
pw.println(suffix);
pw.flush();
pw.close();
os.close();
tmpSocket.close();
}catch (Exception e){
e.printStackTrace();
}
}
//根据路径获取后缀
private String getSuffix(String path){
String[] split = path.split("\\.");
return split[split.length - 1];
}
//发送文件数据
public void sendFile(File file){
try{
Socket tmpSocket = new Socket(ip,Integer.valueOf(port));
//获取输出流
OutputStream os = tmpSocket.getOutputStream();
//以二进制的形式发送文件信息
InputStream is = new FileInputStream(file);
byte[] b = new byte[1024];
int len = 0;
while((len = is.read(b)) > 0){
os.write(b,0,len);
}
//提示文件传输情况
System.out.println("文件传输成功!");
is.close();
os.close();
tmpSocket.close();
}catch (Exception e){
e.printStackTrace();
}
}
public static void main(String[] args) throws Exception {
FtpClient ftpClient = new FtpClient();
}
}
实际测试
本次测试,将在windows物理机中将指定目录下的test.txt,test.docx,test.html依次传入到ubuntu虚拟机中。
1、创建test.txt.test.docx,test.html
2、打开服务器程序
3、打开客户机程序
4、在客户机程序中发送文件到虚拟机中
5、查看虚拟机中的文件
test.docx中的数据传输成功
经验总结
在实现过程中遇到的问题及解决方法
1)
问题:传输文件时,服务器没有接收到客户机传输的文件的后缀名
解决方法:我仔细查看代码,发现我客户机请求连接了三次,而服务器只监听了两次,导致接收乱套了。服务器多监听一次就好了。
2)
问题:客户机发送文件后缀名后报错,说socket已关闭
解决方法:查看代码后发现,我使用的是同一个socket实例,必须使用不同的实例才行,如果同时使用一个实例一起传输数据容易混在一起,如果分开传输,就必须关闭流,否则会阻塞,但关闭流后会报错,所以要使用不同的实例。