通过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 相信不少初学手机联网开发的朋友都想知道 ...
随机推荐
- css排版之-标准文档流
标准流指的是在不使用其他的与排列和定位相关的特殊CSS规则时,各种元素的排列规则.HTML文档中的元素可以分为两大类:行内元素和块级元素. 1.行内元素不占据单独的空间,依附于块级元素,行 ...
- Mac下的MySQL修改默认连接字符集
进入命令行执行以下命令: sudo vim /etc/my.cnf [client] default-character-set=utf8 [mysqld] character-set-server= ...
- 为Go语言GC正名-20秒到100微妙的演变史
http://blog.csdn.net/erlib/article/details/51850912 https://blog.twitch.tv/gos-march-to-low-latency- ...
- 【Tomcat MyEcplise】MyEcplise添加一个新的Server,Tomcat8.5报错
添加新的Tomcat添加不进去,报错: The Apache Tomcat installation at this directory is version 8.5.11. A Tomcat 8. ...
- 二十四种设计模式:备忘录模式(Memento Pattern)
备忘录模式(Memento Pattern) 介绍在不破坏封装性的前提下,捕获一个对象的内部状态,并在该对象之外保存这个状态.这样以后就可将该对象恢复到保存的状态. 示例有一个Message实体类,某 ...
- scikit-learn入门学习记录
一加载示例数据集 from sklearn import datasets iris = datasets.load_iris() digits = datasets.load_digits() 数据 ...
- eclipse package explorer视图中怎么让default package不显示?
如下图所示:
- Chrome DevTools快捷键
- java源码阅读Observable(观察者模式)
1类签名和简介 package java.util; public class Observable Observable是Java内置的观察者模式中的主题类(没错,是类不是接口),和其对应的观察者接 ...
- JS-产生随机数的几个用法!
<script> function GetRandomNum(Min,Max) { var Range = Max - Min; var Rand = Math.random(); ret ...