通过Socket连接一次传输多个文件
近期在做一个通过WIFI在手机之间传输文件的功能。须要在手机之间建立一个持久的Socket
连接并利用该连接数据传输。能够一次传输一个或多个文件。
在一次传输多个文件时,遇到了一个困难:怎样在接收文件时确定文件之间的边界。
为了在接收端正确的拆分文件,在传输文件时须要传输每一个文件的大小。
我採用了这样一种策略:首先发送每一个文件的名称和大小。然后传输文件的内容。
protected void sendFile(Socket socket, File[] files) {
long totalSize = 0;
byte buf[] = new byte[8192];
int len;
try {
if (socket.isOutputShutdown()) {
return;
}
DataOutputStream dout = new DataOutputStream(
socket.getOutputStream());
dout.writeInt(files.length);
for (int i = 0; i < files.length; i++) {
dout.writeUTF(files[i].getName());
dout.flush();
dout.writeLong(files[i].length());
dout.flush();
totalSize += files[i].length();
}
dout.writeLong(totalSize); for (int i = 0; i < files.length; i++) {
BufferedInputStream din = new BufferedInputStream(
new FileInputStream(files[i]));
while ((len = din.read(buf)) != -1) {
dout.write(buf, 0, len);
}
}
System.out.println("文件传输完毕"); } catch (Exception e) {
e.printStackTrace();
Log.d(TAG,"send file exception");
}
return;
}
接收文件时有些复杂。每次从输入流中读入缓存中的数据有可能包括多个文件的内容,
须要利用每一个文件的大小信息把缓存中的数据放入不同的文件。
protected void receiveFile(Socket socket) {
File dirs = new File(mFilePath);
if (!dirs.exists()) {
dirs.mkdirs();
}
DataInputStream din = null;
int fileNum = 0;
long totalSize = 0;
FileInfo[] fileinfos = null;
try {
din = new DataInputStream(new BufferedInputStream(
socket.getInputStream()));
fileNum = din.readInt();
fileinfos = new FileInfo[fileNum];
for (int i = 0; i < fileNum; i++) {
fileinfos[i] = new FileInfo();
fileinfos[i].mFileName = din.readUTF();
fileinfos[i].mFileSize = din.readLong();
}
totalSize = din.readLong();
} catch (IOException e) {
e.printStackTrace();
Log.d(TAG,"readInt Exception");
System.exit(0);
}
System.out.println(fileNum);
System.out.println(totalSize);
for (FileInfo fileinfo : fileinfos) {
System.out.println(fileinfo.mFileName);
System.out.println(fileinfo.mFileSize);
}
// // /////////////////////////////////////////////////////////////////
int leftLen = 0; // 写满文件后缓存区中剩余的字节长度。 int bufferedLen = 0; // 当前缓冲区中的字节数
int writeLen = 0; // 每次向文件里写入的字节数
long writeLens = 0; // 当前已经向单个文件里写入的字节总数
long totalWriteLens = 0; // 写入的所有字节数
byte buf[] = new byte[8192];
for (int i = 0; i < fileNum; i++) {
writeLens = 0;
try {
FileOutputStream fout = new FileOutputStream(mFilePath
+ fileinfos[i].mFileName);
while (true) {
if (leftLen > 0) {
bufferedLen = leftLen;
} else {
bufferedLen = din.read(buf);
}
if (bufferedLen == -1)
return;
System.out.println("readlen" + bufferedLen);
// 假设已写入文件的字节数加上缓存区中的字节数已大于文件的大小,仅仅写入缓存区的部分内容。
if (writeLens + bufferedLen >= fileinfos[i].mFileSize) {
leftLen = (int) (writeLens + bufferedLen - fileinfos[i].mFileSize);
writeLen = bufferedLen - leftLen;
fout.write(buf, 0, writeLen); // 写入部分
totalWriteLens += writeLen;
move(buf, writeLen, leftLen);
break;
} else {
fout.write(buf, 0, bufferedLen); // 所有写入
writeLens += bufferedLen;
totalWriteLens += bufferedLen;
if (totalWriteLens >= totalSize) {
//mListener.report(GroupChatActivity.FAIL, null);
return;
}
leftLen = 0;
}
//mListener.report(GroupChatActivity.PROGRESS,
//(int) (totalWriteLens * 100 / totalSize));
} // end while
fout.close(); } catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
Log.d(TAG,"receive file Exception");
}
} // end for
//mListener.report(GroupChatActivity.FAIL, null);
}
注:在传输文件时还传输了文件的总大小,这样为了在接收文件时判定接收是否结束。
另一种传输方法比較复杂但更加灵活发送文件时依次传输每一个文件的名称。大小和内容。
相比上一个方法这样的发送方式接受时更难处理。
由于每次从输入流中读入缓存的数据可能包括了上一个文件的内容。下一个文件的名称和大小。
因为数据已被读入了缓存,这就不能利用DataInputStream的方法读取UTF字符串和Int,
必须从缓存中解析。
介绍两种解析方法
利用ByteArrayInputStream 把缓存中的内容转化为内存流然后利用DataInputStream读取。
手动解析,利用位运算拼接出Int。
注:Int 的长度为4是确定的。
WriteUTF 写入的字串长度存储在開始的两个字节中。
通过Socket连接一次传输多个文件的更多相关文章
- 对于.NET Socket连接的细节记录
如果客户端直接连接一个不存在的服务器端,客户端会抛出异常: 如果在连接过程中,客户端强制关闭了连接(没有调用Close直接关闭了程序),服务器端会抛出异常: 如果在连接过程中,客户端调用了Close, ...
- 比较 http连接 vs socket连接
http连接 :短连接,客户端,服务器三次握手建立连接,服务器响应返回信息,连接关闭,一次性的socket连接:长连接,客户端,服务器三次握手建立连接不中断(通过ip地址端口号定位进程)及时通讯,客户 ...
- UrlConnection连接和Socket连接的区别
关于UrlConnection连接和Socket连接的区别,只知道其中的原理如下: 抽象一点的说,Socket只是一个供上层调用的抽象接口,隐躲了传输层协议的细节. urlconnection 基于H ...
- 转 Cocos网络篇[3.2](3) ——Socket连接(1)
Cocos网络篇[3.2](3) ——Socket连接(1) 2015-03-05 22:24:13 标签:network http socket cocos [唠叨] 在客户端游戏开发中,使用HTT ...
- Http和Socket连接区别
相信不少初学手机联网开发的朋友都想知道Http与Socket连接究竟有什么区别,希望通过自己的浅显理解能对初学者有所帮助. 1.TCP连接 要想明白Socket连接,先要明白TCP连接.手机能够使用联 ...
- Http、tcp、Socket连接区别
转自Http.tcp.Socket连接区别 相信不少初学手机联网开发的朋友都想知道Http与Socket连接究竟有什么区别,希望通过自己的浅显理解能对初学者有所帮助. 1.TCP连接 要想明白Sock ...
- Socket连接与HTTP连接
我们在传输数据时,可以只使用(传输层)TCP/IP协议,但是那样的话,如果没有应用层,便无法识别数据内容,如果想要使传输的数据有意义,则必须使用到应用层协议,应用层协议有很多,比如HTTP.FTP.T ...
- Http和Socket连接的区别
Http和Socket连接区别 相信不少初学手机联网开发的朋友都想知道Http与Socket连接究竟有什么区别,希望通过自己的浅显理解能对初学者有所帮助. 1.TCP连接 要想明白Socket连接,先 ...
- Http和Socket连接
转自http://hi.baidu.com/%D2%B9%D1%A9%B3%E6/blog/item/d6a72d2bbf467cf2e7cd406d.html 相信不少初学手机联网开发的朋友都想知道 ...
随机推荐
- 随机数选择器 Exercise07_13
import java.util.Scanner; /** * @author 冰樱梦 *时间:2018年下半年 *题目:随机数选择器 */ public class Exercise07_13 { ...
- HDU 4662 MU Puzzle (2013多校6 1008 水题)
MU Puzzle Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 32768/32768 K (Java/Others)Total S ...
- Inno Setup入门(二十四)——Inno Setup类参考(10)
这里介绍一下FolderTreeView 类. TFolderTreeView = class(TCustomFolderTreeView) property OnChange: TNotifyE ...
- fmri 实验设计 / 范式设计/ paradigm design
reference:http://www.psychology.gatech.edu/cabi/Resources/Course/index.html sluggish 懒散的,无精打采的.哈哈,pp ...
- 关于Android架构那些事
刚开始,因为业务比较赶,我们也没有进行比较好的顶层设计,对代码的要求也是最低要求——完成功能开发就行了.这种短期设计也就造成了我们代码的扩展性几乎为零,稍微添加一点新功能,都要大动干戈.在后台系统架构 ...
- Delphi 对象模型学习笔记(转)
摘要 Borland Object Pascal 对象模型(现在已经正是命名为 Delphi 语言)与其他 OOP 语言一样,都提供了一些基础服务: 如对象创建服务.对象释放服务.对象识别服务 ...
- RMAN冗余备份概念与方法
冗余备份概念 RMAN提供了一种更谨慎的备份策略:duplexed方式备份,其实质就是在生成备份集的同时,向指定位置copy指定份数(最大不超过4)的备份集复制,以避免在灾难性事故时数据库损坏和备份丢 ...
- 如何在Centos7上安装zookeeper 多实例
一.如何在Centos7上安装zookeeper 多实例 cd /usr/local/src/ wget https://mirrors.tuna.tsinghua.edu.cn/apache/zoo ...
- TensorFlow------读取CSV文件实例
TensorFlow之读取CSV文件实例: import tensorflow as tf import os def csvread(filelist): ''' 读取CSV文件 :param fi ...
- PHP的两个科学计数法转换为字符串的方法
不常用,所以整理在这里,分享给同行使用 方法一:取尾数法 public function NumToStr($num) { if (stripos($num, 'e') === false) retu ...