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

📄 receiveimpacket.java

📁 很多人都在开发的QQ代码
💻 JAVA
字号:
/*
* 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.packets.in;

import java.io.ByteArrayInputStream;
import java.io.DataInputStream;
import java.nio.ByteBuffer;

import edu.tsinghua.lumaqq.qq.PacketParseException;
import edu.tsinghua.lumaqq.qq.QQ;
import edu.tsinghua.lumaqq.qq.Utils;
import edu.tsinghua.lumaqq.qq.beans.ClusterIM;
import edu.tsinghua.lumaqq.qq.beans.FileInfo;
import edu.tsinghua.lumaqq.qq.beans.FileTransferArgs;
import edu.tsinghua.lumaqq.qq.beans.NormalIM;
import edu.tsinghua.lumaqq.qq.beans.NormalIMHeader;
import edu.tsinghua.lumaqq.qq.beans.ReceiveIMHeader;
import edu.tsinghua.lumaqq.qq.beans.SMS;
import edu.tsinghua.lumaqq.qq.packets.InPacket;


/**
 * <pre>
 * 别人发来的消息包,如果是普通消息,格式为
 * 1. 头部
 * 2. 发送者QQ号,4字节
 * 3. 接收者QQ号,4字节
 * 4. 包序号(并非我们发送时候的序号,因为这个是4字节,可能是服务器端得总序号)
 * 5. 发送者IP,如果是服务器转发的,那么ip就是服务器ip, 4字节
 * 6. 发送者端口,如果是服务器转发的,那么就是服务器的端口,2字节
 * 7. 消息类型,是好友发的,还是陌生人发的,还是系统消息等等, 2字节
 * 8. 发送者QQ版本,2字节
 * 9. 发送者的QQ号,4字节
 * 10. 接受者的QQ号,4字节
 * 11. md5处理的发送方的uid和session key,16字节
 * 12. 普通消息类型,比如是文本消息还是其他什么消息,2字节
 * 13. 会话ID,2字节,如果是一个操作需要发送多个包才能完成,则这个id必须一致
 * 14. 发送时间,4字节
 * 15. 1字节未知
 * 16. 发送者头像,1字节
 * 17. 未知的3字节
 * 18. 是否有字体属性,1字节
 * 19. 未知的4字节
 * 20. 消息类型,这里的类型表示是正常回复还是自动回复之类的信息, 1字节
 * 21. 消息正文,0是分隔符
 * 22. 字体属性,和SendIMPacket中的相同
 * 23. 尾部
 * 
 * 如果是手机短消息,格式为:
 * 1-7. 与普通消息相同,只不过7是0x000B,表示手机短消息
 * 8.   未知1字节,0x0
 * 9.   发送者QQ号,4字节
 * 10.  未知1字节,0x0
 * 11.  未知1字节
 * 12.  发送者名称,最多13字节,不足后面补0
 * 13.  未知的1字节,0x4D
 * 14.  消息内容,一直到结尾都是,这个包长度200字节,如果不足,后面补0
 * 15.  尾部
 * 
 * 如果是群消息,并且是普通聊天消息,格式为:
 * 1-7. 与普通消息相同,只不过发送者QQ号相当于是群的内部ID
 * 8.   群外部ID,4字节
 * 9.   群类型,1字节
 * 10.  发送者QQ号,4字节
 * 11.  未知的两字节,全0
 * 12.  消息序号,2字节
 * 13.  消息发送时间,4字节
 * 14.  未知的两字节,全0
 * 15.  未知的两字节,全0
 * 16.  后面的数据的长度,2字节
 * 17.  以0结尾的消息内容
 * 18.  字体属性,和SendIMPacket中的相同
 * 19.  尾部
 * 
 * 如果是请求传送文件,格式为
 * 1 - 18. 与普通消息格式相同,差别只有12部分,为0x0035,表示是请求传送文件
 * 19.     未用部分,全0,15字节
 * 20.     固定字节0x65
 * 21.     连接方式,1字节
 * 22.     请求者外部ip,4字节
 * 23.     请求者QQ端口,2字节,如果连接方式为0x3,则这个部分没有
 * 24.     第一个监听端口,2字节
 * 25.     请求者真实ip,4字节
 * 26.     第二个监听端口,2字节
 * 27.     空格符0x20
 * 28.     分隔符0x1F
 * 29.     文件名,不定长,不包含路径名
 * 30.     分隔符0x1F
 * 31.     文件字节数的字符串形式加“ 字节”
 * 32.     尾部
 * 
 * 如果是接受传送文件的请求,格式为
 * 1 - 18. 与普通消息格式相同,差别只有12部分,对于UDP请求,为0x0037
 * 19 - 26 和0x0035时相同
 * 27.     尾部 
 * 
 * 如果是通知文件传送的端口,格式为
 * 1 - 18. 与普通消息格式相同,差别只有12部分,为0x003B,表示是通知文件传送端口
 * 19 - 26 和0x0035时相同
 * 27. 尾部
 * 
 * 如果是0x0041,可能是向服务器报告最后连接情况的包,格式为
 * 1 - 27. 和0x003B时相同,差别只有命令不同,为0x0041
 * 
 * 如果是取消传送文件请求,格式为:
 * 1 - 18. 与普通消息格式相同,差别只有12部分,为0x0049,表示是取消传送文件
 * 19.     未用部分,全0,15字节
 * 20.     固定字节0x65
 * 21.     尾部
 * </pre>
 * 
 * @see edu.tsinghua.lumaqq.qq.beans.NormalIM
 * @see edu.tsinghua.lumaqq.qq.beans.NormalIMHeader
 * @see edu.tsinghua.lumaqq.qq.beans.ReceiveIMHeader
 * @see edu.tsinghua.lumaqq.qq.beans.ClusterIM
 * @see edu.tsinghua.lumaqq.qq.beans.FileTransferArgs
 * @see edu.tsinghua.lumaqq.qq.beans.FileInfo
 * 
 * @author 马若劼
 */
