多文件自平衡云模式文件传输系统(下)
下面我们开始处理文件的发送和接受。
文件的发送与接收
关于文件的发送和接收,我们将它定义在一个接口中。
//
public interface ISendReceive {
void send(DataOutputStream dos, byte[] content) throws Exception;
byte[] receive(DataInputStream dis, int len) throws Exception;
}
它的实现类如下:
//
public class NetSendReceive implements ISendReceive {
public static final int DEFAULT_SECTION_LEN = 1 << 15;
private int bufferSize;
public NetSendReceive() {
this.bufferSize = DEFAULT_SECTION_LEN;
}
public ISendReceive setBufferSize(int bufferSize) {
this.bufferSize = bufferSize;
return this;
}
@Override
public void send(DataOutputStream dos, byte[] content) throws Exception {
int len = content.length;
int offset = 0;
int curLen;
while (len > 0) {
curLen = len > this.bufferSize ? this.bufferSize : len;
dos.write(content, offset, curLen);
offset += curLen;
len -= curLen;
}
}
@Override
public byte[] receive(DataInputStream dis, int len) throws Exception {
byte[] buffer = new byte[len];
int curLen;
int factLen;
int offset = 0;
while (len > 0) {
curLen = len > this.bufferSize ? this.bufferSize : len;
factLen = dis.read(buffer, offset, curLen);
// System.out.println("本次接收字节数:" + factLen);
len -= factLen;
offset += factLen;
}
return buffer;
}
}
在用户定义如何发送、接受文件片段后,我们需要思考的是如何按上一篇约定的格式发送、接受文件片段。
//
public class FileSectionSendReceive {
private ISendReceive sendReceive;
public FileSectionSendReceive() {
this.sendReceive = new NetSendReceive();
}
public void setSendReceive(ISendReceive sendReceive) {
this.sendReceive = sendReceive;
}
public void sendLastSection(DataOutputStream dos) throws Exception {
FileSectionInfo sectionInfo = new FileSectionInfo(0, 0, 0);
sectionInfo.setContent(new byte[0]);
sendReceive.send(dos, sectionInfo.toBytes());
}
public void sendSection(DataOutputStream dos, FileSectionInfo sectionInfo) throws Exception {
sendReceive.send(dos, sectionInfo.toBytes());
if (sectionInfo.getLen() <= 0) {
return;
}
sendReceive.send(dos, sectionInfo.getContent());
}
public FileSectionInfo receiveSection(DataInputStream dis) throws Exception {
byte[] head = sendReceive.receive(dis, 16);
FileSectionInfo sectionInfo = new FileSectionInfo(head);
int sectionLen = sectionInfo.getLen();
if (sectionLen > 0) {
sectionInfo.setContent(sendReceive.receive(dis, sectionLen));
}
return sectionInfo;
}
}
由于接收端可能接收多个文件,而这些文件又是由多个发送端分片段发送
的;这意味着,接收端所接收的片段,对应的文件未必是“聚集”的;
也就是说,对于接收端,可能会出现在多个文件来回切换“接收”的情
况。对于文件的接收过程,需要依赖于RandomAccessFile类对象;每
接收一个片段,在完成这个片段内容向文件写前,必须先建立RAF对
象;当这个片段接收完毕后,下一个片段极有可能是另一个文件。这
就会产生,接收端需要不停的对同一个文件,产生RAF对象的可能。
RAF对象的初始化和关闭过程也是需要消耗系统资源的。在此我们可以考虑能否建立一个RAF迟,当一个文件开始读写时,除非接受完毕,否则不轻易关闭,但是在此处需要考虑到多个线程同时写一个文件时的安全问题。
//
public class FileReadWrite {
private int fileNo;
private String filePath;
private RandomAccessFile raf;
public FileReadWrite(int fileNo, String filePath) {
this.fileNo = fileNo;
this.filePath = filePath;
}
public int getFileNo() {
return fileNo;
}
public FileSectionInfo readSection(FileSectionInfo section) throws IOException {
if (raf == null) {
raf = new RandomAccessFile(filePath, "r");
}
raf.seek(section.getOffset());
int len = section.getLen();
byte[] buffer = new byte[len];
raf.read(buffer);
section.setContent(buffer);
return section;
}
public boolean writeSection(FileSectionInfo section) {
if (this.raf == null) {
synchronized (filePath) {
if (this.raf == null) {
try {
this.raf = new RandomAccessFile(filePath, "rw");
} catch (FileNotFoundException e) {
e.printStackTrace();
return false;
}
}
}
}
try {
synchronized (filePath) {
raf.seek(section.getOffset());
raf.write(section.getContent());
}
} catch (IOException e) {
return false;
}
return true;
}
public void close() {
try {
this.raf.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
我们建立一个ResourceFilePool类(池子)将所有文件对应的FileReadWrite放到池子中,除非文件收发结束,不轻易销毁RandomAccessFile对象,降低系统的资源消耗。
//
public class ResourceFilePool {
private Map<Integer, FileReadWrite> fileAcceptPool;
public ResourceFilePool() {
this.fileAcceptPool = new HashMap<Integer, FileReadWrite>();
}
public void addFileInfo(int fileNo, String filePath) {
FileReadWrite readWrite = new FileReadWrite(fileNo, filePath);
this.fileAcceptPool.put(fileNo, readWrite);
}
public FileReadWrite getFileAccpet(int fileNo) {
return this.fileAcceptPool.get(fileNo);
}
public void addFileList(SourceFileList sourList) {
List<FileInfo> fileList = sourList.getFileList();
for(FileInfo file : fileList) {
addFileInfo(file.getFileNo(), sourList.getAbsoluteRoot() + file.getFilePath());;
}
}
}
== 文件的发送 == :
//
public class FileSender implements Runnable{
private String receiveIp;
private int receivePort;
private ResourceFilePool rscPool;
private List<FileSectionInfo> sectionList;
private FileSectionSendReceive FileSectionSend;
public FileSender(String receiveIp, int receivePort, ResourceFilePool rscPool,
List<FileSectionInfo> sectionList) {
this.receiveIp = receiveIp;
this.receivePort = receivePort;
this.rscPool = rscPool;
this.sectionList = sectionList;
this.FileSectionSend = new FileSectionSendReceive();
}
public void startSend() {
new Thread(this, "文件发送").start() ;
}
@Override
public void run() {
Socket socket = null;
DataOutputStream dos = null;
try {
socket = new Socket(receiveIp, receivePort);
dos = new DataOutputStream(socket.getOutputStream());
for(FileSectionInfo section : sectionList) {
FileReadWrite readWrite = rscPool.getFileAccpet(section.getFileNo());
section = readWrite.readSection(section);
FileSectionSend.sendSection(dos, section);
}
FileSectionSend.sendLastSection(dos);
ResourceSender.incSendCount();
} catch (UnknownHostException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} catch (Exception e) {
e.printStackTrace();
}finally {
ResourceSender.decSendCount();
close(socket, dos);
}
}
private void close(Socket socket, DataOutputStream dos) {
try {
if(dos != null) {
dos.close();
}
} catch (IOException e) {
e.printStackTrace();
}
if(socket != null && !socket.isClosed()) {
try {
socket.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
下面这个类ResourceSender是对FileSender类的进一步封装与完善,增加了文件片段发送的计数功能等。
//
public class ResourceSender implements IResourceSender{
private SourceHolderNode holder;
private static final SendCounter sendCounter = new SendCounter();
public ResourceSender() {
this.holder = SourceHolderNode.newInstance();
}
public static void incSendCount() {
sendCounter.incSendAccount();
}
public static void decSendCount() {
sendCounter.decSendCount();
}
@Override
public void sendResource(String receiveIp, int receivePort, ResourceInfo baseInfo,
List<FileSectionInfo> sectionList) {
sendCounter.incSendCount();
SourceFileList fileList = holder.getSourceFileList(baseInfo);
ResourceFilePool rscFilePool = new ResourceFilePool();
rscFilePool.addFileList(fileList);
FileSender fileSender = new FileSender(receiveIp, receivePort, rscFilePool, sectionList);
fileSender.startSend();
}
@Override
public SendCounter getSendCounter() {
return sendCounter;
}
}
== 文件的接受 ==:
下面展示一些 接收端代码片
。
接收端服务器:
//
public class ReceiveServer implements Runnable{
private static final int DEFAULT_RECEIVE_PORT = 54191;
private ServerSocket server;
private String ip;
private int port;
private int sendCount;
private volatile boolean goon;
private ThreadPoolExecutor executor;
private ResourceFilePool receiveFilePool;
public ReceiveServer() {
try {
this.ip = InetAddress.getLocalHost().getHostAddress();
this.executor = new ThreadPoolExecutor(5, 20, 500,
TimeUnit.MILLISECONDS, new LinkedBlockingQueue<Runnable>());
this.receiveFilePool = new ResourceFilePool();
this.port = DEFAULT_RECEIVE_PORT;
this.goon = false;
this.sendCount = 0;
} catch (UnknownHostException e) {
e.printStackTrace();
}
}
public void setPort(int port) {
this.port = port;
}
public void setSendCount(int sendCount) {
this.sendCount = sendCount;
}
public String getIp() {
return ip;
}
public void setIp(String ip) {
this.ip = ip;
}
public void setReceiveFilePool(SourceFileList sourceFileList) {
String absolutePath = sourceFileList.getAbsoluteRoot();
List<FileInfo> fileList = sourceFileList.getFileList();
for(FileInfo file : fileList) {
this.receiveFilePool.addFileInfo(file.getFileNo(),
absolutePath + file.getFilePath());
}
}
public void startup() {
if(this.goon) {
return;
}
try {
this.server = new ServerSocket(port);
this.goon = true;
new Thread(this, "接收服务器").start();
} catch (IOException e) {
e.printStackTrace();
}
}
private void shutdown() {
if(this.server != null && !this.server.isClosed()) {
try {
this.server.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
@Override
public void run() {
if(this.sendCount <= 0) {
return;
}
for(int count = 0; count < this.sendCount; count++) {
try {
Socket sender = this.server.accept();
Receiver receiver = new Receiver(sender);
this.executor.execute(receiver);
} catch (IOException e) {
e.printStackTrace();
}
}
shutdown();
}
public class Receiver implements Runnable {
private Socket socket;
private DataInputStream dis;
private FileSectionSendReceive sectionReceive;
public Receiver(Socket socket) throws IOException {
this.socket = socket;
this.sectionReceive = new FileSectionSendReceive();
this.dis = new DataInputStream(this.socket.getInputStream());
}
@Override
public void run() {
try {
FileSectionInfo section = sectionReceive.receiveSection(dis);
while(section.getLen() > 0) {
FileReadWrite file = receiveFilePool.getFileAccpet(section.getFileNo());
file.writeSection(section);
section = sectionReceive.receiveSection(this.dis);
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
}
接收端:
//
public class ResourceReceiver {
private ResourceInfo baseInfo;
private SourceFileList fileList;
private ResourceRequester request;
private FileDistribution fileDistribution;
private ISenderSelectedStrategy senderSelectedStrategy;
private RMIClient receiveClient;
private IResourceSender resourceSender;
private ReceiveServer receiveServer;
public ResourceReceiver() {
this.request = new ResourceRequester();
this.fileDistribution = new FileDistribution();
this.senderSelectedStrategy = new DefaultSenderSelector();
this.receiveServer = new ReceiveServer();
}
public void setMaxFileSectionSize(int maxSectionSize) {
this.fileDistribution.setMaxSectionSize(maxSectionSize);
}
public void setDistributionStrategy(IDistributionStrategy strategy) {
this.fileDistribution.setDistributionStrategy(strategy);
}
public void setSenderSelectedStrategy(ISenderSelectedStrategy strategy) {
this.senderSelectedStrategy = strategy;
}
public void setResourceRegistryCenterIp(String ip) {
this.request.setRmiServerIp(ip);
}
public void setResourceRegistryCenterPort(int port) {
this.request.setRmiServerPort(port);
}
public void setBaseInfo(ResourceInfo baseInfo) {
this.baseInfo = baseInfo;
}
public void setFileList(SourceFileList fileList) {
this.fileList = fileList;
}
public void setAbsoluteRoot(String absoluteRoot) {
this.fileList.setAbsoluteRoot(absoluteRoot);
}
public void setReceiveServerIp(String ip) {
this.receiveServer.setIp(ip);
}
public void setReceiveServerPort(int port) {
this.receiveServer.setPort(port);
}
public void getResourceFiles() throws ResourceNotExistException {
//TODO 这里要启动接受端的接收进度 监控条
List<DefaultNetNode> senderList = request.getAddressList(baseInfo);
if(senderList == null || senderList.isEmpty()) {
throw new ResourceNotExistException("资源[" + baseInfo + "]不存在!");
}
//senderList = this.senderSelectedStrategy.selectesSender(senderList);
int sendCount = senderList.size();
List<List<FileSectionInfo>> fileSectionListList =
this.fileDistribution.distribution(this.fileList, sendCount);
this.receiveServer.setReceiveFilePool(fileList);
this.receiveServer.setSendCount(sendCount);
//this.receiveServer.startup();
int index = 1;
for(List<FileSectionInfo> sectionList : fileSectionListList) {
System.out.println("第" + index + "个发送端要发送的片段列表:");
int i = 1;
for(FileSectionInfo section : sectionList) {
System.out.println("\t" + index + "-" + "(" + i + ")");
i++;
}
++index;
//TODO 想发送端传递:
//1、接收端服务器ip和port;
//2、资源详细列表,即FileList;
//3、文件片段列表 即 List<FileSectionInfo>;
//4、启动发送端开始发送!(这里通过创建新线程实现启动并完成发送);
}
}
}
== 注册中心 ==
下面展示一些 内联代码片
。
// An highlighted block
public class ResourceCenterServer implements IResourceListener {
public static final int RSCPort = 54187;
private ResourceRegistryCenter center;
public ResourceCenterServer() {
this.center = new ResourceRegistryCenter(RSCPort);
this.center.addListener(this);
}
public void startup() {
this.center.startup();
}
@Override
public void dealMessage(String message) {
System.out.println(message);
}
@Override
public void dealMessage(String message) {
System.out.println(message);
}
上面就是关于多文件云传输的代码部分。