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

📄 packetprocessor.java

📁 用java实现的
💻 JAVA
📖 第 1 页 / 共 3 页
字号:
/*
* 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;

import java.util.Date;
import java.util.Iterator;
import java.util.Vector;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

import edu.tsinghua.lumaqq.qq.beans.FriendOnlineEntry;
import edu.tsinghua.lumaqq.qq.beans.FriendStatus;
import edu.tsinghua.lumaqq.qq.beans.OnlineUser;
import edu.tsinghua.lumaqq.qq.beans.QQFriend;
import edu.tsinghua.lumaqq.qq.beans.QQUser;
import edu.tsinghua.lumaqq.qq.events.PacketEvent;
import edu.tsinghua.lumaqq.qq.events.PacketListener;
import edu.tsinghua.lumaqq.qq.events.QQEvent;
import edu.tsinghua.lumaqq.qq.events.QQListener;
import edu.tsinghua.lumaqq.qq.packets.InPacket;
import edu.tsinghua.lumaqq.qq.packets.in.AddFriendAuthReplyPacket;
import edu.tsinghua.lumaqq.qq.packets.in.AddFriendReplyPacket;
import edu.tsinghua.lumaqq.qq.packets.in.ChangeStatusReplyPacket;
import edu.tsinghua.lumaqq.qq.packets.in.ClusterCommandReplyPacket;
import edu.tsinghua.lumaqq.qq.packets.in.DeleteFriendReplyPacket;
import edu.tsinghua.lumaqq.qq.packets.in.DownloadGroupFriendReplyPacket;
import edu.tsinghua.lumaqq.qq.packets.in.FriendChangeStatusPacket;
import edu.tsinghua.lumaqq.qq.packets.in.FriendRemarkOpReplyPacket;
import edu.tsinghua.lumaqq.qq.packets.in.GetFriendListReplyPacket;
import edu.tsinghua.lumaqq.qq.packets.in.GetFriendOnlineReplyPacket;
import edu.tsinghua.lumaqq.qq.packets.in.GetUserInfoReplyPacket;
import edu.tsinghua.lumaqq.qq.packets.in.GroupNameOpReplyPacket;
import edu.tsinghua.lumaqq.qq.packets.in.KeepAliveReplyPacket;
import edu.tsinghua.lumaqq.qq.packets.in.LoginReplyPacket;
import edu.tsinghua.lumaqq.qq.packets.in.ModifyInfoReplyPacket;
import edu.tsinghua.lumaqq.qq.packets.in.ReceiveIMPacket;
import edu.tsinghua.lumaqq.qq.packets.in.RemoveSelfReplyPacket;
import edu.tsinghua.lumaqq.qq.packets.in.SearchUserReplyPacket;
import edu.tsinghua.lumaqq.qq.packets.in.SendIMReplyPacket;
import edu.tsinghua.lumaqq.qq.packets.in.SystemNotificationPacket;
import edu.tsinghua.lumaqq.qq.packets.in.RequestKeyReplyPacket;
import edu.tsinghua.lumaqq.qq.packets.in.UploadGroupFriendReplyPacket;
import edu.tsinghua.lumaqq.qq.packets.out.ClusterCommandPacket;
import edu.tsinghua.lumaqq.qq.packets.out.ModifyInfoPacket;
import edu.tsinghua.lumaqq.qq.packets.out.SendIMPacket;


/**
 * 这个类用来处理收到的数据,其注册为IRecevier的包事件监听器,这样每当一个包到来时便
 * 被调用处理
 *
 * @author 马若劼
 */
class PacketProcessor implements PacketListener {
    // Log对象
    static Log log = LogFactory.getLog(PacketProcessor.class);