public class ReceiveIMPacket extends InPacket {
	// 流对象
	private ByteArrayInputStream bais;
	private DataInputStream dis;
	
	// 用于发送确认
    public byte[] reply;
    // 为true时表示收到的消息是空的
    public boolean empty; 
    // 整个包的头
    public ReceiveIMHeader header;
    
    // 仅用于普通消息时
    public NormalIMHeader normalHeader;
    public NormalIM normalIM;
    
    // 仅用于系统通知时
    public char systemMessageType;
    public String sysMessage;
    
    // 仅用于文件传输时
    public FileInfo fileInfo;
    public FileTransferArgs fileArgs;
    
    // 仅用于群普通消息时
    public ClusterIM clusterIM;
    
    // 仅用于手机短消息时
    public SMS sms;
    
    // 仅用于其他类型群消息
    public int externalId;
    public byte clusterType;
    public int sender;
    public String message;
    
    /**
     * 构造函数
     * @param buf 缓冲区
     * @param length 包长度
     * @throws PacketParseException 解析错误
     */
    public ReceiveIMPacket(ByteBuffer buf, int length) throws PacketParseException {
        super(buf, length);
    }   

    /* (non-Javadoc)
     * @see edu.tsinghua.lumaqq.qq.packets.InPacket#parseBody(java.nio.ByteBuffer)
     */
    protected void parseBody(ByteBuffer buf) throws PacketParseException {
        empty = false;
        // 检查消息长度,至少要有16字节,因为我们需要前16字节做为确认发回
        if(buf.remaining() < 16)
            throw new PacketParseException("收到的消息太短,抛弃该消息");  
	    // 得到前16个字节用作回复
	    reply = new byte[16];
	    buf.get(reply);
	    // 读取消息头
	    buf.rewind();
	    header = new ReceiveIMHeader();
	    header.readBean(buf);
        // 检查输入流可用字节
        if(!buf.hasRemaining()) {
            empty = true;
            return;
        }
	    // 判断消息类型
        switch(header.type) {
		    case QQ.QQ_RECV_IM_TO_BUDDY:
		    case QQ.QQ_RECV_IM_TO_UNKNOWN:
		        /* 是从好友或者陌生人处发来的消息 */			        
		        parseNormalIM(buf);
		    	break;
		    case QQ.QQ_RECV_IM_SYS_MESSAGE:
		        /* 是系统消息 */
		        parseSystemMessage(buf);
		    	break;
		    case QQ.QQ_RECV_IM_CLUSTER_IM:
		    	/* 群消息 */
		        parseClusterIM(buf);
		    	break;
		    case QQ.QQ_RECV_IM_CREATE_CLUSTER:
		    case QQ.QQ_RECV_IM_ADDED_TO_CLUSTER:
		    case QQ.QQ_RECV_IM_DELETED_FROM_CLUSTER:
		    	externalId = buf.getInt();
		    	clusterType = buf.get();
		    	sender = buf.getInt();
		    	break;
		    case QQ.QQ_RECV_IM_APPROVE_JOIN_CLUSTER:
		    case QQ.QQ_RECV_IM_REJECT_JOIN_CLUSTER:
		    case QQ.QQ_RECV_IM_REQUEST_JOIN_CLUSTER:
		    	externalId = buf.getInt();
		    	clusterType = buf.get();
		    	sender = buf.getInt();
		    	int len = buf.get() & 0xFF;
		    	byte[] b = new byte[len];
		    	buf.get(b);
		    	message = Utils.getString(b, QQ.QQ_CHARSET_DEFAULT);
		    	break;
		    case QQ.QQ_RECV_IM_SMS:
		        processSMS(buf);
		    default:
		        // 其他类型的消息我们现在没有办法处理,忽略
		        break;     
        }
    }

