您的位置:首页 > 移动开发 > Android开发

android udp文件断点续传

2016-07-07 11:12 288 查看

前端时间,项目需要文件断点续传,需要抛弃原来的UDT文件传输。改用UDP传输。

因为UDP传送数据较TCP快速,系统开销也少,所以选择UDP,但UDP不对收到的数据进行排序,在UDP报文的首部中并没有关于数据顺序的信息(如TCP所采用的序号),而且报文不一定按顺序到达的,所以接收端无从排起。UDP对接收到的数据报也不发送确认信号,发送端不知道数据是否被正确接收,也不会重发数据,所以我们要自己在制定一套协议。废话不多说上代码。

/**
* 断点发送文件
* @param filepath 文件路径
* @param startindex 文件已经发送大小
* @param videoArrayList 需要发送的文件列表
* @throws JSONException json异常
* @throws IOException io异常
*/
public void sendFile(String filepath, long startindex,
ArrayList<VideoFilePath> videoArrayList) throws JSONException,
IOException {
long fileAccess = 0;
if (client != null && !client.isClosed()) {
client.close();
client = null;
}
client = new DatagramSocket();
client.setSoTimeout(3 * 1000);// 设置超时时间为3s
try {//首先和接收方握手
String sendStr = "C";
byte[] sendBuf;
sendBuf = sendStr.getBytes();
InetAddress addr = InetAddress.getByName(Custom.getMainLoginIp().getIP());
DatagramPacket sendPacket = new DatagramPacket(sendBuf,sendBuf.length, addr, Util.UDP_PORT);
client.send(sendPacket);
Log.e("UDP", "UDP Control--> sendFile 发送C");
byte[] recvBuf = new byte[1];
DatagramPacket recvPacket = new DatagramPacket(recvBuf,recvBuf.length);
client.receive(recvPacket);
String recvStr = new String(recvPacket.getData(), 0,recvPacket.getLength());
Log.e("UDP", "UDP Control--> sendFile 接受响应" + recvStr);
while (!"S".equals(recvStr)) {
client.receive(recvPacket);
recvStr = new String(recvPacket.getData(), 0,recvPacket.getLength());
}
VideoFilePath zipFilePath = new VideoFilePath();// 把zip文件加入列表一起处理。
zipFilePath.setVideoPath(filepath);//文件路径
zipFilePath.setVideoAccess(startindex);//设置已经发送大小
File zipFile = new File(filepath);
if (!zipFile.exists()) {//判断文件是否存在
throw new FileNotFoundException();
}
zipFilePath.setVideoLength(zipFile.length());//获取文件总长度
videoArrayList.add(zipFilePath);

for (int v = 0; v < videoArrayList.size(); v++) {//循环发送多个文件
VideoFilePath videoFilePath = videoArrayList.get(v);
File file = new File(videoFilePath.getVideoPath());
if (!file.exists()) {
throw new FileNotFoundException();
}
RandomAccessFile access = new RandomAccessFile(file, "r");
// 缓冲区5KB
sendBuf = new byte[1024 * 5];
// 剩余要读取的长度
long tatol = videoFilePath.getVideoLength() - videoFilePath.getVideoAccess();
access.skipBytes((int) videoFilePath.getVideoAccess());
Log.e("UDP", "UDP Control--> 开始发送文件");
// 本次要读取的长度假设为剩余长度
int len = (int) (videoFilePath.getVideoLength() - videoFilePath.getVideoAccess());
// 如果本次要读取的长度大于缓冲区的容量
if (len > sendBuf.length) {
// 修改本次要读取的长度为缓冲区的容量
len = sendBuf.length;
}
fileAccess += videoFilePath.getVideoAccess();//已经发送文件长度(用于显示进度条)

while (tatol > 0 && isSendFile) {
int sendAccess = 0;
for (int i = 0; i < 20; i++) {
if (tatol < len) {
sendBuf = new byte[(int) tatol];

4000
len = (int) tatol;
}
// 读取文件,返回真正读取的长度
int rlength = access.read(sendBuf, 0, len);
// 将剩余要读取的长度减去本次已经读取的
tatol -= rlength;
sendAccess += rlength;
// 如果本次读取个数不为0则写入输出流,否则结束
if (rlength > 0) {
byte[] sendIndexBuf = new byte[rlength + 4];
System.arraycopy(sendBuf, 0, sendIndexBuf, 4,rlength);//把读取的文件包写入新的byte数组
Util.int2Byte(i, sendIndexBuf, 0);//给新的数组写入文件包前标 0-20
// 将本次读取的写入输出流中
sendPacket = new DatagramPacket(sendIndexBuf,sendIndexBuf.length, addr, Util.UDP_PORT);
client.send(sendPacket);
}
if (tatol <= 0) {//本个文件发送完毕
break;
}
}
recvBuf = new byte[1];
recvPacket = new DatagramPacket(recvBuf, recvBuf.length);
client.receive(recvPacket);//接受回执,是否20个包接受成功
soTimeIndex = 0;// 初始化超时次数
// 发送已经发送文件进度
fileAccess += sendAccess;
Message startMsg = new Message();
startMsg.arg1 = Const.UP_PROGRESS;
startMsg.obj = fileAccess;
if (tatol <= 0 && v == videoArrayList.size() - 1) {// 最后一个文件发送完毕
startMsg.arg1 = Const.UP_UDP_SUCCESS;
}
handler.sendMessage(startMsg);
}
access.close();
}
// 如果剩余长度小于等于0则结束
client.close();
} catch (SocketTimeoutException e) {
//如果连续5次链接超时,则认定链接失败。
if (isSendFile) {
Message msg = handler.obtainMessage();
soTimeIndex++;
if (soTimeIndex >= 5) {
soTimeIndex = 0;
msg.arg1 = Const.UP_SCREEN_CLOSE;
} else {
msg.arg1 = Const.UP_UDP_CLOSE;
}
handler.sendMessage(msg);
client.close();
}
}
}