    // QQ客户端
    private QQClient client;
    // QQ用户,我们将在收到登陆包后设置user不为null,所以如果user为null意味着尚未登陆
    private QQUser user;
    // QQ监听器
    private Vector qqListeners, qqListenersBackup;
    // 收到的包计数,注意这个是没有冲突的包的计数,所以超过65535就清0
    private int total;  
    // 检查重复包的类
    private PacketMonitor monitor;
    // 是否监听器有改变,这是为了处理同步问题而设置的,在实际fireQQEvent时,
    //     不使用qqListeners,而是用backup,这样在事件触发期间如果要修改
    //     监听器也是可以的,不然有可能造成并发修改异常。如果有更好的办法当
    //     然,目前只想出这种办法
    private boolean listenerChanged;
    private static int i = 0;
    
    /**
     * @param udp 如果要以UDP方式解析包,则为true
     */
    public PacketProcessor(QQClient client) {
        this.client = client;
        this.user = null;
        this.qqListeners = new Vector();
        this.qqListenersBackup = new Vector();
        this.total = 0;
        this.monitor = PacketMonitor.getInstance();
        this.listenerChanged = false;
    }    
    
    // 处理登陆应答
    private void processLoginReply(InPacket in) throws PacketParseException {
    	QQEvent e;
    	LoginReplyPacket packet = (LoginReplyPacket)in;
    	// 开始判断应答内容
    	switch(packet.replyCode) {
    		case QQ.QQ_LOGIN_REPLY_OK:
    			log.debug("登陆成功,用户IP为: " + Utils.getIpStringFromBytes(packet.ip) + " 端口: " + packet.port);
    			// 登陆成功的话我们就设置用户的一些信息,然后触发事件
    			user.setSessionKey(packet.sessionKey);
    			user.setIp(packet.ip);
    			user.setServerIp(packet.serverIp);
    			user.setLastLoginIp(packet.lastLoginIp);
    			user.setPort(packet.port);
    			user.setServerPort(packet.serverPort);
    			user.setLoginTime(packet.loginTime);
    			user.setLastLoginTime(packet.lastLoginTime);
    			// 得到文件传输密钥
    		    byte[] b = new byte[4 + QQ.QQ_KEY_LENGTH];
    		    b[0] = (byte)((user.getQqNum() >>> 24) & 0xFF);
    		    b[1] = (byte)((user.getQqNum() >>> 16) & 0xFF);
    		    b[2] = (byte)((user.getQqNum() >>> 8) & 0xFF);
    		    b[3] = (byte)(user.getQqNum() & 0xFF);
    		    System.arraycopy(user.getSessionKey(), 0, b, 4, QQ.QQ_KEY_LENGTH);
    		    user.setFileSessionKey(Utils.doMD5(b));
    		    // 触发事件 
    			e = new QQEvent(packet);
    			e.type = QQEvent.QQ_LOGIN_SUCCESS;
    			this.fireQQEvent(e);
    			break;
    		case QQ.QQ_LOGIN_REPLY_PWD_ERROR:
    			log.debug("用户密码错误,错误信息: " + packet.replyMessage);
    			// 如果是密码错误,触发事件    			
    			e = new QQEvent(packet);
    			e.type = QQEvent.QQ_LOGIN_PASSWROD_ERROR;
    			this.fireQQEvent(e);
    			break;
    		case QQ.QQ_LOGIN_REPLY_REDIRECT:
    			log.debug("登陆重定向");
    			// 如果是登陆重定向,继续登陆
    			this.user = null;
    			client.setLoginRedirect(true);
    			client.login(Utils.getIpStringFromBytes(packet.redirectIp), packet.redirectPort);
    			break;
    		case QQ.QQ_LOGIN_REPLY_MISC_ERROR:
    			log.debug("未知登陆错误");
    			// 如果是其他未知错误,触发事件
    			e = new QQEvent(packet);
    			e.type = QQEvent.QQ_LOGIN_UNKNOWN_ERROR;
    			this.fireQQEvent(e);
    			break;
    	}
    }

