本来想写一个分布式文件系统,类似Hadoop,以此来增加Hadoop的认识,但是奈何技术受限,所以先写一个一对一的文件传输系统。
一、设计方向
设计一个简单的网盘,服务器一台,客户端若干。实现简单地文件传输。
二、设计思路
- 运用
serversocket
与socket
网络编程作为底层来实现框架。 - 运用Java的I/O实现文件传输的一系列问题。
- 运用配置文件
properties
来实现统一的参数管理。
三、系统文件版本v0.20.8.2.1的问题以及以后改进
我给我写的这个简单的系统框架起了一个简单名字LeeFileSystem
,显得有些仪式感,另外,给它整了一个版本号v0.20.8.2.1
,更有仪式感,哈哈。
版本v0.20.8.2.1问题改进思路
- 传输的文件仅限文本文件。
改进:先学学序列化与反序列化,和如何传输一个压缩包之类的
- 再Java-12的jdk编写的代码,在服务端jdk-1.8.1竟然报错,显示版本不对。
改进:增加服务端的代码开发
- 框架鲁棒性差,耦合性较为满意
改进:开发日志功能,开发错误错误抓取功能,以便更好开发
四、代码及运行结果
为了降低耦合性,我多写了几个类,以便管理和维护。
(1)第一段代码:socket连接
服务器端
package cn.ServerSocketTransform.connect;
import cn.ServerSocketTransform.transform.Commandaccess;
import cn.ServerSocketTransform.transform.FileInput;
import cn.ServerSocketTransform.transform.FileOutput;
import java.io.IOException;
import java.net.ServerSocket;
import java.net.Socket;
/**
* 服务器端 ,此端在服务器端启动
*/
public class ConnectServer {
private static final int port = 8848;
private ServerSocket serverSocket;
private Socket socket;
private Commandaccess commandaccess;
private String[] command;
/**
* 开启服务器
* @throws IOException
*/
public ConnectServer() throws IOException {
/**
* 服务器开启
*/
serverSocket = new ServerSocket(this.port);
System.out.println("waiting connection...");
/**
* 循环服务
*/
while(true){
/**
* 接受请求,并获得地址
*/
socket = serverSocket.accept();
System.out.println(serverSocket.getInetAddress() + ":connect successfully!");
/**
* 接受客户端发送的命令,并处理
* 放入if-else当中
* download下载,即服务端--data-->客户端
* upload上传,即服务端<--data--客户端
*/
commandaccess = new Commandaccess(socket);
command = commandaccess.getcommand();
/**
* commands[0]:是下载或上传的命令
* commands[1-2]:是下载的地址
*/
if (command[0].equals("download")){
new Thread(()->{
try {
FileOutput fo = new FileOutput(socket);
fo.send(command[1]);
fo.close();
} catch (IOException e) {
e.printStackTrace();
}
}).start();
}else if (command[0].equals("upload")){
new Thread(()->{
try {
FileInput fi = new FileInput(socket);
fi.download(command[2]);//上传到服务器的地址
fi.close();
} catch (IOException e) {
e.printStackTrace();
}
}).start();
}else {
System.out.println("Error Command");
}
}
}
public void close() throws IOException {
serverSocket.close();
}
/**
public static void main(String[] args) throws IOException {
ConnectServer connectServer = new ConnectServer();
connectServer.close();
}*/
public Socket getSocket() {
return socket;
}
public void setSocket(Socket socket) {
this.socket = socket;
}
}
客户端
package cn.ServerSocketTransform.connect;
import cn.ServerSocketTransform.transform.Command;
import cn.ServerSocketTransform.transform.FileInput;
import cn.ServerSocketTransform.transform.FileOutput;
import java.io.IOException;
import java.net.Socket;
/**
* 此端在客户端使用
*/
public class ConnectServerClien {
private static final int ServerPort = 8848;
private Socket socket;
private Command command;
private String[] commands;
public ConnectServerClien(String ip) throws IOException {
/**
* 发送请求至服务器
*/
socket = new Socket(ip, ServerPort);
/**
* 发送命令,并由if-else来执行解析
* download下载,即服务端--data-->客户端
* upload上传,即服务端<--data--客户端
*/
command = new Command(socket);
commands = command.commandsend();
/**
* commands[0]:是下载或上传的命令
* commands[1-2]:是下载的地址
*/
if (commands[0].equals("download") ){
new Thread(()->{
try {
FileInput fi = new FileInput(socket);
fi.download(commands[2]);
fi.close();
} catch (IOException e) {
e.printStackTrace();
}
}).start();
}else if (commands[0].equals("upload")){
new Thread(()->{
try {
FileOutput fo = new FileOutput(socket);
fo.send(commands[1]);
fo.close();
} catch (IOException e) {
e.printStackTrace();
}
}).start();
}
}
public void close() throws IOException {
socket.close();
}
/**
public static void main(String[] args) throws IOException {
ConnectServerClien connectServerClien = new ConnectServerClien("127.0.0.1");
connectServerClien.close();
}*/
public Socket getSocket() {
return socket;
}
public void setSocket(Socket socket) {
this.socket = socket;
}
}
(2)第二段代码:文件传输
因为考虑到要在服务器上运行,所以想写一个简单的命令运行方式,但好像,不是这么个设计方法。
考虑到用shell,我还是scanner来实例化我的伪命令
吧。
发送命令的客户端
package cn.ServerSocketTransform.transform;
import java.io.IOException;
import java.io.OutputStream;
import java.net.Socket;
import java.util.Scanner;
/**
* 此代码适用于发送命令,与Linux的bin相当
*/
public class Command {
private Socket socket;
private OutputStream outputStream;
public Command(Socket socket){
this.socket = socket;
System.out.println("命令:\n" +
"download serveraddress\\file localaddress\\file\n" +
"upload localaddress\\file serveraddress\\file\n" +
"---------------------------------");
System.out.println("[LFS@clience]");
}
public String[] commandsend() throws IOException {
/**
* 键盘输入流
*/
Scanner scanner = new Scanner(System.in);
String command = scanner.nextLine();
/**
* 发送至服务器端
*/
outputStream = socket.getOutputStream();
outputStream.write(command.getBytes());
/**
* 本地解析
*/
String[] commands = command.split(" ");
/*for (String i : commands){
System.out.println(i);
}*/
/*for (int i = 0; i< 3 ; i++){
if (commands[i].equals("download")) {
System.out.println(commands[i] + i);
}
}*/
return commands;
}
}
接受命令的服务端
package cn.ServerSocketTransform.transform;
import java.io.IOException;
import java.io.InputStream;
import java.net.Socket;
/**
* 此代码适用于接受命令,与Linux的bin相当
*/
public class Commandaccess {
private Socket socket;
private InputStream inputStream;
public Commandaccess(Socket socket){
this.socket = socket;
}
public String[] getcommand() throws IOException {
/**
* 接受命令,并解析
*/
inputStream = socket.getInputStream();
byte[] b = new byte[1024];
int len = inputStream.read(b);
String command = new String(b ,0 ,len);
/**
* 返回服务器
*/
String[] commands = command.split(" ");
return commands;
}
}
文件传输发送端
package cn.ServerSocketTransform.transform;
import java.io.*;
import java.net.Socket;
/**
* 用来 上传 文件
*/
public class FileOutput {
private Socket socket;
private BufferedInputStream bis;
private BufferedOutputStream bos;
/**
* 传入socket对象
* @param socket
* @throws IOException
*/
public FileOutput(Socket socket) throws IOException {
this.socket = socket;
}
/**
* 传入指定的发送的文件路径
* @param path
* @throws IOException
*/
public void send(String path) throws IOException {
bis = new BufferedInputStream(new FileInputStream(path));//指定的文件下载路径
bos = new BufferedOutputStream(socket.getOutputStream());
byte[] b = new byte[1024 * 8];
int len;
while ((len = bis.read(b)) != -1){
bos.write(b, 0, len);
bos.flush();
}
System.out.println("sending mission success");
}
public void close() throws IOException {
bis.close();
bos.close();
}
}
文件传送接收端
package cn.ServerSocketTransform.transform;
import java.io.*;
import java.net.Socket;
/**
* 下载
* /root/Transform
*/
public class FileInput {
private Socket socket;
private BufferedInputStream bis;
private BufferedOutputStream bos;
/**
* 获取socket对象,进行文件下载
* @param socket
* @throws IOException
*/
public FileInput(Socket socket) throws IOException {
this.socket = socket;
}
/**
* 传入服务器文件的地址,或者下载文件的地址
* @param path
* @throws IOException
*/
public void download(String path) throws IOException {
bis = new BufferedInputStream(socket.getInputStream());
bos = new BufferedOutputStream(new FileOutputStream(path));//path是指定的下载地址
byte[] b = new byte[1024 * 8];
int len;
while ((len = bis.read(b)) != -1){
bos.write(b, 0, len);
}
System.out.println("downloading mission success");
}
public void close() throws IOException {
bis.close();
bos.close();
}
}
(3)第三段代码:配置文件、初始化文件夹
配置文件
#该文件系统名称LeeFileSystem,简称LFS网络文件系统
#当前Version:0.20.8.2.1-inner
#核心技术为File与socket网络编程,将来加入分布式
#受启发于Hadoop,给文件系统耦合度低,高效率
#配置服务器的IP与文件系统的根目录
ServerAddress=D:\\JavaProgramSummarySite\\ServerSocketTransform\\test
ServerIP=127.0.0.1
加载配置文件
package cn.FileSystem;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.util.Properties;
/**
* 加载类,加载配置文件
*/
public class PropertiesUtil {
private Properties properties;
private File file;
private FileInputStream fileInputStream;
/**
* 读取加载文件位置,以此加载配置
* @throws IOException
*/
public PropertiesUtil() throws IOException {
properties = new Properties();
file = new File("ServerAddress.properties");
fileInputStream = new FileInputStream(file.getAbsolutePath());
properties.load(fileInputStream);
}
/**
* 返回加载类对象,以便获取加载的文件信息
* @return
*/
public Properties getProperties() {
return properties;
}
}
将配置文件加载后,即可读取并传入其他文件,配置文件的内容是即将创建的实现文件传输保存地址的文件,还有服务器端的IP这样,客户端只需读取服务器的IP即可实现在预先创建的文件夹下的文件操作。当然,服务端也可以通过读取配置文件里面的那个地址,来实现,这个“网盘”的存储地址。
初始化文件
package cn.FileSystem;
import java.io.File;
import java.io.IOException;
import java.text.SimpleDateFormat;
import java.util.Date;
/**
* 创建Lee文件系统的地址
*/
public class CreateFileBlock {
private PropertiesUtil propertiesUtil;
private String address;
private File file;
private Date date;
private static final String addr = "SERVER";
/**
* 先加载配置文件
* 获取要创建文件的地址
* 判断文件存在与否,输出文件信息
* @throws IOException
*/
public CreateFileBlock() throws IOException {
propertiesUtil = new PropertiesUtil();
address = propertiesUtil.getProperties().getProperty("ServerAddress");
file = new File(address+"\\"+addr);
if (file.exists()){
date = new Date(file.lastModified());
SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd hh:mm:ss");
String time = format.format(date);
System.out.println("Absolutepath:" + file.getAbsolutePath()
+"\nlastmodified:" + time );
}else {
System.out.println("Create successfully:" + file.mkdirs()
+"\nisDirectory:" + file.isDirectory()
+"\nAbsolutepath:" + file.getAbsolutePath()
+"\nKB:" + file.length() );
}
}
}
(4)总主函数
服务端
package cn.ServerSocketTransform.job;
import cn.FileSystem.CreateFileBlock;
import cn.ServerSocketTransform.connect.ConnectServer;
import java.io.IOException;
public class jobserver {
public static void main(String[] args) throws IOException, InterruptedException {
/**
* 此对象是用来创建工作区域的
*/
CreateFileBlock createFileBlock = new CreateFileBlock();
ConnectServer connectServer = new ConnectServer();
Thread.sleep(5000);
connectServer.close();
}
}
客户端
package cn.ServerSocketTransform.job;
import cn.FileSystem.PropertiesUtil;
import cn.ServerSocketTransform.connect.ConnectServerClien;
import java.io.IOException;
public class jobclien {
public static void main(String[] args) throws IOException, InterruptedException {
PropertiesUtil propertiesUtil = new PropertiesUtil();
ConnectServerClien connectServerClien = new ConnectServerClien(propertiesUtil.getProperties().getProperty("ServerIP"));
Thread.sleep(5000);
connectServerClien.close();
}
}
五、运行
开服务器、开客户端:
可以看到创建的SERVER文件夹,和客户端连接的信息
发送消息:
上图可见,在server中,有一个已经被传送来的文件。
简单功能实现,以后再去完善。