近期在做一个通过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连接一次传输多个文件的更多相关文章

  1. 对于.NET Socket连接的细节记录

    如果客户端直接连接一个不存在的服务器端,客户端会抛出异常: 如果在连接过程中,客户端强制关闭了连接(没有调用Close直接关闭了程序),服务器端会抛出异常: 如果在连接过程中,客户端调用了Close, ...

  2. 比较 http连接 vs socket连接

    http连接 :短连接,客户端,服务器三次握手建立连接,服务器响应返回信息,连接关闭,一次性的socket连接:长连接,客户端,服务器三次握手建立连接不中断(通过ip地址端口号定位进程)及时通讯,客户 ...

  3. UrlConnection连接和Socket连接的区别

    关于UrlConnection连接和Socket连接的区别,只知道其中的原理如下: 抽象一点的说,Socket只是一个供上层调用的抽象接口,隐躲了传输层协议的细节. urlconnection 基于H ...

  4. 转 Cocos网络篇[3.2](3) ——Socket连接(1)

    Cocos网络篇[3.2](3) ——Socket连接(1) 2015-03-05 22:24:13 标签:network http socket cocos [唠叨] 在客户端游戏开发中,使用HTT ...

  5. Http和Socket连接区别

    相信不少初学手机联网开发的朋友都想知道Http与Socket连接究竟有什么区别,希望通过自己的浅显理解能对初学者有所帮助. 1.TCP连接 要想明白Socket连接,先要明白TCP连接.手机能够使用联 ...

  6. Http、tcp、Socket连接区别

    转自Http.tcp.Socket连接区别 相信不少初学手机联网开发的朋友都想知道Http与Socket连接究竟有什么区别,希望通过自己的浅显理解能对初学者有所帮助. 1.TCP连接 要想明白Sock ...

  7. Socket连接与HTTP连接

    我们在传输数据时,可以只使用(传输层)TCP/IP协议,但是那样的话,如果没有应用层,便无法识别数据内容,如果想要使传输的数据有意义,则必须使用到应用层协议,应用层协议有很多,比如HTTP.FTP.T ...

  8. Http和Socket连接的区别

    Http和Socket连接区别 相信不少初学手机联网开发的朋友都想知道Http与Socket连接究竟有什么区别,希望通过自己的浅显理解能对初学者有所帮助. 1.TCP连接 要想明白Socket连接,先 ...

  9. Http和Socket连接

    转自http://hi.baidu.com/%D2%B9%D1%A9%B3%E6/blog/item/d6a72d2bbf467cf2e7cd406d.html 相信不少初学手机联网开发的朋友都想知道 ...

随机推荐

  1. Java高级架构师(一)第39节:Nginx的Rewrite模块

  2. .Net 有关程序集查找与加载的一点反思

    最近在做一款叫VICA产品,此产品采用了插件机制,插件在运行中加载,插件与插件之间存在依赖关系,所有的插件DLL为方便管理都放置在Plugins的文件夹下统一管理.这种处理方式不自觉的就让我想了解cl ...

  3. 拍拍CPS入门使用

    1.新建应用,获取应用相应的appOAuthID.appOAuthKey.accessToken(这个一点击获取就会改变的,而且最长有效期为3个月,失效了需要重新获取) http://fuwu.pai ...

  4. git log --oneline --graph的读法

    星号表明这个提交所在的分支: 最左边的直线表示当前分支的历史状态,从图看,当前分支HEAD是master分支 :提交历史是:8cfbb25<--d486463<--a88c595<- ...

  5. 创建maven web项目无法创建sec目录

    创建maven web项目无法创建sec目录 解决方法:-DarchetypeCatalog=internal

  6. Java集合类理解

    深入Java集合学习系列:http://zhangshixi.iteye.com/blog/674856 http://blog.csdn.net/shf4715/article/details/47 ...

  7. [转]使用VS2010的Database 项目模板统一管理数据库对象

    本文转自:http://www.cnblogs.com/shanyou/archive/2010/05/08/1730810.html Visual Studio 2010 有一个数据库项目模板:Vi ...

  8. phpstorm不安装apache就可以本地测试PHP

    最近再搞个PHP的项目,找了很多发现phpstorm这个非常小巧而且很好用,,顺便推荐一下idea开发android非常不错,这2个IDE都是一家公司的.本文由智动软件(zdexe.com)原创,转载 ...

  9. 安装pip源

    国内源地址: 豆瓣(douban) http://pypi.douban.com/simple/ 阿里云 http://mirrors.aliyun.com/pypi/simple/ 中国科技大学 h ...

  10. Mybatis中动态SQL多条件查询

    Mybatis中动态SQL多条件查询 mybatis中用于实现动态SQL的元素有: if:用if实现条件的选择,用于定义where的字句的条件. choose(when otherwise)相当于Ja ...