    /* (non-Javadoc)
     * @see edu.tsinghua.lumaqq.protocol.qq.PacketListener#packetArrived(edu.tsinghua.lumaqq.protocol.qq.PacketEvent)
     */
    public void packetArrived(PacketEvent e) {
        InPacket in = (InPacket) e.getSource();
        log.debug("开始处理" + in.toString());
        
        // 检查是否是在登陆之前收到的包,如果是,推迟处理该包
        if(in.getCommand() != QQ.QQ_CMD_LOGIN && (user == null || !user.isLoggedIn())) {
            /*
             * 以下代码用来将在登录之前收到的包推回队列延迟处理,但是在实际中观察发现,登录之前收到的
             * 东西基本没用,所以在这里不启用这段代码
             */
        	// 把这个包压回队列延迟处理
        	//client.getPort().pushBack(in);
        	//log.debug("在登陆之前收到了其他包,压回队列延迟处理,包序号为: " + (int)in.getSequence());
        	return;
        }
      
        // 这里检查是不是服务器发回的确认包
        //     为什么要检查这个呢,因为有些包是服务器主动发出的,对于这些包我们是不需要
        //     从超时发送队列中移除的,如果是服务器的确认包,那么我们就需要把超时发送队列
        //     中包移除
        switch (in.getCommand()) {
        	// 这三种包是服务器先发出的,我们要确认
        	case QQ.QQ_CMD_RECV_IM:
        	case QQ.QQ_CMD_RECV_MSG_SYS:
        	case QQ.QQ_CMD_RECV_MSG_FRIEND_CHANGE_STATUS:
        		break;
        	// 其他情况我们删除超时队列中的包
        	default:
        	    IPort port = client.getPort();
        		if(port != null) port.removeResend(in);
        		log.debug("包" + (int)in.getSequence() + "的确认已经收到,将不再发送");
        		break;
        }
        
        // 现在开始判断包的类型,作出相应的处理
        try {
	        switch (in.getCommand()) {
	        	case QQ.QQ_CMD_KEEP_ALIVE:
	        		processKeepAliveReply(in);
	        		break;
	        	case QQ.QQ_CMD_MODIFY_INFO:
	        	    processModifyInfoReply(in);
	        		break;
	        	case QQ.QQ_CMD_SEARCH_USER:
	        		processSearchUserReply(in);
	        		break;
	        	case QQ.QQ_CMD_ADD_FRIEND:
	        	    processAddFriendReply(in);
	        		break;
	        	case QQ.QQ_CMD_DELETE_FRIEND:
	        	    processDeleteFriendReply(in);
	        		break;
	        	case QQ.QQ_CMD_REMOVE_SELF:
	        	    processRemoveSelfReply(in);
	        		break;
	        	case QQ.QQ_CMD_ADD_FRIEND_AUTH:
	        	    processAddFriendAuthReply(in);
	        		break;
	        	case QQ.QQ_CMD_GET_USER_INFO:
	        		processGetUserInfoReply(in);
	        		break;
	        	case QQ.QQ_CMD_CHANGE_STATUS:
	        		processChangeStatusReply(in);
	        		break;
	        	case QQ.QQ_CMD_SEND_IM:
	        		processSendIMReply(in);
	        		break;
	        	case QQ.QQ_CMD_RECV_IM:
	        	    processReceiveIM(in);
	        		break;
	        	case QQ.QQ_CMD_LOGIN:
	        		this.user = client.getUser();
	        		processLoginReply(in);
	        		break;
	        	case QQ.QQ_CMD_GET_FRIEND_LIST:
	        		processGetFriendListReply(in);
	        		break;
	        	case QQ.QQ_CMD_GET_FRIEND_ONLINE:
	        		processGetFriendOnlineReply(in);
	        		break;
	        	case QQ.QQ_CMD_RECV_MSG_SYS:
	        	    processSystemNotification(in);
	        		break;
	        	case QQ.QQ_CMD_RECV_MSG_FRIEND_CHANGE_STATUS:
	        		processFriendChangeStatus(in);
	        		break;
	        	case QQ.QQ_CMD_UPLOAD_GROUP_FRIEND:
	        		processUploadGroupFriendReply(in);
	        		break;
	        	case QQ.QQ_CMD_DOWNLOAD_GROUP_FRIEND:
	        		processDownloadGroupFriendReply(in);
	        		break;
	        	case QQ.QQ_CMD_GROUP_NAME_OP:
	        		processGroupNameOpReply(in);
	        		break;
	        	case QQ.QQ_CMD_FRIEND_REMARK_OP:
	        		processFriendRemarkOpReply(in);
	        		break;
	        	case QQ.QQ_CMD_CLUSTER_CMD:
	        		processClusterCommandReply(in);
	        		break;
	        	case QQ.QQ_CMD_REQUEST_KEY:
	        		processRequestKeyReply(in);
	        		break;
	        	default:
	        		break;
	        }        	
        } catch (PacketParseException ex) {
        	log.error(ex.getMessage() + ": 抛弃改包");
        	if(ex.getEvent() != null)
        	    this.fireQQEvent(ex.getEvent());
        }
    }

