通过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 相信不少初学手机联网开发的朋友都想知道 ... 
随机推荐
- 【常见加密方法】Base64编码&Tea算法简介
			Base64编码 [Base64编码是什么] Base64是一种基于64个可打印字符来表示二进制数据的表示方法. ——维基百科 Base64,顾名思义,是基于64种可视字符的编码方式.这64种符号由A ... 
- ArrayList源码及解析
			package java.util; import java.util.function.Consumer; import java.util.function.Predicate; import j ... 
- Codeforces Round #343 (Div. 2) E. Famil Door and Roads lca 树形dp
			E. Famil Door and Roads 题目连接: http://www.codeforces.com/contest/629/problem/E Description Famil Door ... 
- mvn在线仓库搜索
			使用Maven进行开发的时候,比较常见的一个问题就是如何寻找我要的依赖,比如说,我想要使用activeMQ,可是我不知道groupId,artifactId,和合适的version.怎么办呢?本文介绍 ... 
- bootstrap之双日历时间段选择控件示例—daterangepicker(中文汉化版)
			效果图: 参考代码: <link href="/css/daterangepicker.min.css?ver=0.6" rel="stylesheet" ... 
- 使用Javascript实现ajax示例
			使用原始的javascript实现ajax <!DOCTYPE html> <html xmlns="http://www.w3.org/1999/xhtml"& ... 
- Java_导出Excel
			导出的Excel标题.Sheet名称.数据内容都可以使用中文 一.pom.xml引入jar包 1 2 3 4 5 <dependency> <groupId ... 
- MFC进度条刷新处理
			m_p.SetRange(0,1000); m_p.SetStep(1); for (int i=0;i<1001;i++) { m_p.SetPos(i); Sleep(10); MSG ms ... 
- Efficient GPU Screen-Space Ray Tracing
			http://jcgt.org/published/0003/04/04/paper.pdf 一个号称只有2ms的实时gpu光线追踪 screen space reflection用到了 和其他ray ... 
- enter 键登录的实现
			js 代码 document.onkeypress = function() { var iKeyCode = -1; if (arguments[0]) { iKeyCode = arguments ... 