/**
* int转byte数组
*
* @param a
* @param b
* @param offset
* @return
*/
public static void int2Byte(int a, byte[] b, int offset) {
b[offset++] = (byte) (a >> 24);
b[offset++] = (byte) (a >> 16);
b[offset++] = (byte) (a >> 8);
b[offset++] = (byte) (a);
}

/**
* 断点接受文件
* @param filepath 文件路径
* @param startindex 文件已接受大小
* @param totallen 文件总长度
* @param videoArrayList 还需接受文件列表
* @return 是否接受成功
* @throws Exception
*/
@SuppressWarnings("resource")
private boolean recevfileContinue(String filepath, long startindex,long totallen, ArrayList<VideoFilePath> videoArrayList)throws Exception {
ArrayList<VideoFilePath> fileList = new ArrayList<VideoFilePath>();
fileList.addAll(videoArrayList);
ArrayList<byte[]> recevList = new ArrayList<byte[]>();
boolean isSucc = true;
Log.e("UDP", "recevFile---->startindex:" + startindex);
Log.e("UDP", "recevFile---->totallen:" + totallen);
// 清除上一次链接
if (datagramServer != null && !datagramServer.isClosed()) {
datagramServer.close();
datagramServer = null;
}
Log.e("UDP", "接受文件 datagramServer.close");
byte[] sendbufs = new byte[1];
datagramServer = new DatagramSocket(null);
datagramServer.setReuseAddress(true);
datagramServer.bind(new InetSocketAddress(Util.UDP_PORT));
datagramServer.setSoTimeout(2500);// 设置超时时间为20s
Log.e("UDP", "接受文件 setSoTimeout");
byte[] recvBuf = new byte[1];
Log.e("UDP", "接受文件 recvBuf");
DatagramPacket recvPacket = new DatagramPacket(recvBuf, recvBuf.length);
Log.e("UDP", "接受文件 recvPacket");
datagramServer.receive(recvPacket);// 接受udp链接成功校验
Log.e("UDP", "接受文件 receive");
String recvStr = new String(recvPacket.getData(), 0,
recvPacket.getLength());// 接受的数据转换为字符串
Log.e("UDP", "接受文件 recvStr" + recvStr);
while (!"C".equals(recvStr)) {// 是否为规定协议,不是,继续接受
datagramServer.receive(recvPacket);
recvStr = new String(recvPacket.getData(), 0,
recvPacket.getLength());
}
int port = recvPacket.getPort();// 获取链接端口
InetAddress addr = recvPacket.getAddress();// 获取链接ip地址
Log.e("UDP", "接受文件 port" + port + "   addr" + addr.toString());
String sendStr = "S";// 发送链接回应
byte[] sendBuf;
sendBuf = sendStr.getBytes();
DatagramPacket sendPacket = new DatagramPacket(sendBuf, sendBuf.length,
addr, port);
datagramServer.send(sendPacket);// 发送响应

VideoFilePath videoFilePath = new VideoFilePath();
videoFilePath.setVideoPath(filepath);//文件路径
videoFilePath.setVideoAccess(startindex);//文件已经发送大小
videoFilePath.setVideoLength(totallen);//文件总长度
fileList.add(videoFilePath);//把文件加到接受列表中
for (int v = 0; v < fileList.size(); v++) {
VideoFilePath videoFile = fileList.get(v);
String[] str = videoFile.getVideoPath().split("/");
String videoPath = FileHelper.TEMP_DIR + "//" + str[str.length - 1];
File file = new File(videoPath);
long tatol = -1;
RandomAccessFile access = null;
access = new RandomAccessFile(file, "rw");
access.skipBytes((int) videoFile.getVideoAccess());
tatol = videoFile.getVideoLength() - videoFile.getVideoAccess();

Log.e("UDP", "接受文件 while");
while (tatol > 0) {
// 防止下面接受循环时 数组越界
for (int arraySize = 0; arraySize < 20; arraySize++) {
recevList.add(arraySize, new byte[0]);
}
for (int i = 0; i < 20; i++) {
if (tatol <= 0) {
break;
}
recvBuf = new byte[(1024 * 5) + 4];//加上前标长度
recvPacket = new DatagramPacket(recvBuf, recvBuf.length);
try {
datagramServer.receive(recvPacket);
} catch (SocketTimeoutException e) {
Log.e("UDP", "接受文件时链接超时 e" + e.getLocalizedMessage());
if (datagramServer != null&& !datagramServer.isClosed()) {
datagramServer.close();
datagramServer = null;
}
return false;
}
Log.e("UDP", "接受文件 arraycopy file");
int reallen = recvPacket.getLength() - 4;//实际文件长度
Log.d("UDP", "接受文件 reallen == " + reallen);
if (reallen <= 0) {
return false;
}
tatol -= reallen;
byte[] recvBuffs = new byte[reallen];
Log.e("UDP", "接受文件 recvPacket.getData().length"
+ recvPacket.getData().length);
System.arraycopy(recvPacket.getData(), 4, recvBuffs, 0,
reallen);
int index = Util.getInt(recvPacket.getData(), 0);//获取文件包前标
recevList.add(index, recvBuffs);//加入接受缓存数组等接受完毕20个一起写入
Log.e("UDP", "index   ;  " + index);
}
sendbufs[0] = (byte) 0x06;
sendPacket = new DatagramPacket(sendbufs, sendbufs.length,addr, port);
Log.e("UDP", "接受文件 send data");
datagramServer.send(sendPacket);// 接受20个包成功,发送成功回执
for (int re = 0; re < recevList.size(); re++) {//循环写入文件
byte[] buffs = recevList.get(re);
Log.e("UDP", "接受文件 write file");
access.write(buffs, 0, buffs.length);
}
recevList.clear();//清理缓存数组
Log.d("recevfileContinue", "tatol == " + tatol);
}
access.close();
}
datagramServer.close();
Log.d("recevfileContinue", "isSucc == " + isSucc);
return isSucc;
}

