Myicq scenario analysis

zhaozj2021-02-16  52

1. First Scenario: User Registration

1. The first scenario: User registration This scenario mainly used data structure: (1) Icquser let us first look at the ICQUSER code, class icquser: public icqinfo {public: icquser (); void load (dbinstream & in); void save (DBOutStream & out); string passwd; uint8 auth;}; class IcqInfo: public ContactInfo, public DBSerialize {public: IcqInfo (); virtual ~ IcqInfo () {} void save (DBOutStream & out); void load (DBInStream & in) ;}; class ContactInfo {public: QID qid; string nick; uint8 face; uint8 gender; uint8 age; string country; string province; string city; string email; string address; string zipcode; string tel; string name; uint8 blood; string college; string profession; string homepage; string intro; uint32 status;}; class DBSerialize {public: virtual void save (DBOutStream & out) = 0; virtual void load (DBInStream & in) = 0;}; it is obvious that it It is a data structure used to store the basic information of the person. It has two methods Save and Load. Let's take a look at their code, you can think of it is used to place the data into the database and read from the database. DBSerialize is a Abstract class.

Void Icquser :: Load (Dbinstream & IN) {ICQINFO :: Load (in >> passwd >> auth;} void icquser :: save (dboutstream & out) {icqinfo :: save (out); out << passwd < > Face >> Nick >> Age >> Gnder >> Country >> Province >> City; in >> Email >> Address >> Zipcode >> Tel ;> Name >> BLOOD >> College >> Professional >> HomePage >> Intro;} void icqinfo :: save (dboutstream & out) {OUT << Face << Nick << Age << Gender << Country << Province << city; out << Email << address << zipcode << tel; out << name << blood << college << professional << HomePage << Intro;} / ** utility class what reads from Or Writes To a data block * / class dboutstream {public: dboutstream () {cursor = data;} char * getdata () {return data;} int getSize () {return (Cursor - data);} DBOUTSTREAM & OPERATOR << (uint8 b ); DBOutStream & operator << (uint16 w); DBOutStream & operator << (uint32 dw); DBOutStream & operator << (const char * str); DBOutStream & operator << (strList & strList); DBOutStream & operator << (const string & str) { Return Operator << (Str.c_str ());} DBOUTSTREAM & Operator << (const QID & QID) {Return (* this << qid.uin << qid.domain);} private: char data; char * Cursor;}; class dbinstream {public: dbinstream (void * d, int N) {cursor = data = (char *) D; DataLen = N;} Dbinstream & Operator >> (UINT8 & OPERATOR >> (UINT16 & W) DBINSTREAM & OPERATOR >>

(UINT32 & DW); String & Str); Dbinstream & Operator >> (Dbinstream & Operator >> (QID & QID) {Return (* this >> qid.uin >> qid.domain);} private : char * data; char * cursor; int datalen;}; (2) IcqOptionclass IcqOption: public DBSerialize {public: IcqOption (); virtual ~ IcqOption (); void load (DBInStream & in); void save (DBOutStream & out); void playSound (int sound, IcqContact * c = NULL); bitset flags; // System option flagsuint32 hotKey; string soundFiles [NR_SOUNDS]; string host; uint16 port; string autoReplyText; string quickReplyText; uint32 autoStatus; uint32 autoStatusTime; string Skin; // proxy stuffuint8 proxytype; proxyInfo proxy [nr_proxy_types]; private: Bool Plays (const char * file);}; Storage Various options In this scenario, we assume that the user has clicked the next button of the network settings that page . Enter the following code: BOOL CREGFINISHDLG :: OnsetAnd * wiz = (cregwizard *) getParent (); 207 wiz-> getdata (& icqlink-> myinfo, & icqlink-> options); first 206 go to the previous The pointer of the window is the pointer of the property sheet, which in turn calls its function getData.

Enter the code: cregfinishdlg :: OnsetArs () -> CREGWIZARD :: getData () 54 void cregwizard :: getData (icquser * info, icqoption * Options) 55 {56 if (MODEDLG.M_MODE == 0) 57 Info-> QID = QID; 58 else {59 info-> qid.uin = modedlg.m_uin; 60 info-> qid.domain = finishdlg.domain; 61} 62 63 info-> face = BasicDlg.m_pic; 64 info-> agn = BasicDLG .m_age; 65 info-> nick = BasicDlg.m_nick; 66 info-> gender = BasicDlg.m_gender; 67 info-> country = BasicDlg.m_country; 68 info-> province = BasicDLG.M_Province; 69 info-> city = BasicDLG .m_city; 70 71 info-> email = Commdlg.m_email; 72 info-> address = commdlg.m_address; 73 info-> zipcode = commandlg.m_zipcode; 74 info-> tel = commdlg.m_tel; 75 76 info.m_tel; 75 76 info-> name = miscDlg.m_name; 77 info-> blood = miscDlg.m_blood; 78 info-> college = miscDlg.m_college; 79 info-> profession = miscDlg.m_profession; 80 info-> homepage = miscDlg.m_homepage; 81 info-> intro = miscDlg.m_intro; 82 83 info-> passwd = (MODEDLG.M_MODE == 0? Basicdlg.m_passwd: MODEDLG.M_PASSWD); 84 85 if (options) {86 options-> Host = networkDlg.m_host; 87 options-> port = networkDlg.m_port; 88 options-> flags.set (UF_USE_PROXY, networkDlg.m_useProxy); 89 options-> proxyType = networkDlg.m_proxyType; 90 for (int i = 0; i proxy [i] = networkdlg.proxyInfo [i]; 92} 93} This method is incorporated into two structures ICQUSER and ICQOption have explained the 56th line to 61, which is mainly assigned to QID. , Modedlg.m_mode, if the new application is selected, the QQ number is 0, and the QQ number is 1, this scenario is 0, that is, the INFO-> QID = QID; QID is a public variable for ICQWindow QID QID

Class QID {public: qid () {uin = 0;} BOOL OPERATOR == (Const QID & QID) {Return (uin == qid.uin&&! strcasecmp (domain.c_str (), qid.domain.c_str ()) } Bool isadmin () {return (uin == admin_uin && domain.empty ());} char * TOSTRING (); bool parse (const char * qid); uint32 uin; String Domain;}; left code It is assigned according to the Varges of the ICQUSER and ICQOption added to the global variable ICQLINK based on these dialogs. Back to Regfinishdlg :: OnsetAndive () Start, 208 wiz-> qid.uin = 0; 209210 CString str; 211 str.LoadString (IDS_PLEASE_WAIT); 212 SetDlgItemText (IDC_STATUS, str); 213 str.LoadString (IDS_REG_REGISTERING); 214 SetDlgItemText (IDC_STATUS_DETAIL, str); 215 wiz-> SetWizardButtons (PSWIZB_DISABLEDFINISH); 216217 m_faceLabel. start (); 218219 resolveHost (); 220221 return CPropertyPage :: OnSetActive ();} this line MyIcq number assigned to some status setting 0,211-218, 219 into the CRegFinishDlg :: resolveHost () void CRegFinishDlg :: resolveHost () {172 CRegWizard * wiz = (cregwizard *) getParent (); 173 const char * host = wiz-> networkdlg.m_host; 174175 if (wiz-> networkdlg.m_useproxy&&! wiz-> networkdlg.m_proxyResolve) 176 Getudpses Sion () -> Connect (Host, WIZ-> Networkdlg.m_port); 177 else {178 STRUCT IN_ADDR Addr; 179 addr.s_addr = inet_addr (host); // convert XXXX to unsigned integers to addr.s_addr180 IF ( Addr.s_addr! = INADDR_NONE) 181 OnHostFound (AddR); 182 else183 ((CICQDLG *) AFXGETMAINWND ()) -> gethostByName (Host);}}

175 lines see if there is a proxy server in the Network Settings dialog box, this scene is not selected to pass the address selected by the user as a parameter to OnHostFound (AddR); enter onhostfound

void CRegFinishDlg :: onHostFound (struct in_addr & addr) {163 if (addr.s_addr = INADDR_NONE!) {164 CRegWizard * wiz = (CRegWizard *) GetParent (); 165 getUdpSession () -> connect (inet_ntoa (addr), wiz- > networkdlg.m_port); 166} else167 ontimeout ();} At 165 row call function getUpdSession () -> Connect Introduction parameters are the address and port input by the user.

Inline udpsession * getudpsession () {return ICQLINK-> udpsession;

void UdpSession :: connect (const char * host, uint16 port) {090 IcqOption & options = icqLink-> options; 091092 // Connect using proxy093 if (options.flags.test (UF_USE_PROXY)) {094 if (options.proxyType == PROXY_SOCKS) 095 socksProxy.start (options.proxy [PROXY_SOCKS], sock); 096 else if (options.proxyType == PROXY_HTTP) 097 httpProxy.start (host, options.proxy [PROXY_HTTP]);} 099 else {// not Use proxy

100 SockAddr_in addr; 101 socklen_t len ​​= sizeof (addr); 103 Memset (& addr, 0, sizeof (addr)); 104 addr.sin_family = AF_INET; 105 addr.sin_port = htons (port); 106 addr.sin_addr.s_addr = INET_ADDR (HOST); 107 :: Connect (Sock, (SockAddr *) & addr, len); 109 getSockName (Sock, (SockAddr *) & addr, & len; 110 realip = ntohl (addr.sin_addr.s_addr); 112 ICQLINK- > OnConnect (TRUE);

}

First, it is determined that there is no use of the PROXY this scenario in this scenario to connect to the incoming server and port, and store the real IP in Realip, then call ICQLINK-> onConnect (TRUE); this function is one Define function,

The CICQDLG class has rewritten it, so the program enters CICQDLG :: onConnect

Void cicqdlg :: onConnect (Bool Success) {292 ICQWindow * Win = FindWindow (WIN_REG_WIZARD);

Enter FindWindow

ICQWINDOW * ICQLINK :: FindWindow (int Type, QID * QID, UINT32 SEQ) {Ptrlist :: Iterator it; for (it = windowlist.begin (); it! = Windowlist.end (); it) {icqwindow * Win = (ICQWindow *) * it; if (Win-> Type == Type && (! QID || WIN-> QID == * qid) && (SEQ == 0 || Win-> SEQ == SEQ)) Return win;} return null;

Connect the program 293 if (win) {294 (CregWizard *) WIN) -> onConnect (Success); 295 return; 296} ........

In this scenario, Win_REG_WIZARD exists so enters (CregWizard *) WIN) -> OnConnect (Success);

Void onConnect (Bool Success) {finishdlg.onconnect (success);

Void CREGFINISHDLG :: ONCONNECT (BOOL SUCCESS) {148 IF (Success) {149 CREGWIZARD * WIZ = (CREGWIZARD *) getParent (); 150 if (wiz-> modedlg.m_mode == 0) 151 wiz-> seq = getudpsession ) -> regnewuin; 152 else {153 ICQLINK-> Myinfo.qid.uin = wiz-> modedlg.m_uin; wiz-> seq = getudpsession () -> login (wiz-> modedlg. M_passwd, status_offline); 155} 156} else157 ontimeout ();

Modedlg.m_mode, if the new application is selected, the QQ number is 0, and the QQ number is selected is 1. This scenario is 0, so the 151 line.

UINT16 UDPSession :: Regnewuin () @ @ itpacket * out = createpacket (udp_new_uin); 003 * OUT << Passwd; 004 return sendpacket (out);}

First seeing 001 regnewuin is to register a new ID,

Void udpsession :: initsession () {161 sid = (rand () & 0x7fffffff) 1; // Assign the SID initial value as a random number

162 SendSeq = (Rand () & 0x3FFF);

163 retrykeepalive = 0; 164 sessioncount = 0; 165166 MEMSET (Window, 0, Sizeof (Window)); 167168 CLEARSENDQUE (); // Clear Send queue}

Take another 002

(1) udpoutpacket * udpsession :: createpacket {udpoutpacket * p = new udpoutpacket; p-> cmd = cmd; p-> seq = sendseq; createpacket (* p, cmd, sendseq); Return P; } (2) void udpsession :: createpacket (udpoutpacket & out, uint16 cmd, uint16 seq) {OUT << (uint16) myicq_udp_ver << (uint32) 0; OUT << icqlink-> Myinfo.qid.uin << SID << CMD << seq; out << (uint16) 0; // checkcode will be calculated latter}

(3) OUTPACKET & ICQOUTPACKET :: Operator << (uint16 w) {if (Cursor <= DATA MAX_PACKET_SIZE - SIZEOF (W)) {* (uint16 *) CURSOR = HTONS (W); CURSOR = SizeOf (W); } Return (* this);

Inheritance relationship: udpoutpacket-> icqoutpacket-> Outpacket-> packet

CMD = UDP_NEW_UIN, CUSOR and DATA initialization are what do you do? See code:

Class icqoutpacket: public outpacket {public: icqoutpacket () {cursor = data;

...

Protected: char data [MAX_PACKET_SIZE]; CHAR * CURSOR;

(3) Is the first sentence to determine whether the length of CURSOR SIZEOF (W) exceeds max_packet_size if there is no exceeds turn the W to the network byte to assign the current address to the CURSOR.

Then move Cursor's pointer w

The latter sentences in (2) are assigned to DATA. That is to say, ICQ_UDP_VER-0- ICQLINK-> myinfo.qid.uin -sid - cmd -seq-0 is connected to DATA.

Where MyicQ_UPD_VER = 1, ICQLINK-> Myinfo.qid.uin = Applying for a QQ number is 0, selecting the QQ number 1, SID = random number, cmd = udp_new_uin, SEQ = random number

Look at 003: * OUT << Passwd; add Passwd to Data;

Last 004: Sendpacket (OUT)

UINT16 UDPSESSION:: SendPacket (udpoutpacket * p) {// packet virt be encrypted before sending to server1 if (p-> cmd! = udp_new_uin && p-> cmd! = udp_login) 2 p-> encrypt (); 3 P- > attempts = 0; 4 p-> expire = time (null) send_timeout; 5 Senddirect (p); 6 sendqueue.push_back (p); 7 return sendseq;} 1-2 If cmd does not equal to register new ID and login Encrypt.

Expire is the number of times attempts attempting to define timeout, attempts.

Arrive at 5

Void udpsession :: Senddirect (udpoutpacket * out) {if (iCQLINK-> isproxytype (proxy_http)) Senddirect (out, httpproxy.sock); else senddirect (out, sock);}

Whether to use the proxy server if it is not used, use int Sock, which is initialized in the constructor of the UpdSession

Sock = icqsocket :: Createsocket (SOCK_DGRAM, THIS);

int IcqSocket :: createSocket (int type, SocketListener * l) {CMySocket * pSocket = new CMySocket (l); pSocket-> Create (0, type, FD_READ | FD_CONNECT | FD_CLOSE); return addSocket (pSocket);}

(1) CreateSocket first row created a CMYSocket on the heap it inherited in the CasyncSocket class to see its constructor

CMYSOCKET :: CMYSOCKET (SocketListener * L) {listener = L;} SocketListener is a pure virtual function, which is the parent class of UDPSession, which override the virtual function of the SocketListener's OnReceive.

(2) The second line of Createsocket is the Create function, and a socket object is created in SOCK_DGRAM (TCP protocol).

(3) Take a look at the code of the third line

Inline int Addsocket (CasyncSocket * psocket) {int suck = * psocket; sockhash.set (sock, psocket); Return Sock;

Static CMAP sockhash

Void udpsession :: Senddirect (udpoutpacket * out, int s) {203 char buf [MAX_PACKET_SIZE 256]; 204 CHAR * P = BUF; 205 INT N = OUT-> getSize (); 206207 icqoption & options = ICQLINK-> Options; 208209 IF (options.flags.test (uf_use_proxy)) {210 switch (options.proxytype) {211 case proxy_http: 212 * (uint16 *) p = htons (n); 213 p = Sizeof (uint16); 214 Break; 215216 case proxy_socks: 217 * (uint16 *) p = 0; 218 p = sizeof (uint16); 219 * p = 0; 220 if (options.Proxy [proxy_socks] .resolve) {221 // ipv4222 * p = 1 ; 223 * (uint32 *) P = destip; 224 p = SizeOf (uint32); 225} else {226 // Domain Name227 * p = 3; 228 string & domain = ICQLINK-> Myinfo.qid.domain; 229 uint8 LEN = Domain.Length (); 230 * p = le; 231 Memcpy (p, domain.c_str (); 232 p = le; 233} 234 * (uint16 *) P = htons (options.port); 235 p = sizeof (uint16); 236 Break; 237} 238} 239 Memcpy (p, out-> getdata (), n); 240 p = n; 241 Send (S, BUF, P - BUF, 0);} This function is mainly transmitted by send () () Updsesson :: data) To the server, this scenario is also the information filled in by the user.

From now on, the server code will be entered until the server returns to this. Look at the server code:

This function is in main.cpp

INT Main (int Argc, char * argv []) {66 WSADATA WSADATA; 67 Word Version = MakeWord (2, 2); 68 IF (WsaStartup (Version, & Wsadata)! = 0) 69 Return 1; 7071 Initargs (Argc, Argv); 7273 #ifndef _debug74 if (! startservice ()) 75 # Endif76 {77 // We area NOT IN Service Mode78 IF (MyicQStart ()) {79 Handlepacket (); 80 myicqdestroy (); 81} 82} 8384 WSacleanup (); 85 return 0;} 66 lines to 70 lines of the initial Winsock, version 2.2.71 Row of INITARGS (Argc, Argv) processing parameters are mainly in Windows, add myicq to the service, automatically run when the system starts . To 78 lines we look at MyicQStart's code (main.cpp), main () -> myicqstart ()

Bool myicqstart () {45 if (! myicqinit ()) 46 return false; enter MyicQinit (Myicq.cpp), Main () -> myicqstart () -> myicqinit ()

bool myicqInit () {187 srand (time (& curTime)); 188189 parseProfile (CONFIG_FILE, parseConfig); 190191 Log :: open (_ops.logFile.c_str (), _ops.logLevel); 192193 loadPlugins (); 194195 // Initialize Database subsystem196 if (! dbmanager :: init (_ops.dbinfo)) {197 log (1) ("Cannot connect to mysql master / n"); 198 return false; 199} 200 for (int i = 0; i

189 line Parseprofile is the main function of the function is to store the contents of each section from the file specified by config_file.

struct OPTIONS {// network uint32 myicqIP; uint16 myicqPort; // log int logLevel; string logFile; // mysql DB_INFO dbInfo; int enableRegister; int enableS2S; // server string domain; string desc; // plugins string pluginDir;}; 191 Open the log file, the 193 loading the plug-in module. 196-199 (mysql provided) and connected by calling mysql_init mysql_real_connect; mysql initialization assigned mysqlWrite, parameters _ops.dbInfo, 200-205 mysql_init call is connected to initialize mysql_real_connect;. Static DBManager dbQuery [NR_DB_QUERY]; structure MYICQ * mysqlread; 207-208 Initialization UPD service Updession :: init () (Updession.cpp)

Main () -> myicqstart () -> myicqinit () -> Updession :: init ()

bool UdpSession :: init () {696 sockaddr_in addr; 697698 qidAdmin.uin = ADMIN_UIN; 699 qidAdmin.domain = emptyStr; 700701 sock = socket (AF_INET, SOCK_DGRAM, 0); 702 if (sock <0) {703 LOG (1 ("Socket () failed / n"); 704 goto failed; 705} 706707 Memset; 708 addr.sin_family = AF_INET; 709 Addr.sin_addr.s_addr = _Ops.myicqip; 710 Addr.sin_Port = _Ops.myicqport; 711 IF (Bind (Sock, (SockAddr *) & addr, sizeof (addr)) <0) {712 log (1) ("bind (): can not bind ON% S:% D / N ", 713 inet_ntoa (addr.sin_addr), ntohs (_OPS.MYICQPORT)); 714 goto failed; 715} 716717 Desinit (0); 718 return; 719720 failed: 721 if (Sock> = 0) 722 Close Sock); 723 Return False;}

This function is some of the initialization of Socket Socket Initialization Process Socket () --- Bind () --- Listen () Note: There is no listen () in this function (no connection) UDP protocol

Server :: init () mainly is initialization (connected) TCP protocol, then return to myicqstart to see code:

48 DWORD ID; 49 INT I; 5051 // Creating Threads ... 52 Createthread (Null, 0, PulseThread, Null, 0, & ID); 5354 // DNS55 Createthread (NULL, 0, DNSTHREAD, NULL, 0, & ID) ; 5657 CreateThread (NULL, 0, DBUPDATHREAD, NULL, 0, & ID); 58 for (i = 0; i

Void Pulse () {for (;;) {TIME ((Time_t *) & Curtime); Sleep (1);}}

Static DWORD WINAPI DNSTHREAD (LPVOID) {HandledNS (); Return 0;}

Void handledns () {DNSManager :: process ();

Static DWORD WINAPI DBUPDATTHREAD (LPVOID) {handledbupdate (); return 0;}

Void handledbupdate () {dbmanager :: processUpdate ();}

Let's take a look down! Back to Main left

79 Handlepacket (); 80 myicqdestroy (); 81} 82} 8384 wsacleanup (); 85 RETURN 0;

}

The most important thing is Handlepacket (), see code:

void handlePacket () {240 int sock = UdpSession :: sock; 241242 for (;;) {243 fd_set readfds; 244 fd_set writefds; 245246 FD_ZERO (& readfds); 247 FD_ZERO (& writefds); 248249 FD_SET (sock, & readfds); 250 INT MAXFD = SOCK; 251252 if (_OPS.Enables2s) {253 INT N = Server :: generatefds (readfds, writefds); 254 if (n> maxfd) 255 Maxfd = n; 256} 257258 TimeVal To; 259 TO.TV_SEC = 1; 260 TO.TV_USEC = 0; 261262 INT N = SELECT (MAXFD 1, & ReadFDS, & Writefds, Null, & to); 263264 if (n> 0) {265 IF (FD_Isset (Sock, & ReadFDS) 266 UDPSession :: OnRecEive (); 267 This function uses the SELECT model in Winsock. It is called UDPSession :: OnReceive () to process UDPSESSION :: OnRecEive () for non-blocking status, if you find that there is un read, we will continue to process, we continue,

Handlepacket () -> UDPSession :: OnRecEive

bool UdpSession :: onReceive () {852 char buf [UDP_PACKET_SIZE]; 853 sockaddr_in addr; 854 socklen_t len ​​= sizeof (addr); 855 int n = recvfrom (sock, buf, UDP_PACKET_SIZE, 0, (sockaddr *) & addr, & len) ; 856857 if (n <(int) sizeof (udp_cli_hdr)) 858 Return False; 859860 udpinpacket in (BUF, N); 861

855-858 After receiving the character from the specified SOCK, create a UDPACKET object, and its inheritance relationship is udpinpacket-> icqinpacket-> inpacket, the constructor is as follows

ICQINPACKET (CHAR * D, INT N) {CURSOR = DATA = (uint8 *) D; DATALEN = N;

UDPINPACKET :: UDPINPACKET (CHAR * D, INT N): ICQINPACKET (D, N) {* this >> Header.Ver >> Header.reserved; * this >> Header.uin >> Header.SID >> Header.cmd ; * This >> Header.seq >> Header.cc;}

UDP_CLI_HDR HEADER;

Struct UDP_CLI_HDR {uint16 Ver; uint32 reserved; uint32 uin; uint32 sid; uint16 cmd; uint16 seq; uint16 cc; // check code}; inpacket & icqinpacket :: Operator >> (uint8 & b) {b = 0; IF (Cursor < = DATA DATALEN - SIZEOF (B)) B = * CURSOR ; return * this;} Inpacket & icqinpacket :: Operator >> (uint16 & w) {w = ntoHS (READ16 ()); return * this;} Inpacket & icqinPacket :: Operator >> (UINT32 & DW) {dw = ntohl (READ32 ()); return * this;} Inpacket & icqinPacket :: Operator >> (ICQ_STR & STR) {uint16 g; iperator >> (LEN); if (Len && Cursor < = DATA DATALEN - LEN &&! CURSOR [LEN - 1]) {str.text = (char *) CURSOR; str.len = len - 1; cursor = le;} else {str.text = ""; STR .le = 0;} return * this;

UINT16 ICQINPACKET :: READ16 () {uint16 w = 0; if (Cursor <= DATA DATALEN - SIZEOF (W)) {w = * (uint16 *) Cursor; Cursor = SizeOf (w);} return w;} UINT32 ICQINPACKET :: READ32 () {uint32 dw = 0; if (Cursor <= DATA DATALEN - SIZEOF (DW) {dw = * (uint32 *) Cursor; Cursor = SizeOf (DW);} Return Dw;}

The most meaning of the above function is to convert the read data from the network byte to the host byte to the host byte by the operator overload.

Let us continue udpsession :: onreceive ()

862 uint32 ip = addr.sin_addr.s_addr; 863 uint16 port = addr.sin_port; 864 udpsession * s = sessionhash :: get (ip, port); 865866 if (! S) {867 uint16 cmd = in. HEADER.CMD; 868 IF (cmd == udp_new_uin || cmd == udp_login) {869 s = New UDPSession (in, IP, port); 870871 // add it to the hash872 sessionhash :: PUT (S, IP, port); 873 KeepaliveList .add (& S-> ListItem); 874} else875 s = sessionhash :: get (in.Header.uin); 876} 877 if (s) 878 S-> onpacketReceived (in); 879880 return;} 862 and 863 Take the client's IP and Port, see 864 Find the IP in the queue IpportItem in the member variable of UDPSession, then return to a UDPSession

866-876 If there is no words in the queue, and a new id or login is newly created newly created a new UDPSession and connect it into the BUCKET queue with ipportItem, then use ListItem

Link into the KeePaliveList queue. Turn on OnPacketReceived, this function is a Swith and CASE used to handle the jump function of various commands for UPD_New_uin is Onnewuin (in); see Onnewuin

Handlepacket () -> udpsession :: onreceive () -> udpsession :: onnewuin

void UdpSession :: onNewUIN (UdpInPacket & in) {1314 if (_ops.enableRegister) {1315 ICQ_STR passwd; 1316 in >> passwd; 1317 DBRequest * req = new DBRequest (DBF_UPDATE | DBF_INSERT, newUserCB, this, in.header.seq) 1318 Write_Str (Req, "Insert Into Basic_TBL (Passwd, MSG_ID) Select Password ("); 1319 * Req << Passwd; 1320 Write_STR (Req, "), Max (ID) from bcmsg_tbl"); 1321 dbmanager :: query (REQ); 1322} else {1323 udpoutpacket * out = createpacket (udp_new_uin, in. HEADER.SEQ); 1324 * OUT << (uint32) 0; 1325 SendPacket (OUT); 1326} 1327 ISDEAD = 1;}

If you have registered now, you will create the password in I to the PASSWD, and the 1317 line will create a new DBREQUEST object.

DBREQUEST :: DBREQUEST (uint8 f, db_callback cb, refobject * obj, uint32 d) {flags = f; callback = cb; refobj = obj; data = d; res = null; cursor = sql;} Note: Add SQL address Gave Cursor.1318 as follows:

#define Write_Str (Req, Str) Req-> WriteString (STR, SIZEOF (STR) - 1)

Void Writestring (const char * text, int N)

{IF (CURSOR - SQL N <= max_sql)

{Memcpy (CURSOR, TEXT, N); CURSOR = N;}}

The SQL statements consisting of 1318-1320 are as follows:

INSERT INTO BASIC_TBL (Passwd, MSG_ID) SELECT Password ("Your Password", Max (ID) from bcmsg_tbl;

Then go to 1321:

Handlepacket () -> udpsession :: onreceive () -> udpsession :: onnewuin-> dbmanager :: query

Void DBManager :: Query (DBREQUEST * REQ) {if (Req-> Callback && Req-> Refobj) Req-> refobj-> addref (); Queue & Q = ((Req-> Flags & DBF_UPDATE)? UPDATEQUE: queryueue) Q.PUT (& Req-> ListItem);

According to the type of SQL statement (Update, INSERT ...) is determined by UPDATEQEUE or queryueue queue, this situation uses queryqueue, put this UDPSession ListItem

In the QueryQueue queue, wait for the thread DBQuerythread processing, then return to the 268th line of Handlepacket ()

268 IF (_OPS.Enables2s) 269 Server :: Examinefds (ReadFDs, Writefds); 270271} else if (n <0) {272 if (errno == EINTR) 273 Continue; 274275 log (1) ("SELECT () failed / n "); 276 break; 277} 278279 DBManager :: dispatch (); 280 DNSManager :: dispatch (); 281282 static time_t lastTime;! 283 if (curTime = lastTime) {284 lastTime = curTime; 285 checkTimeOuts (); 286} 287}} 168-270 is used for TCP / IP even slightly, to 279

Handlepacket () -> dbmanager :: dispatch ()

void DBManager :: dispatch () {while (resultQueue.isEmpty ()!) {ListHead * pos = resultQueue.get (); DBRequest * req = LIST_ENTRY (pos, DBRequest, listItem); RefObject * obj = req-> refObj; MySQL_RES * RES = Req-> Res; Req-> Callback (REQ); if (obj) Obj-> release (); if (! (! (! (! (! (! (! (! (! (! (! (!) MSQL_FREE_RESULT (RES); Delete Req; }} DBQuerythread threads will be put into the ResultQueue after processing, and the callback is a function pointer to the newusercb that has just been established.

Static void newusercb (dbrequest * req) {udpsession * session = (udpsession *) Req-> refobj; udpoutpacket * out = session-> createpacket (udp_new_uin, req-> data);

UINT32 UIN = 0; if (Req-> RET == 0) {uin = Req-> LastInsertID; dbrequest * Req = new dbrequest (dbf_update); Write_Str (Req, "Insert Into ext_tbl (uin) value ("); * REQ << uin << ')'; dbmanager :: query (req);} session-> uin = uin;

LOG (2) ("% lu registered / n", uin);

* OUT << uin; * OUT << _Ops.domain.c_str (); session-> sendpacket (out);} It can be seen that it is a bit different from the SendPacket and the client.

Void udpsession :: SendPacket (udpoutpacket * p) {p-> attempts = 0; p-> expire = curtime send_timeout; senddirect (p); 1 sendqueue.add (& P-> sendItem); 2 Globalsendqueue.Add (& P- > globalsenditem;

Inline void udpsession :: Senddirect (udpoutpacket * p) {p-> send (sock, ip, port);

Int UDPOUTPACKET :: Send (int suck, uint32 ip, uint16 port) {sockaddr_in addr; // is it Neccesary? // MEMSET (addr, 0, sizeof (addr)); addr.sin_family = Af_Inet; addr.sin_addr.s_addr = IP; addr.sin_port = port; return sendto (sock, (char *) DATA, CURSOR - DATA, 0, (SOCKADDR *) & addr, sizeof (addr));} These several function calls are to Sendto to the client Send back information. 1 and 2 Putting Function is to link SendItem, GlobalSendItem into SendQueue and GlobalsendQueue queues.

The previous process is just started when the process is started, and the DBQuerythread is used to handle QueryQueue to the column code:

Static DWORD WINAPI DBQUERYTHREAD (LPVOID I) {HandledbQuery ((int) i); return 0;}

Void HandledbQuery (INT i) {dbquery [i] .processQuery ();

Static dbmanager dbquery [NR_DB_QUERY];

Void dbmanager :: processQuery () {for (;;) {dbrequest * Req = getdbrequest (queryqueue); if (mysql_real_query (mysqlread, req-> sql, req-> sqllen ()) == 0) Req-> Res = MySQL_Store_Result (mysqlread); PutdbRequest (Req);}}

Inline DBREQUEST * GETDBREQUEST (Queue & q) {Listhead * POS = Q.Get (); Return List_ENTRY (POS, DBREQUEST, LISTITEM);

MySQL_Real_Query is the API function provided by MySQL. The function is to execute the SQL statement to return 0 indicates that the success is not 0.

MySQL_STORE_RESULT is the API function provided by MySQL, the function returns the result set, then looks to PutdbRequest (Req);

Inline void Putdbrequest (DBREQUEST * REQ) {if (Req-> Callback) Resultqueue.put (& Req-> ListIn); Else Delete Req;}

Put in the ResultQueue queue.

This way the server is sent again to the client, look at it tomorrow! (2004.03.17)

2004.0.23 No! There are five days without seeing it! Go back to the client

UINT16 UDPSESSION:: SendPacket (udpoutpacket * p) {// packet virt be encrypted before sending to server1 if (p-> cmd! = udp_new_uin && p-> cmd! = udp_login) 2 p-> encrypt (); 3 P- > attempts = 0; 4 p-> expire = time (null) send_timeout; 5 Senddirect (p); 6 sendqueue.push_back (p); 7 return sendseq;} After executing the 5th line, look at the 6th line : Ptrlist sendqueue; define typedef list ptrlist in ICQTypes.h;

List is a class of standard template libraries. Push_back is in the queue places P in SendQueue: Then return, assign the value? CregWizard * Wiz's SEQ member variable.

The client code has created a CMYSocket object here, which is to send it to the server side. The server sends a packet to this socket, then it's

The onRecEive function will be called, see its code

Void CMYSocket :: OnRecEive (int Nerror) {

Listener-> onRecEcept;

}

SocketListener * listener; because socketListener is a pure virtual class, see the constructor of Updsession in front,

SOCK = ICQSocket :: Createsocket (SOCK_DGRAM, this); So call Updession :: OnRecEive ()

bool UdpSession :: onReceive () {char data [MAX_PACKET_SIZE]; char * p = data; sockaddr_in addr; socklen_t addrlen = sizeof (addr); // Receive data from udp socket int n = recvfrom (sock, data, sizeof (data ), 0, (sockaddr *); if (n <0) Return false; if (ICQLINK-> isproxytype (proxy_socks)) {if (Data [0]! = 0 || Data [1]! = 0 || DATA [2]! = 0 || Data [3]! = 1) Return False; P = 10; N - = 10;} udpinpacket in (p, n); return onpacketRecEived (in);}

2004.03.25

Create a UDPinPacket In (P, N) after reading data from the network buffer;

UDPINPACKET :: UDPINPACKET (Const Char * D, INT LEN): ICQINPACKET (D, LEN) {* this >> Header.Ver >> Header.reserved; * this >> Header.uin >> Header.SID >> Header. CMD; * this >> Header.seq >> Header.ackseq;} This function and the server are almost mainly to store the received data into the header, and onPacketReceived (in) is made after the new ID is called ONNEWUINREPLY. () Let us see the code

Void Udpsession :: OnNewUinReply (udpinpacket & in) {qid qid; in >> qid.uin >> qid.domain; icqlink-> OnnewuinReply (QID);

OnnewuinReply is a pure virtual function, and I have seen it in front of CICQDLG is a subclass of ICQLINK, so it is called.

Void Cicqdlg :: OnNewuinReply (QID & QID) {MyInfo.qID = QID; ICQWindow * Win = FindWindow (win_reg_wizard); if (win) (CREGWIZARD *) WIN -> OnnewuinReply (QID);}

Call it again

Void OnnewuinReply (QID & QID) {finishdlg.onnewuinReply (QID);

Adjusted

void CRegFinishDlg :: onNewUINReply (QID & qid) {CRegWizard * wiz = (CRegWizard *) GetParent (); wiz-> qid = qid; CString str; if (qid.uin) {001 wiz-> isFinished = TRUE; str.LoadString (IDS_FINISHED); SetDlgItemText (IDC_STATUS, str); str.Format (IDS_REG_SUCCESS, qid.toString ()); SetDlgItemText (IDC_STATUS_DETAIL, str); wiz-> SetWizardButtons (PSWIZB_FINISH); 007 wiz-> GetDlgItem (IDCANCEL) -> EnableWindow (FALSE);} else {str.LoadString (IDS_FAILED); SetDlgItemText (IDC_STATUS, str); str.LoadString (IDS_REG_FAILED); SetDlgItemText (IDC_STATUS_DETAIL, str);} m_faceLabel.stop ();}

Finally, I entered the territory of the MFC, handled some of the UI's stuff, 001-007 sets the failure of flashing, set a few strings, and enable the completion button. Then one layer is returned and then called and then calls

Updession :: SendackPacket (SEQ); a response to the server. Then the client stops waiting for the user to press the completion button. After the user is pressed, the CPROPERTYPAGE is triggered. This object does not use to rewrite, so the layer is returned to the main window, and OK will later.

转载请注明原文地址:https://www.9cbs.com/read-25615.html

New Post(0)