⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 filedatapacket.java

📁 用java实现的
💻 JAVA
📖 第 1 页 / 共 2 页
字号:
/*
* LumaQQ - Java QQ Client
*
* Copyright (C) 2004 luma <stubma@163.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
package edu.tsinghua.lumaqq.qq.filetrans;

import java.nio.ByteBuffer;

import edu.tsinghua.lumaqq.qq.PacketParseException;
import edu.tsinghua.lumaqq.qq.QQ;
import edu.tsinghua.lumaqq.qq.Utils;

/**
 * <pre>
 * 文件操作信息包,这个包里面携带了文件的一些基本信息,格式为
 * 1. 头部
 * 2. 未知字节,0x00
 * 3. 2字节操作类型,后续格式为
 *    1. 操作类型为0x1时,可能是表示开始传送文件基本信息,格式为
 *       i.  三个未知字节,全0
 *    2. 操作类型为0x2时,可能是表示开始传送文件的数据,格式和0x1时相同
 *    3. 操作类型为0x3时,可能时表示传送结束,格式和0x1时相同
 *    4. 如果操作类型为0x7,表示文件相关操作,根据子操作类型内容有所不同,格式为
 *       a. 当子操作类型为0x1时,表示文件基本信息传输,格式为
 * 	  	 	i.   两个字节,为全0,作用未知
 * 	     	ii.  子操作类型字节0x1
 *       	iii. 文件长度,四字节
 *       	iv.  文件数据分片数,一次传送1000字节,所以这个域是根据iii计算出来的,4字节
 *       	v.   每个分片的最大长度,4字节,一般都是1000字节一个分片,只有最后一个不是最大字节数
 *       	vi.  16个字节的md5,对于较小的文件,这个就是全部文件的md5,对于超过10002432字节的
 *               文件,这个MD5是前10002432字节的MD5。太变态了,为什么选这么一个没规律的数字。找
 *               的我头都晕了。
 *          vii. 文件名的MD5,16字节
 *       	viii. 文件名字节长度,2字节
 *       	ix.  八个未知字节,全0
 *       	x.   文件名
 *       b. 如果子操作类型为0x2,表示文件数据传输,格式为
 *       	i.   消息包的顺序号
 *       	ii.  子操作类型字节0x2
 *       	iii. 分片序号,4字节
 *       	iv.  当前分片在文件中的绝对偏移, 4字节
 *       	v.   当前包含的数据字节数,2字节
 *       	vi.  实际的数据
 *       c. 如果子操作类型为0x3,表示文件传送结束,格式为
 *       	i.   最大分片序号加1,例如如果文件分成两个分片发送,那么这里就是3,2字节
 *       	ii.  子操作类型0x3
 *    5. 操作类型为0x8时,为0x7操作的回复包,根据子操作类型不同而不同,格式为
 * 	  	 a. 如果子操作类型为0x1,表示确认文件基本信息已经收到,格式为
 *    	 	i.   未知两个字节,0x0000
 *    	 	ii.  子操作类型字段,0x1,1字节
 *    	 	iii. 未知的四字节,全0
 *    	 b. 如果子操作类型为0x2,表示已经收到了文件分片,格式为
 *    	 	i.   包顺序号
 *    	 	ii.  子操作类型字段,0x2,1字节
 *    	 	iii. 分片序号,4字节
 *    	 c. 如果子操作类型是0x3,表示已经收到了结束信息,格式为
 *    	 	i.   最大的分片序号再加1,2字节
 *    	 	ii.  子操作类型字段,0x3,1字节
 * 
 * 注意:这种包都不是加密的
 * </pre>
 * 
 * @author 马若劼
 */
public class FileDataPacket extends FilePacket {
    // 命令
    private char command;
    // 包携带的信息类型
	private byte infoType;
	// 文件基本信息字段
	private int fileSize;
	private int fragments;
	private String fileName;
	// 文件数据信息字段
	private int packetIndex;
	private int fragmentIndex;
	private int fragmentOffset;
	private int fragmentLength;
	private int fragmentMaxSize;
	private byte[] fileMD5;
	private byte[] fileNameMD5;
	private byte[] fragmentData;
	// heart beat 字段
	private char heartBeatSequence;
	
	/**
	 * 构造一个发送包
     * @param watcher FileWatcher对象
	 */
	public FileDataPacket(FileWatcher watcher) {
		super(watcher);
	}

	/* (non-Javadoc)
	 * @see edu.tsinghua.lumaqq.qq.filetrans.FilePacket#initContent(java.nio.ByteBuffer)
	 */
	protected void initContent(ByteBuffer out) {
	    // 调用父类的initContent填充包头
	    super.initContent(out);
	    // 位置字节
	    out.put((byte)0);
	    // 命令
	    out.putChar(command);
	    // 判断命令类型和信息类型,调用不同的填充函数
	    switch(command) {
	        case QQ.QQ_FILE_CMD_FILE_OP:
				if(infoType == QQ.QQ_FILE_BASIC_INFO)
					initBasicInfo(out);
				else if(infoType == QQ.QQ_FILE_DATA_INFO)
					initDataInfo(out);
				else if(infoType == QQ.QQ_FILE_EOF)
					initEOFInfo(out);			
				break;
	        case QQ.QQ_FILE_CMD_FILE_OP_ACK:
				if(infoType == QQ.QQ_FILE_BASIC_INFO)
					initBasicInfoAck(out);
				else if(infoType == QQ.QQ_FILE_DATA_INFO)
					initDataInfoAck(out);
				else if(infoType == QQ.QQ_FILE_EOF)
					initEOFInfoAck(out);
				break;
		    case QQ.QQ_FILE_CMD_HEART_BEAT:
		    case QQ.QQ_FILE_CMD_HEART_BEAT_ACK:
		        out.putChar(heartBeatSequence);
		    	out.put((byte)0);
				break;
		    case QQ.QQ_FILE_CMD_TRANSFER_FINISHED:
		        out.putChar((char)0);
		    	out.put((byte)0);
			    break;
			default:
			    log.error("未知命令");
				break;
	    }
	}	

