该篇主要完成资源接收。
1、资源接收端通信模式
- 资源接收端根据文件长度接收资源片段。每接收一个文件,关闭通信。
- 资源接收端需要准备,未接收片段池和文件对象池。
- 接收端可能有多个发送端。
接收端建立C/S服务器端。
2、接收片段内容处理
- 判断发送端是否发送完毕;发送完毕,关闭通信。
- 获取片段内容
- 将内容写入文件
- 填充未接收片段
- 若未接收片段未空,关闭文件资源。
class SectionProcesser implements ISectionProcesser {
private RandomAccessFilePool accessFilePool;
private UnreceivePool unreceivePool;
public SectionProcesser(RandomAccessFilePool accessFilePool,
UnreceivePool unreceivePool) {
this.accessFilePool = accessFilePool;
this.unreceivePool = unreceivePool;
}
@Override
public void processerSection(SectionHander sectionHander, byte[] context) throws Exception {
long fileId = sectionHander.getFileId();
if (fileId == -1) {
close();
return;
}
RandomAccessFile raf = accessFilePool.getRandomAccessFile(fileId, "rw");
FileAccessor.writeSection(raf,sectionHander.getOffset(),context);
UnreceiveSection unreceiveSection = unreceivePool.getUnreceiveSection(fileId);
boolean isAllreceive = unreceiveSection.receiveSection(sectionHander);
if (isAllreceive) {
this.accessFilePool.closeRaf(fileId);
}
}
}
3. 接收线程
负责对一个接收端内容接收
package man.kuke.core;
import java.io.DataInputStream;
import java.io.IOException;
import java.io.RandomAccessFile;
import java.net.Socket;
/**
* @author: kuke
* @date: 2020/11/29 - 20:08
* @description:
*/
public class Receiver implements Runnable{
private DataInputStream dis;
private SectionTransfer sectionTransfer;
private volatile boolean goon;
private Socket socket;
public Receiver(RandomAccessFilePool accessFilePool,
UnreceivePool unreceivePool,Socket socket) {
try {
dis = new DataInputStream(socket.getInputStream());
} catch (IOException e) {
e.printStackTrace();
}
this.socket = socket;
//片段接收器
sectionTransfer = new SectionTransfer();
goon = true;
this.sectionTransfer.setSectionProcessor(new SectionProcesser(accessFilePool,unreceivePool));
}
public void start() {
new Thread(this).start();
}
@Override
public void run() {
while(goon) {
try {
this.sectionTransfer.receiveSection(this.dis);
} catch (Exception e) {
//接收失败,应该考虑断点续传
if (goon) {
//TODO dealerror
}
close();
}
}
close();
}
private void close() {
goon = false;
try {
if (dis != null) {
dis.close();
}
} catch (IOException ignored) {
} finally {
dis = null;
}
try {
if (socket != null && socket.isClosed()) {
socket.close();
}
} catch (IOException ignored) {
} finally {
socket = null;
}
}
class SectionProcesser implements ISectionProcesser {
private RandomAccessFilePool accessFilePool;
private UnreceivePool unreceivePool;
public SectionProcesser(RandomAccessFilePool accessFilePool,
UnreceivePool unreceivePool) {
this.accessFilePool = accessFilePool;
this.unreceivePool = unreceivePool;
}
@Override
public void processerSection(SectionHander sectionHander, byte[] context) throws Exception {
long fileId = sectionHander.getFileId();
if (fileId == -1) {
close();
return;
}
RandomAccessFile raf = accessFilePool.getRandomAccessFile(fileId, "rw");
FileAccessor.writeSection(raf,sectionHander.getOffset(),context);
UnreceiveSection unreceiveSection = unreceivePool.getUnreceiveSection(fileId);
boolean isAllreceive = unreceiveSection.receiveSection(sectionHander);
if (isAllreceive) {
this.accessFilePool.closeRaf(fileId);
}
}
}
}
4.接收端服务器
- 接收端服务器对资源进行分割多个部分,生成未接收片段池
- 设置资源存放路径
- 根据资源信息,生成相应文件目录
- 指定发送端个数。
public ReceiveServer(int senderCount,String absoluteRoot,
ResourceInfo resourceInfo) {
this.port = DEFAULT_PORT;
this.senderCount = senderCount;
this.accessFilePool = new RandomAccessFilePool(resourceInfo);
this.unreceivePool = newUnreceivePool(resourceInfo);
resourceInfo.createDirctories(absoluteRoot);
}
/**
* 接收方根据文件列表,准备接收文件。
* @param resourceInfo
* @return
*/
private static UnreceivePool newUnreceivePool(ResourceInfo resourceInfo) {
UnreceivePool unreceivePool = new UnreceivePool();
Iterator<FileInfo> fileInfoAccessor = resourceInfo.fileInfoAccessor();
while (fileInfoAccessor.hasNext()) {
FileInfo next = fileInfoAccessor.next();
unreceivePool.addTargetFile(next.getId(),next.getSize());
}
return unreceivePool;
}
- 开启服务器
public void startup() {
// if (this.goon) {
// return;
// }
try {
serverSocket = new ServerSocket(port);
new Thread(this).start();
} catch (IOException e) {
e.printStackTrace();
}
}
- 打开接收端线程
@Override
public void run() {
int linkedSenderCount = 0;
//TODO 这里没有考虑到某一个发送端,在发送前连接时,
//就已经崩溃了。
//处理方法提示,进行超时处理即可。
while (linkedSenderCount < this.senderCount) {
try {
Socket socket = serverSocket.accept();
Receiver receiver = new Receiver(accessFilePool, unreceivePool, socket);
receiver.start();
linkedSenderCount++;
} catch (IOException e) {
// this.goon = false;
}
}
}
- 关闭通信
public void shutdown() {
if(!goon) {
return;
}
close();
}
private void close() {
if (serverSocket != null && !serverSocket.isClosed()) {
try {
serverSocket.close();
} catch (IOException e) {
e.printStackTrace();
} finally {
serverSocket=null;
}
}
goon = false;
}
3.总结
该篇仅针对接收文件接收。已指派的发送端,用线程相应接收。接收端根据资源信息生成接收列表,根据发送端个数,开辟接收线程。每接收一片内容,都会在未接收片段池有一段记录,这样保证了传输可靠性。但是建立接收服务器前提是:需要知道接收端地址,与接收端协商要发送的资源 。