    // 处理群命令的回复包
	private void processClusterCommandReply(InPacket in) throws PacketParseException {
		ClusterCommandReplyPacket packet = (ClusterCommandReplyPacket)in;

		switch(packet.subCommand) {
			case QQ.QQ_CLUSTER_CMD_CREATE_CLUSTER:
				processClusterCreateReply(packet);
				break;
			case QQ.QQ_CLUSTER_CMD_ACTIVATE_CLUSTER:
				processClusterActivateReply(packet);
				break;
			case QQ.QQ_CLUSTER_CMD_GET_CLUSTER_INFO:
				processClusterGetInfoReply(packet);
				break;
			case QQ.QQ_CLUSTER_CMD_GET_ONLINE_MEMBER:
				processClusterGetOnlineMemberReply(packet);
				break;
			case QQ.QQ_CLUSTER_CMD_GET_MEMBER_INFO:
				processClusterGetMemberInfoReply(packet);
				break;
			case QQ.QQ_CLUSTER_CMD_EXIT_CLUSTER:
				processClusterExitReply(packet);
				break;
			case QQ.QQ_CLUSTER_CMD_JOIN_CLUSTER:
				processClusterJoinReply(packet);
				break;
			case QQ.QQ_CLUSTER_CMD_JOIN_CLUSTER_AUTH:
				processClusterJoinAuthReply(packet);
				break;
			case QQ.QQ_CLUSTER_CMD_MODIFY_CLUSTER_INFO:
				processClusterModifyInfoReply(packet);
				break;
			case QQ.QQ_CLUSTER_CMD_SEARCH_CLUSTER:
				processClusterSearchReply(packet);
				break;
			case QQ.QQ_CLUSTER_CMD_SEND_IM:
				processClusterSendIMReply(packet);
				break;
		}
	}

	// 处理发送群消息的回复包
	private void processClusterSendIMReply(ClusterCommandReplyPacket packet) {
		QQEvent e;
		if(packet.replyCode == QQ.QQ_CLUSTER_CMD_REPLY_OK) {
			log.debug("群消息发送成功");
			e = new QQEvent(packet);
			e.type = QQEvent.QQ_SEND_CLUSTER_IM_SUCCESS;
			this.fireQQEvent(e);
		} else {
			log.debug("群消息发送失败");
			ClusterCommandPacket ccp = (ClusterCommandPacket)monitor.retrieveRequest(packet);
			packet.clusterId = ccp.getClusterId();
			e = new QQEvent(packet);
			e.type = QQEvent.QQ_SEND_CLUSTER_IM_FAIL;
			this.fireQQEvent(e);
		}
	}

	// 处理搜索群的回复包
	private void processClusterSearchReply(ClusterCommandReplyPacket packet) {
		QQEvent e;
		if(packet.replyCode == QQ.QQ_CLUSTER_CMD_REPLY_OK) {
			log.debug("群搜索成功");
			e = new QQEvent(packet);
			e.type = QQEvent.QQ_SEARCH_CLUSTER_SUCCESS;
			this.fireQQEvent(e);
		} else {
			log.debug("搜索群失败");
			e = new QQEvent(packet);
			e.type = QQEvent.QQ_SEARCH_CLUSTER_FAIL;
			this.fireQQEvent(e);
		}
	}

	// 处理修改群信息的回复包

⌨️ 快捷键说明

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