myicq-1.0a1服务器代码分析(一)

    技术2022-05-11  138

    myicq-1.0a1服务器代码分析(一)

    顾剑辉(Solarsoft)

    myicq代码的公布已经有一段时间了,听说作者张勇已经不再公开的服务器端的代码了,不尤觉得可惜,拜读他的作品已经有一段时间了,今天来发表一下自己的意见。

    我现在就从整体的构架来谈谈他服务器端的代码,服务器代码可分成数据库、upd服务、服务器群组、插件四块。

    我这里对upd服务的实现进行一些讨论,

    一、数据缓冲包的类管理

    先定义了抽象类OutPacketInPacket,代码如下:

    class OutPacket {

    public:

           virtual OutPacket &operator <<(uint8 b) = 0;

           virtual OutPacket &operator <<(uint16 w) = 0;

           virtual OutPacket &operator <<(uint32 dw) = 0;

           virtual OutPacket &operator <<(ICQ_STR &str) = 0;

    };

     

     

    class InPacket {

    public:

           virtual InPacket &operator >>(uint8 &b) = 0;

           virtual InPacket &operator >>(uint16 &w) = 0;

           virtual InPacket &operator >>(uint32 &dw) = 0;

           virtual InPacket &operator >>(ICQ_STR &str) = 0;

    };

    在实现其派生类IcqOutPacketIcqInPacket,并定义数据缓冲区uint8 data[UDP_PACKET_SIZE],大小为1024,主要是处理数据的输入与输出。

    再派生其类UdpOutPacketUdpInPacket,此时进行了DES的数据加密与解密的处理,并加入数据头,定义如下:

    struct UDP_CLI_HDR {

           uint16 ver;

           uint32 reserved;

           uint32 uin;

           uint32 sid;

           uint16 cmd;

           uint16 seq;

           uint16 cc;              // check code

    };

     

    struct UDP_SRV_HDR {

           uint16 ver;

           uint32 reserved;

           uint32 uin;

           uint32 sid;

           uint16 cmd;

           uint16 seq;

           uint16 ackseq;

    };

    此时缓冲区的包类已经结束。

    二、socket服务代码的编写

    先定义2个类RefObject Session,其中RefObject为记数类,两代码如下:

    class RefObject {

    public:

           RefObject() {

                  refCount = 1;

           }

     

           int addRef() {

                  return ++refCount;

           }

           int release() {

                  register int ret = --refCount;

                  if (!ret)

                         delete this;

                  return ret;

           }

     

    protected:

           virtual ~RefObject() {}

     

           int refCount;

    };

    class Session {

    public:

           uint32 uin;

           uint32 status;

           uint32 ip;

           uint16 msgport;

           uint32 realip;

    };

    现在我们来看一下udp服务类的定义

    class UdpSession : public RefObject, public Session {

    public:

           UdpSession(UdpInPacket &in, uint32 ip, uint16 port);

           ~UdpSession();

     

           UdpOutPacket *createPacket(uint16 cmd, uint16 ackseq = 0);

           void sendPacket(UdpOutPacket *p);

     

           void sendOnline(Session *s, ICQ_STR &domain);

           void sendOffline(uint32 uin, ICQ_STR &domain);

           void sendStatusChanged(Session *s, ICQ_STR &domain);

           void sendMessage(uint8 type, QID &src, uint32 when, ICQ_STR &text);

           void sendGroupTypes();

           void updateContactReply(uint16 seq, uint8 *data, int n, Server *server = NULL);

           void searchRandomReply(uint16 seq, uint8 *data, int n);

           void searchUINReply(uint16 seq, uint8 *data, int n);

     

           void dead();

           void logout();

     

           void onlineNotify();

           void offlineNotify();

           void statusNotify(uint32 newStatus);

     

           static bool init();

           static void destroy();

           static bool onReceive();

           static void checkSendQueue();

           static void checkKeepAlive();

     

           static void sendMessage(uint8 type, QID &dst, QID &src, uint32 when, ICQ_STR &text);

           static void addFriend(uint16 seq, QID &dst, uint32 src, Server *server = NULL);

           static void addFriendAuth(uint16 seq, QID &dst, uint32 src, Server *server, uint8 auth, bool sendAuthMsg = true);

           static void updateContact(uint16 seq, uint32 dst, uint32 src, Server *server = NULL);

           static void searchUIN(uint16 seq, uint32 dst, uint32 src, Server *server = NULL);

     

           uint8 auth;

           uint8 face;

           char nickname[MAX_NICK + 1];

           char province[MAX_PROVINCE + 1];

     

           uint16 tcpver;

           uint16 port;

           uint32 oldMsgID, lastMsgID;

           char subkey[128];

     

           ListHead uinItem;

           ListHead ipportItem;

           ListHead listItem;

     

           static int sock;

           static uint32 sessionCount;

     

    private:

           void createPacket(UdpOutPacket &out, uint16 cmd, uint16 seq, uint16 ackseq);

           void sendDirect(UdpOutPacket *p);

           void sendAckPacket(uint16 seq);

           bool setWindow(uint16 seq);

           void notify(DB_CALLBACK cb1, DB_CALLBACK cb2);

     

           void onAck(uint16 seq);

           void onKeepAlive(UdpInPacket &in);

           void onNewUIN(UdpInPacket &in);

           void onGetContactList(UdpInPacket &in);

           void onGetRemoteContactList(UdpInPacket &in);

           void onLogin(UdpInPacket &in);

           void onLogout(UdpInPacket &in);

           void onChangeStatus(UdpInPacket &in);

           void onUpdateContact(UdpInPacket &in);

           void onModifyUser(UdpInPacket &in);

           void onUpdateUser(UdpInPacket &in);

           void onSendMessage(UdpInPacket &in);

           void onSearchRandom(UdpInPacket &in);

           void onSearchCustom(UdpInPacket &in);

           void onAddFriend(UdpInPacket &in);

           void onDelFriend(UdpInPacket &in);

           void onSendBCMsg(UdpInPacket &in);

           void onGetServerList(UdpInPacket &in);

           void onGetGroupList(UdpInPacket &in);

           void onSearchGroup(UdpInPacket &in);

     

           void onCreateGroup(UdpInPacket &in);

           void onEnterGroup(UdpInPacket &in);

           void onExitGroup(UdpInPacket &in);

           void onGroupStart(UdpInPacket &in);

           void onGroupMessage(UdpInPacket &in);

           void onGroupCmd(UdpInPacket &in);

     

           bool onPacketReceived(UdpInPacket &in);

     

           uint16 udpver;

           uint32 sid;

           uint16 sendSeq;

           uint16 recvSeq;

           uint32 window;

           time_t expire;

     

           uint8 isDead : 1;

     

           IcqGroup *group;

     

           ListHead sendQueue;

     

           DECLARE_SLAB(UdpSession)

    };

    都是一些与客户端通信的接口。

    我的评论与建议:这样的数据缓冲区的设计,代码执行效率不是很高,而且可能造成服务器缓冲区溢出的错误,应该设计时加入一些防范措施。

    UdpSession的设计不太合理,模块化不高,代码零乱。我到认为这里到可以用插件机制,也就是一个插件来管理部分的通信消息命令。这样可提高程序的可读性,还便于今后通信消息命令的扩展。这点可以学学入侵检测系统Snort的规则处理模块与插件的管理。

     

     


    最新回复(0)