	/**
	 * 解析手机短消息
	 * 
     * @param buf
     */
    private void processSMS(ByteBuffer buf) {
        sms = new SMS();
        sms.readBean(buf);
    }

    /**
	 * 解析群普通消息
	 */
	private void parseClusterIM(ByteBuffer buf) {
		clusterIM = new ClusterIM();
		clusterIM.readBean(buf);
	}

	/**
	 * 解析系统消息
	 */
	private void parseSystemMessage(ByteBuffer buf) {
        // 解析系统消息,系统消息由0x2F分隔而成
        //     第一段是回复代码
        //     第二段是消息正文
        byte[] b = new byte[buf.remaining()];
        buf.get(b);
        int i = Utils.indexOf(b, 0, (byte)0x2F);
        systemMessageType = (char)Utils.getUnsignedInt(b, 0, i);
        // 后面是系统消息了
        i++;
        sysMessage = Utils.getString(b, i, b.length - i, QQ.QQ_CHARSET_DEFAULT);
	}

	/**
	 * 解析普通消息
	 */
	private void parseNormalIM(ByteBuffer buf) throws PacketParseException {
        try {
            // 读入普通消息头
            normalHeader = new NormalIMHeader();
            normalHeader.readBean(buf);
            // 判断普通消息类型
            if(normalHeader.type == QQ.QQ_IM_NORMAL_TEXT) {
                normalIM = new NormalIM();
                normalIM.readBean(buf);			            
            } else if(normalHeader.type == QQ.QQ_IM_UDP_REQUEST 
            		|| normalHeader.type == QQ.QQ_IM_TCP_REQUEST){
            	fileArgs = new FileTransferArgs();
            	fileArgs.readBean(buf);
            	fileInfo = new FileInfo();
            	fileInfo.readBean(buf);
            } else if(normalHeader.type == QQ.QQ_IM_ACCEPT_UDP_REQUEST
            		|| normalHeader.type == QQ.QQ_IM_NOTIFY_IP) {
            	fileArgs = new FileTransferArgs();
            	fileArgs.readBean(buf);
            } 
            log.debug("收到的普通消息类型为:" + Utils.getNormalIMTypeString(normalHeader.type));
        } catch (Exception e) {
            throw new PacketParseException(e.getMessage());
        }
	}
}

⌨️ 快捷键说明

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