/**
* 判断是否为断点续传
*
* @param fileName
*            文件名字
* @param md5
*            文件md5
* @return 文件已经存在长度
*/
private long sendFileContinue(String fileName, String md5,
ArrayList<VideoFilePath> videoArray) {
// 判断文件是否存在
String filepath = FileHelper.TEMP_DIR + "//" + fileName;
String textFilePath = FileHelper.TEMP_DIR + "//fileText.xml";
File textFile = new File(textFilePath);
File file = new File(filepath);
try {
for (int v = 0; v < videoArray.size(); v++) {// 查询已经接受视频的大小
VideoFilePath videoFilePath = videoArray.get(v);
String[] str = videoFilePath.getVideoPath().split("/");
String videoPath = FileHelper.TEMP_DIR + "//"
+ str[str.length - 1];
File videoFile = new File(videoPath);
if (videoFile.exists()) {
videoFilePath.setVideoAccess(videoFile.length());
} else {
videoFilePath.setVideoAccess(0);
}
}
if (!textFile.exists()) {// 管理文件是否存在
textFile.createNewFile();
}
String textStr = FileHelper.readFile(textFilePath);
ArrayList<SendFileText> sendFileTextArrayList = Json
.analysisFileText(textStr);
if (file.exists()) {// 如果存在,读取文件管理文本,查看md5是否一致
for (int i = 0; i < sendFileTextArrayList.size(); i++) {
SendFileText sendFileText = sendFileTextArrayList.get(i);
if (fileName.equals(sendFileText.getFileName())) {
if (md5.equals(sendFileText.getFileMD5())) {
return FileHelper.fileSize(filepath);
} else {
sendFileTextArrayList.remove(sendFileText);
break;
}
}
}
FileHelper.deleteFile(filepath);
SendFileText addSendFileText = new SendFileText();
addSendFileText.setFileMD5(md5);
addSendFileText.setFileName(fileName);
sendFileTextArrayList.add(addSendFileText);
FileHelper.writeFile(textFilePath,
Json.structureFileText(sendFileTextArrayList));
return 0;
} else {// 如果不存在,把文件管理文本内相同文件名字的字段删除,并且写入新的文件字段,return 0.
for (int i = 0; i < sendFileTextArrayList.size(); i++) {
SendFileText sendFileText = sendFileTextArrayList.get(i);
if (fileName.equals(sendFileText.getFileName())
|| md5.equals(sendFileText.getFileMD5())) {
sendFileTextArrayList.remove(sendFileText);
break;
}
}
SendFileText addSendFileText = new SendFileText();
addSendFileText.setFileMD5(md5);
addSendFileText.setFileName(fileName);
sendFileTextArrayList.add(addSendFileText);
FileHelper.writeFile(textFilePath,
Json.structureFileText(sendFileTextArrayList));
return 0;
}
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
return 0;
}
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  android udp 文件传输