	// 初始化结束信息回复包
	private void initEOFInfoAck(ByteBuffer out) {
		// 最大分片序号加1
		out.putChar((char)packetIndex);
		// 子操作类型字节
		out.put(infoType);
		// 四个未知字节,全0
		out.putInt(0);
	}

	// 初始化数据信息回复包
	private void initDataInfoAck(ByteBuffer out) {
		// 刚收到的分片序号
		out.putChar((char)packetIndex);
		// 子操作类型
		out.put(infoType);
		// 之前已经收到的最大分片序号
		out.putInt(fragmentIndex);
	}

	// 初始化基本信息回复包
	private void initBasicInfoAck(ByteBuffer out) {
		// 两个未知字节全0
		out.putChar((char)0);
		// 子操作类型
		out.put(infoType);
		// 四个未知字节,全0
		out.putInt(0);
	}

    /**
	 * 初始化文件结束信息包
	 */
	private void initEOFInfo(ByteBuffer out) {
		// 最大分片序号加1
		out.putChar((char)(watcher.getFragments() + 1));
		// 子操作类型类型字节
		out.put(infoType);
	}

	/**
	 * 初始化数据信息包
	 */
	private void initDataInfo(ByteBuffer out) {
		// 当前分片序号
		out.putChar((char)packetIndex);
		// 子操作类型
		out.put(infoType);
		// 已经发送成功的最大分片序号
		out.putInt(fragmentIndex);
		// 分片的绝对偏移
		out.putInt(fragmentOffset);
		// 当前分片的长度
		out.putChar((char)fragmentLength);
		// 当前分片数据
		out.put(fragmentData);
	}

	/**
	 * 初始化基本信息包
	 */
	private void initBasicInfo(ByteBuffer out) {
		// 未知2字节,0x0000
		out.putChar((char)0);
		// 子操作类型
		out.put(infoType);
		// 文件长度
		out.putInt(watcher.getFileSize());
		// 文件分片数
		out.putInt(watcher.getFileSize() / watcher.getMaxFragmentSize() + 1);
		// 分片长度
		out.putInt(watcher.getMaxFragmentSize());
		// 两个md5
		out.put(watcher.getFileMD5());
		out.put(watcher.getFileNameMD5());
		// 文件名长度,2字节
		byte[] b = watcher.getFileName().getBytes();
		out.putChar((char)b.length);
		// 未知的8字节,全0
		out.putLong(0);
		// 文件名
		out.put(b);
	}

	/* (non-Javadoc)
	 * @see edu.tsinghua.lumaqq.qq.filetrans.FilePacket#parseContent(java.nio.ByteBuffer)
	 */
	protected void parseContent(ByteBuffer in) throws PacketParseException {
	    super.parseContent(in);
	    // 跳过一个未知字节
	    in.get();
	    // 读取command
	    command = in.getChar();
	    switch(command) {
			case QQ.QQ_FILE_CMD_FILE_OP:
			    parseFileOp(in);	
				break;
			case QQ.QQ_FILE_CMD_FILE_OP_ACK:
			    parseFileOpAck(in);
				break;
			case QQ.QQ_FILE_CMD_HEART_BEAT:
			    parseHeartBeat(in);
				break;
			case QQ.QQ_FILE_CMD_HEART_BEAT_ACK:
			    parseHeartBeatAck(in);
				break;
			case QQ.QQ_FILE_CMD_TRANSFER_FINISHED:
			    parseTransferFinished(in);
				break;
	    }
	}
	
    /**
     * 解析文件操作的回复包
     * @param in
     */
    private void parseFileOpAck(ByteBuffer in) {
        // 读取分片序号
        packetIndex = in.getChar();
        // 读取信息类型
        infoType = in.get();
		if(infoType == QQ.QQ_FILE_BASIC_INFO)
		    parseBasicInfoAck(in);
		else if(infoType == QQ.QQ_FILE_DATA_INFO)
		    parseDataInfoAck(in);
		else if(infoType == QQ.QQ_FILE_EOF)
		    parseFileEOFAck(in);
    }

    /**
	 * 解析文件操作包
     * @param in
     */
    private void parseFileOp(ByteBuffer in) {
        // 读取分片序号
        packetIndex = in.getChar();
        // 读取信息类型
        infoType = in.get();
		if(infoType == QQ.QQ_FILE_BASIC_INFO)
		    parseBasicInfo(in);
		else if(infoType == QQ.QQ_FILE_DATA_INFO) 
		    parseDataInfo(in);   
    }

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -