1//函数名称:IOThreadProc
  2//函数功能:消息处理的线程
  3//处理对象:核心函数
  4//研究人员:长寿梦 
  5DWORD WINAPI CP2PServer::IOThreadProc(LPVOID  lpParam)
  6{
  7    CP2PServer *pThis=(CP2PServer *)lpParam;    
  8    char buff[MAX_PACKET_SIZE];
  9    CP2PMessage *pMsg=(CP2PMessage *)buff;
 10    sockaddr_in remoteAddr;
 11    //
 12    WSABUF wsaBuf;
 13    wsaBuf.buf=buff;
 14    wsaBuf.len=MAX_PACKET_SIZE;
 15    //
 16    DWORD dwRecv;
 17    DWORD dwFlags=0;
 18    int nAddrLen=sizeof(remoteAddr);
 19    
 20    _RecordsetPtr m_record;     //记录集, 记录ADO查询的数据
 21    _bstr_t vSQL;    //SQL语句
 22    CString strNo,strName,strPhone,strText,strLog,strTime;
 23    CString strPassword,strIp,strPort,strOnline;
 24    COleDateTime time;
 25
 26    while(TRUE)
 27    {    //    接收信息到wsaBuf
 28        int nRet=::WSARecvFrom(pThis->g_s,&wsaBuf,1,&dwRecv,&dwFlags,(sockaddr *)&remoteAddr,&nAddrLen,&pThis->g_ol,NULL);
 29
 30        if(nRet==SOCKET_ERROR) 
 31        {
 32            int nError=::WSAGetLastError();
 33            if(nError==WSA_IO_PENDING)
 34            {   
 35                //    TRACE0("没接到消息,正在等待。。。重叠接收信息\n");
 36                ::WSAGetOverlappedResult(pThis->g_s,&pThis->g_ol,&dwRecv,TRUE,&dwFlags);    
 37            }

 38            //else if()
 39        }

 40        //查看是否要退出
 41        if(pThis->g_bThreadExit) 
 42        break;
 43        if(dwRecv<sizeof(CP2PMessage))
 44            continue;
 45        //消息循环:总共处理15个消息
 46        switch(pMsg->nMessageType)
 47        {   
 48            //[1/15]用户登陆消息//////////////////////////////////////////////////////////////
 49            case USERLOGIN: 
 50                {
 51                    strNo=pMsg->peer.ImNo;
 52                    strPassword=(char*)(pMsg+1);
 53                    //写到日志列表中
 54                    time=COleDateTime::GetCurrentTime();
 55                    strTime=time.Format("%H:%M:%S ");
 56                    strLog=strTime+strNo+" 请求登陆";
 57                    ::PostMessage(g_pPage3->m_hWnd,WM_NEWLOG,(WPARAM)(LPCTSTR)strLog,0);
 58                     
 59                    vSQL="select * from imuser where ImNo='"+strNo+"' and Impassword='"+strPassword+"' and ImOnline=0";
 60                    m_record=pThis->ado.GetRecordSet(vSQL);
 61                    //如果登录成功,则返回自己信息,记录登录地址,接收离线消息,接收离线好友请求,设置为在线状态,广播这状态变化
 62                    if(!m_record->adoEOF)
 63                    {  
 64                        //写到日志列表中
 65                        time=COleDateTime::GetCurrentTime();
 66                        strTime=time.Format("%H:%M:%S ");
 67                        strLog=strTime+strNo+" 登陆成功";
 68                        ::PostMessage(g_pPage3->m_hWnd,WM_NEWLOG,(WPARAM)(LPCTSTR)strLog,0);
 69
 70                        strName=(LPCTSTR)(_bstr_t)m_record->GetCollect("ImName");
 71                        strPhone=(LPCTSTR)(_bstr_t)m_record->GetCollect("ImPhone");
 72                
 73                        strName.TrimRight(" ");
 74                        strPhone.TrimRight(" ");
 75                        
 76                        //登陆成功,并返回用户自己的信息
 77                        pMsg->nMessageType=USERLOGACK;
 78                        strcpy((char*)pMsg->peer.ImNo,(LPCSTR)(LPCTSTR)strNo);
 79                        strcpy((char*)pMsg->peer.ImName,(LPCSTR)(LPCTSTR)strName);
 80                        strcpy((char*)pMsg->peer.ImPhone,(LPCSTR)(LPCTSTR)strPhone);
 81                        ::sendto(pThis->g_s,(char *)pMsg,sizeof(CP2PMessage),0,(sockaddr*)&remoteAddr,sizeof(remoteAddr));
 82                        //将IP,状态写入数据库   inet_addr,inet_ntoa,
 83                        strIp=::inet_ntoa(remoteAddr.sin_addr);                 //将IP转成字符串
 84                        strPort.Format("%d",ntohs(remoteAddr.sin_port));        //将端口转成字符串,先要转化成本地字节序
 85                        vSQL="update imuser set ImOnline='1',ImIp='"+strIp+"',ImPort='"+strPort+"' where ImNo='"+strNo+"'";
 86                        pThis->ado.ExecuteSQL(vSQL);
 87                        //设置服务器该用户在线状态
 88                        strText=strName+"("+strNo+")";
 89                        ::PostMessage(g_pPage1->m_hWnd,WM_USERSTATIC_UPDATE,(WPARAM)(LPCTSTR)strText,(LPARAM)(LPCTSTR)"Login");
 90
 91                        //发送离线的消息:对表[OfflineMessage]的操作
 92                        vSQL="select * from OfflineMessage where ImNo='"+strNo+"'";  
 93                        m_record=pThis->ado.GetRecordSet(vSQL);
 94                        ::Sleep(500);        /////////////////////
 95                        char *pBuf = NULL;
 96                        char sendBuf[MAX_PACKET_SIZE];
 97                        while(!m_record->adoEOF)
 98                        {
 99                            long lDataSize = m_record->GetFields()->GetItem("ImMsg")->ActualSize;///得到数据的长度
100                            if(lDataSize > 0)
101                            {
102                               _variant_t varBLOB;
103                               varBLOB = m_record->GetFields()->GetItem("ImMsg")->Value;
104                               if(varBLOB.vt == (VT_ARRAY | VT_UI1)) ///判断数据类型是否正确
105                               {
106                                    SafeArrayAccessData(varBLOB.parray,(void **)&pBuf);///得到指向数据的指针
107                                    /*****在这里我们可以对pBuf中的数据进行处理*****/            
108                                    memset(sendBuf,0,MAX_PACKET_SIZE);
109                                    memcpy(sendBuf,pBuf,lDataSize);
110                                    SafeArrayUnaccessData (varBLOB.parray);
111                                    CP2PMessage *pM=(CP2PMessage *)sendBuf;
112                                    pM->nMessageType=P2PMESSAGE;
113                                    CString sss=(char *)(pM+1)+sizeof(MessageInfo);
114                                    //AfxMessageBox(sss);
115                                    ::sendto(pThis->g_s,(char *)sendBuf,lDataSize,0,(sockaddr*)&remoteAddr,sizeof(remoteAddr));
116
117                               }

118                            }

119                            m_record->MoveNext();
120                        }

121                        //删除该用户的离线好友请求
122                        vSQL="delete from OfflineMessage where ImNo='"+strNo+"'";
123                        pThis->ado.ExecuteSQL(vSQL);
124
125                        //发送离线好友请求的消息:对表[OffAddfriend]的操作
126                        vSQL="select * from OffAddfriend where ImNo='"+strNo+"'";  
127                        m_record=pThis->ado.GetRecordSet(vSQL);
128                        ::Sleep(500);        /////////////////////
129                        //char *pBuf = NULL;
130                        //char sendBuf[sizeof(CP2PMessage)+sizeof(NewFriendMessage)];
131                        while(!m_record->adoEOF)
132                        {
133                            long lDataSize = m_record->GetFields()->GetItem("ImInfo")->ActualSize;///得到数据的长度
134                            if(lDataSize > 0)
135                            {
136                               _variant_t varBLOB;
137                               varBLOB = m_record->GetFields()->GetItem("ImInfo")->Value;
138                               if(varBLOB.vt == (VT_ARRAY | VT_UI1))                                 ///判断数据类型是否正确
139                               {
140                                    SafeArrayAccessData(varBLOB.parray,(void **)&pBuf);              ///得到指向数据的指针
141                                    /*****在这里我们可以对pBuf中的数据进行处理*****/            
142                                    memset(sendBuf,0,MAX_PACKET_SIZE);
143                                    memcpy(sendBuf,pBuf,lDataSize);
144                                    SafeArrayUnaccessData (varBLOB.parray);        
145                                    ::sendto(pThis->g_s,(char *)sendBuf,lDataSize,0,(sockaddr*)&remoteAddr,sizeof(remoteAddr));
146
147                               }

148                            }

149                            m_record->MoveNext();
150                        }

151                        //删除该用户的离线好友请求
152                        vSQL="delete from OffAddfriend where ImNo='"+strNo+"'";
153                        pThis->ado.ExecuteSQL(vSQL);
154
155                        //给所有用户发送消息,该用户状态发生了变化
156                        vSQL="select ImIp,ImPort,ImOnline from imuser where ImNo!='"+strNo+"'and ImOnline=1";  //只要IP地址和端口号
157                        m_record=pThis->ado.GetRecordSet(vSQL);
158                        while(!m_record->adoEOF)
159                        {
160                            //保存状态改变的用户信息(登陆的用户)
161                            CP2PMessage peerMsg={0};
162                            peerMsg.nMessageType=CHANGESTATE;    //改变状态消息
163                            strcpy((char*)peerMsg.peer.ImNo,(LPCSTR)(LPCTSTR)strNo);
164                            strcpy((char*)peerMsg.peer.ImName,(LPCSTR)(LPCTSTR)strName);
165                            peerMsg.peer.ImOnline=1;
166
167                            //要发送对象的地址                        
168                            strIp=(LPCTSTR)(_bstr_t)m_record->GetCollect("ImIp");
169                            strPort=(LPCTSTR)(_bstr_t)m_record->GetCollect("ImPort");
170                            strOnline=(LPCTSTR)(_bstr_t)m_record->GetCollect("ImOnline");
171                            strIp.TrimRight(" ");
172                            strPort.TrimRight(" ");
173                            strOnline.TrimRight(" ");
174                            
175                            remoteAddr.sin_addr.S_un.S_addr=inet_addr(strIp);
176                            remoteAddr.sin_port=htons(atoi(strPort));
177                            if(strOnline=="1" && strPort!="0")
178                            ::sendto(pThis->g_s,(char *)&peerMsg,sizeof(peerMsg),0,(sockaddr *)&remoteAddr,sizeof(remoteAddr));
179                            m_record->MoveNext();
180                        }

181                        
182                    }
 
183                    //如果登录失败
184                    else
185                    {
186                        //写到日志列表中
187                        time=COleDateTime::GetCurrentTime();
188                        strTime=time.Format("%H:%M:%S ");
189                        strLog=strTime+strNo+" 登陆失败";
190                        ::PostMessage(g_pPage3->m_hWnd,WM_NEWLOG,(WPARAM)(LPCTSTR)strLog,0);
191                    }

192                }

193                break;
194
195            //[2/15]注销///////////////////////////////////////////////////////////////////////////////
196            case USERLOGOUT:
197                {
198                    strNo=pMsg->peer.ImNo;
199                    strName=pMsg->peer.ImName;
200                    //写到日志列表中
201                    time=COleDateTime::GetCurrentTime();
202                    strTime=time.Format("%H:%M:%S ");
203                    strLog=strTime+strNo+" 退出";
204                    ::PostMessage(g_pPage3->m_hWnd,WM_NEWLOG,(WPARAM)(LPCTSTR)strLog,0);
205
206                    vSQL="update imuser set ImOnline='0',ImPort='0' where ImNo='"+strNo+"'";
207                    pThis->ado.ExecuteSQL(vSQL);
208                    //离线状态
209                    strText=strName+"("+strNo+")";
210                    ::PostMessage(g_pPage1->m_hWnd,WM_USERSTATIC_UPDATE,(WPARAM)(LPCTSTR)strText,(LPARAM)(LPCTSTR)"Logout");
211                    
212                    //给所有用户发送消息,该用户状态发生了变化
213                    vSQL="select ImIp,ImPort,ImOnline from imuser where ImNo!='"+strNo+"'and ImOnline=1";  //只要IP地址和端口号
214                    m_record=pThis->ado.GetRecordSet(vSQL);
215                    while(!m_record->adoEOF)
216                    {
217                        //保存状态改变的用户信息(登陆的用户)
218                        CP2PMessage peerMsg={0};
219                        peerMsg.nMessageType=CHANGESTATE;    //改变状态消息
220                        strcpy((char*)peerMsg.peer.ImNo,(LPCSTR)(LPCTSTR)strNo);
221                        strcpy((char*)peerMsg.peer.ImName,(LPCSTR)(LPCTSTR)strName);
222                        peerMsg.peer.ImOnline=0;
223                        //要发送对象的地址
224                    
225                        strIp=(LPCTSTR)(_bstr_t)m_record->GetCollect("ImIp");
226                        strPort=(LPCTSTR)(_bstr_t)m_record->GetCollect("ImPort");
227                        strOnline=(LPCTSTR)(_bstr_t)m_record->GetCollect("ImOnline");
228                        strIp.TrimRight(" ");
229                        strPort.TrimRight(" ");
230                        strOnline.TrimRight(" ");
231
232                        
233                        remoteAddr.sin_addr.S_un.S_addr=inet_addr(strIp);
234                        remoteAddr.sin_port=htons(atoi(strPort));
235                        if(strOnline=="1" && strPort!="0")
236                            ::sendto(pThis->g_s,(char *)&peerMsg,sizeof(peerMsg),0,(sockaddr *)&remoteAddr,sizeof(remoteAddr));
237                        m_record->MoveNext();
238                    }

239                
240
241                }

242                break;
243
244
245            //[3/15]获取通讯录列表/////////////////////////////////////////////////////////////
246            case GETUSERLIST:   
247                {
248                    int Online=0;
249                    strNo=pMsg->peer.ImNo;
250                    //写到日志列表中
251                    time=COleDateTime::GetCurrentTime();
252                    strTime=time.Format("%H:%M:%S ");
253                    strLog=strTime+strNo+" 获取通讯录列表";
254                    ::PostMessage(g_pPage3->m_hWnd,WM_NEWLOG,(WPARAM)(LPCTSTR)strLog,0);
255                    //
256                    vSQL="select ImNo,ImName,ImOnline from imuser where ImNo!='"+strNo+"'";
257                    m_record=pThis->ado.GetRecordSet(vSQL);
258                    while(!m_record->adoEOF)
259                    {
260                        CP2PMessage peerMsg={0};
261                        strNo=(LPCTSTR)(_bstr_t)m_record->GetCollect("ImNo");
262                        strName=(LPCTSTR)(_bstr_t)m_record->GetCollect("ImName");
263                        Online=atoi((LPCTSTR)(_bstr_t)m_record->GetCollect("ImOnline"));
264                        //去掉空格
265                        strNo.TrimRight(" ");
266                        strName.TrimRight(" ");
267                        strcpy((char*)peerMsg.peer.ImNo,(LPCSTR)(LPCTSTR)strNo);
268                        strcpy((char*)peerMsg.peer.ImName,(LPCSTR)(LPCTSTR)strName);
269                        peerMsg.peer.ImOnline=Online;
270                        peerMsg.nMessageType=GETUSERLISTACK;
271                        ::sendto(pThis->g_s,(char *)&peerMsg,sizeof(peerMsg),0,(sockaddr *)&remoteAddr,sizeof(remoteAddr));
272                        m_record->MoveNext();
273                    }

274                    
275                }

276                break;
277
278            //[4/15]获取好友列表/////////////////////////////////////////////////////////////////////////
279            case GETFRIENDLIST:
280                {
281                    int Online=0;
282                    strNo=pMsg->peer.ImNo;
283                    //写到日志列表中
284                    time=COleDateTime::GetCurrentTime();
285                    strTime=time.Format("%H:%M:%S ");
286                    strLog=strTime+strNo+" 获取好友列表";
287                    ::PostMessage(g_pPage3->m_hWnd,WM_NEWLOG,(WPARAM)(LPCTSTR)strLog,0);
288                    //
289                    vSQL="select ImNo,ImName,ImOnline from imuser,friend where friend.imno1='"+strNo+"' and friend.imno2=imuser.imno";
290                    m_record=pThis->ado.GetRecordSet(vSQL);
291                    while(!m_record->adoEOF)
292                    {
293                        CP2PMessage peerMsg={0};
294                        strNo=(LPCTSTR)(_bstr_t)m_record->GetCollect("ImNo");
295                        strName=(LPCTSTR)(_bstr_t)m_record->GetCollect("ImName");
296                        Online=atoi((LPCTSTR)(_bstr_t)m_record->GetCollect("ImOnline"));
297                        //去掉空格
298                        strNo.TrimRight(" ");
299                        strName.TrimRight(" ");
300                        strcpy((char*)peerMsg.peer.ImNo,(LPCSTR)(LPCTSTR)strNo);
301                        strcpy((char*)peerMsg.peer.ImName,(LPCSTR)(LPCTSTR)strName);
302                        peerMsg.peer.ImOnline=Online;
303                        peerMsg.nMessageType=GETFRIENDLISTACK;
304                        ::sendto(pThis->g_s,(char *)&peerMsg,sizeof(peerMsg),0,(sockaddr *)&remoteAddr,sizeof(remoteAddr));
305                        m_record->MoveNext();
306                    }

307                }

308                break;
309
310            //[5/15]发给服务器的语音
311            case GETVOIP:
312                strNo=(char*)(pMsg+1);
313                //是发给服务器的语音,则返回信息,否则转发(P2PCONNECT 的处理相同)
314                if(strNo=="10000")
315                {
316                    //有语音请求,需建立监听
317                    ::SendMessage(g_pDlg->m_hWnd,WM_NEWVOIP,NULL,NULL);
318                    //保存发送方的端口,如果g_nVOIPPort不为0表示已经连接,还没断开
319                    CP2PMessage ackMsg;
320                    ackMsg.nMessageType=GETVOIPACK;
321                    memcpy(&ackMsg.peer,&pThis->m_LocalPeer,sizeof(PEER_INFO));    
322                    sockaddr_in peerAddr ={0};
323                    peerAddr.sin_family=AF_INET;
324
325                    if(g_nVOIPPort==0 && !g_pDlg->m_bBtnConnectDown)
326                    {   
327                        g_nVOIPPort=pMsg->peer.p2pAddr.nPort;
328                        ackMsg.peer.p2pAddr.nPort=g_nLocalPort;
329                        
330                    }

331                    else
332                    {
333                        ackMsg.peer.p2pAddr.nPort=0;   //返回0表示对方正在通话中
334
335                    }

336                    
337                    for(int i=0;i<pMsg->peer.AddrNum;i++)
338                    {  
339                        peerAddr.sin_addr.S_un.S_addr=pMsg->peer.addr[i].dwIp;
340                        peerAddr.sin_port=htons(pMsg->peer.addr[i].nPort);
341                        ::sendto(pThis->g_s,(char *)&ackMsg,sizeof(ackMsg),0,(sockaddr *)&peerAddr,sizeof(peerAddr));
342                        //CString str;
343                        //str=::inet_ntoa(peerAddr.sin_addr);
344                            //AfxMessageBox(str);
345                    }

346                    break;
347                }

348
349            //[6/15]连接:消息转发的实现 *关键技术*/////////////////////////////////////////////////////////////////////////
350            case P2PCONNECT:    
351                {    
352                    strNo=(char*)(pMsg+1);
353                    CString strFrom=pMsg->peer.ImNo;
354                    if(pMsg->nMessageType==P2PCONNECT)
355                    {
356                        //写到日志列表中
357                        time=COleDateTime::GetCurrentTime();
358                        strTime=time.Format("%H:%M:%S ");
359                        strLog=strTime+strFrom+"--->"+strNo+" 消息请求";
360                        ::PostMessage(g_pPage3->m_hWnd,WM_NEWLOG,(WPARAM)(LPCTSTR)strLog,0);
361
362                    }

363                    else
364                    {
365                        //写到日志列表中
366                        time=COleDateTime::GetCurrentTime();
367                        strTime=time.Format("%H:%M:%S ");
368                        strLog=strTime+strFrom+"--->"+strNo+" 语音请求";
369                        ::PostMessage(g_pPage3->m_hWnd,WM_NEWLOG,(WPARAM)(LPCTSTR)strLog,0);
370                    }

371
372                    //如果A发给server,则服务器直接应答
373                    if(strNo=="10000")
374                    {
375                        CP2PMessage ackMsg;
376                        ackMsg.nMessageType=P2PCONNECTACK;
377                        memcpy(&ackMsg.peer,&pThis->m_LocalPeer,sizeof(PEER_INFO));
378                        ::sendto(pThis->g_s,(char *)&ackMsg,sizeof(ackMsg),0,(sockaddr*)&remoteAddr,sizeof(remoteAddr));  //发送确认连接
379                    
380                    }

381                    //如果A发给B,则sever从数据库中搜索B地址,按此地址转发给B
382                    else
383                    {
384                        vSQL="select * from imuser where ImNo='"+strNo+"";
385                        m_record=pThis->ado.GetRecordSet(vSQL);
386                        if(!m_record->adoEOF)
387                        {  
388                            strIp=(LPCTSTR)(_bstr_t)m_record->GetCollect("ImIp");
389                            strPort=(LPCTSTR)(_bstr_t)m_record->GetCollect("ImPort");
390                            strOnline=(LPCTSTR)(_bstr_t)m_record->GetCollect("ImOnline");
391                            strIp.TrimRight(" ");
392                            strPort.TrimRight(" ");    
393                            strOnline.TrimRight(" ");
394                            if(strOnline=="1" && strPort!="0")
395                            {
396                                remoteAddr.sin_addr.S_un.S_addr=inet_addr(strIp);
397                                remoteAddr.sin_port=htons(atoi(strPort));
398                                ::sendto(pThis->g_s,(char*)pMsg,sizeof(CP2PMessage),0,(sockaddr*)&remoteAddr,sizeof(remoteAddr));
399                            }

400                        }

401                        else    //语音请求的账号不存在
402                        {
403                            if(pMsg->nMessageType==GETVOIP)
404                            {
405                                pMsg->nMessageType=GETVOIPACK;
406                                pMsg->peer.p2pAddr.nPort=10000;  //端口==10000表示账号不存在
407                                ::sendto(pThis->g_s,(char *)pMsg,sizeof(CP2PMessage),0,(sockaddr*)&remoteAddr,sizeof(remoteAddr)); 
408
409                            }

410                        }

411                    }

412                    //AfxMessageBox(strNo);
413
414                }

415                break;
416
417            //[7/15]加为好友////////////////////////////////////////////////////////////////////////
418            case ADDFRIEND:    
419                {
420                    CString strFrom,strTo;
421                    strFrom=pMsg->peer.ImNo;
422                    strTo=(char *)(pMsg+1);
423                    //写到日志列表中
424                    time=COleDateTime::GetCurrentTime();
425                    strTime=time.Format("%H:%M:%S ");
426                    strLog=strTime+strFrom+"--->"+strTo+" 加为好友";
427                    ::PostMessage(g_pPage3->m_hWnd,WM_NEWLOG,(WPARAM)(LPCTSTR)strLog,0);
428                    //AfxMessageBox(strFrom+"------>"+strTo);
429                    strNo=(char*)(pMsg+1);
430                    vSQL="select * from imuser where ImNo='"+strTo+"";
431                    m_record=pThis->ado.GetRecordSet(vSQL);
432                    if(!m_record->adoEOF)
433                    {  
434                        strIp=(LPCTSTR)(_bstr_t)m_record->GetCollect("ImIp");
435                        strPort=(LPCTSTR)(_bstr_t)m_record->GetCollect("ImPort");
436                        strOnline=(LPCTSTR)(_bstr_t)m_record->GetCollect("ImOnline");
437                        strIp.TrimRight(" ");
438                        strPort.TrimRight(" ");    
439                        strOnline.TrimRight(" ");
440                        if(strOnline=="1" && strPort!="0")
441                        {
442                            remoteAddr.sin_addr.S_un.S_addr=inet_addr(strIp);
443                            remoteAddr.sin_port=htons(atoi(strPort));
444                            ::sendto(pThis->g_s,(char*)pMsg,sizeof(CP2PMessage)+sizeof(NewFriendMessage),0,(sockaddr*)&remoteAddr,sizeof(remoteAddr));
445                        }

446                        else   //没在线,先将离线好友请求添加到数据库中
447                        {
448                            //取得记录集
449                            char *pNo=(char*)(pMsg+1);
450                            vSQL="select * from OffAddfriend";
451                            m_record=pThis->ado.GetRecordSet(vSQL);
452                            m_record->AddNew();                                          ///添加新记录
453                            m_record->PutCollect("ImNo",_variant_t(pNo));              ///为新记录填充ImNo字段
454                             
455                            //将离线请求写到数据库
456                             char        *pBuf = buff;
457                             VARIANT        varBLOB;
458                             SAFEARRAY    *psa;
459                             SAFEARRAYBOUND    rgsabound[1];
460                             if(pBuf)
461                             {    
462                               rgsabound[0].lLbound = 0;
463                               rgsabound[0].cElements = dwRecv;                             //存入的数据是去掉用户名的
464                               psa = SafeArrayCreate(VT_UI1, 1, rgsabound);              ///创建SAFEARRAY对象
465                               for (long i = 0; i <(long)dwRecv; i++)
466                                  SafeArrayPutElement (psa, &i, pBuf++);                 ///将pBuf指向的二进制数据保存到SAFEARRAY对象psa中
467                               
468                               varBLOB.vt = VT_ARRAY | VT_UI1;                           ///将varBLOB的类型设置为BYTE类型的数组
469                               varBLOB.parray = psa;                                     ///为varBLOB变量赋值
470                               m_record->GetFields()->GetItem("ImInfo")->Value=varBLOB;     ///加入BLOB类型的数据
471                             }
 
472                             m_record->Update();       
473                        }

474                    }

475                    
476                }

477                break;
478 
479                //[8/15]strTo 答应 strFrom 好友请求()////////////////////////////////////////////////////
480              case ADDFRIENDACK:  
481                  {
482                      CString strFrom,strTo;
483                      BOOL bAllow;
484                      NewFriendMessage *pNew=(NewFriendMessage *)(pMsg+1);
485                      strFrom=pNew->szNo;
486                      strTo=pMsg->peer.ImNo;
487                      bAllow=pNew->bAllow;
488                      //写到日志列表中
489                    time=COleDateTime::GetCurrentTime();
490                    strTime=time.Format("%H:%M:%S ");
491                    strLog=strTime+strTo+"--->"+strFrom+" 接受加为好友";
492                    ::PostMessage(g_pPage3->m_hWnd,WM_NEWLOG,(WPARAM)(LPCTSTR)strLog,0);
493                      //返回确认消息
494                      vSQL="select * from imuser where ImNo='"+strFrom+"";
495                      m_record=pThis->ado.GetRecordSet(vSQL);
496                      if(!m_record->adoEOF)
497                      {  
498                          strNo=(LPCTSTR)(_bstr_t)m_record->GetCollect("ImNo");
499                          strName=(LPCTSTR)(_bstr_t)m_record->GetCollect("ImName");
500                          strIp=(LPCTSTR)(_bstr_t)m_record->GetCollect("ImIp");
501                          strPort=(LPCTSTR)(_bstr_t)m_record->GetCollect("ImPort");
502                          strOnline=(LPCTSTR)(_bstr_t)m_record->GetCollect("ImOnline");
503                          strNo.TrimRight(" ");
504                          strName.TrimRight(" ");
505                          strIp.TrimRight(" ");
506                          strPort.TrimRight(" ");    
507                          strOnline.TrimRight(" ");
508                          //先给对方发送
509                          if(bAllow)
510                          {
511                              vSQL="select * from friend where ImNo1='"+strTo+"' and ImNo2='"+strFrom+"";
512                              m_record=pThis->ado.GetRecordSet(vSQL);
513                              if(m_record->adoEOF)
514                              {
515                                  vSQL="insert into friend values('"+strTo+"','"+strFrom+"')";
516                                  pThis->ado.ExecuteSQL(vSQL);
517                                  CP2PMessage peerMsg={0};
518                                  strcpy((char*)peerMsg.peer.ImNo,(LPCSTR)(LPCTSTR)strNo);
519                                  strcpy((char*)peerMsg.peer.ImName,(LPCSTR)(LPCTSTR)strName);
520                                  peerMsg.peer.ImOnline=atoi(strOnline);
521                                  peerMsg.nMessageType=ADDFRIENDACK;
522                                  ::sendto(pThis->g_s,(char*)&peerMsg,sizeof(CP2PMessage),0,(sockaddr*)&remoteAddr,sizeof(remoteAddr));
523                            }

524                          }

525                          //
526                          vSQL="select * from friend where ImNo1='"+strFrom+"' and ImNo2='"+strTo+"";
527                          m_record=pThis->ado.GetRecordSet(vSQL);
528                          if(m_record->adoEOF)
529                          {
530                              vSQL="insert into friend values('"+strFrom+"','"+strTo+"')";
531                              pThis->ado.ExecuteSQL(vSQL);
532                          }

533                          //在给发送方发送确认信息
534                          if(strOnline=="1" && strPort!="0")
535                          {
536
537                              pMsg->peer.ImOnline=1;
538                              remoteAddr.sin_addr.S_un.S_addr=inet_addr(strIp);
539                              remoteAddr.sin_port=htons(atoi(strPort));
540                              //直接转发就可以了,消息包中已经包含对方的信息
541                              ::sendto(pThis->g_s,(char*)pMsg,sizeof(CP2PMessage),0,(sockaddr*)&remoteAddr,sizeof(remoteAddr));
542                          
543                          }

544                          
545
546                      }

547
548                  }

549                  break;
550                  
551            //[9/15]服务器收到客户端节点发给服务器的消息/////////////////////////////////////////////////////
552            case P2PMESSAGE:
553            {
554                int nDataLen=dwRecv-sizeof(CP2PMessage)-sizeof(MessageInfo);
555                if(nDataLen>0)
556                {   
557                    //发送确认消息
558                    CP2PMessage ackMsg;
559                    ackMsg.nMessageType=P2PMESSAGEACK;
560                    memcpy(&ackMsg.peer,&pThis->m_LocalPeer,sizeof(PEER_INFO));
561                    ::sendto(pThis->g_s,(char *)&ackMsg,sizeof(ackMsg),0,(sockaddr*)&remoteAddr,sizeof(remoteAddr));
562                                        
563                    CString strText,strNo,strName;
564                    strName=pMsg->peer.ImName;
565                    strNo=pMsg->peer.ImNo;
566                    strText=strName+"("+strNo+")";
567                    HWND hwnd=pThis->FindMessage(strText);
568                    //找到消息窗口,如果消息窗口存在,则将消息直接传过去,否则弹出提示
569                    if(!hwnd)
570                    {
571                        ::SendMessage(g_pPage1->m_hWnd,WM_NEWMESSAGE,(WPARAM)(pMsg+1),(LPARAM)(LPTSTR)(LPCTSTR)strText);
572                    }

573                    else
574                    {
575                        ::SendMessage(hwnd,WM_NEWMESSAGE,(WPARAM)(pMsg+1),(LPARAM)(LPTSTR)(LPCTSTR)strText);
576                    }

577            
578                }

579            }

580            break;
581
582        //[9/15]收到消息应答
583       case P2PMESSAGEACK:             
584                {
585                    pThis->m_bMessageACK=TRUE;
586                }

587                break;
588
589        //[10/15]语音连接应答
590        case GETVOIPACK:
591            {    
592                //得到从对方返回的监听端口,如果为0.表示对方正在通话中
593                if(pMsg->peer.p2pAddr.nPort==0)
594                {
595                    g_strVOIP="0.0.0.0";
596                    //AfxMessageBox("对不起,对方正在通话中,请稍后再拨!");
597                }

598                else
599                {
600                    //保存被叫方的IP和监听端口
601                      g_strVOIP=::inet_ntoa(remoteAddr.sin_addr);
602                    g_nVOIPPort=pMsg->peer.p2pAddr.nPort;
603
604                    //CString str,str1;
605                    //str.Format("对方的监听端口是:%d\r\n",g_nVOIPPort);
606                    //str1.Format("本地的监听端口是:%d",g_nLocalPort);
607                    //AfxMessageBox(str+str1);
608                }

609            }

610            break;
611
612        //[11/15]离线消息
613        case OFFP2PMESSAGE:
614            {
615                DWORD size=dwRecv-sizeof(CP2PMessage)-sizeof(MessageInfo)-MAX_USERNAME;  //消息长,去掉对方的用户名后
616                MessageInfo *pInfo=(MessageInfo *)(pMsg+1);
617                char *pNo=(char *)(pInfo+1);
618                CString strFrom,strTo;
619                strFrom=pMsg->peer.ImNo;
620                strTo=pNo;
621                //写到日志列表中
622                time=COleDateTime::GetCurrentTime();
623                strTime=time.Format("%H:%M:%S ");
624                strLog=strTime+strFrom+"--->"+strTo+" 离线消息";
625                ::PostMessage(g_pPage3->m_hWnd,WM_NEWLOG,(WPARAM)(LPCTSTR)strLog,0);
626                //取得记录集
627                vSQL="select * from OfflineMessage";
628                m_record=pThis->ado.GetRecordSet(vSQL);
629                m_record->AddNew();                                          ///添加新记录
630                m_record->PutCollect("ImNo",_variant_t(pNo));              ///为新记录填充ImNo字段
631                 
632                memcpy(pNo,pNo+MAX_USERNAME,size);
633                //将离线消息写到数据库
634                 char        *pBuf = buff;
635                 VARIANT        varBLOB;
636                 SAFEARRAY    *psa;
637                 SAFEARRAYBOUND    rgsabound[1];
638                 if(pBuf)
639                 {    
640                   size=dwRecv-MAX_USERNAME;
641                   rgsabound[0].lLbound = 0;
642                   rgsabound[0].cElements = size;                             //存入的数据是去掉用户名的
643                   psa = SafeArrayCreate(VT_UI1, 1, rgsabound);              ///创建SAFEARRAY对象
644                   for (long i = 0; i <(long)size; i++)
645                      SafeArrayPutElement (psa, &i, pBuf++);                 ///将pBuf指向的二进制数据保存到SAFEARRAY对象psa中
646                   
647                   varBLOB.vt = VT_ARRAY | VT_UI1;                           ///将varBLOB的类型设置为BYTE类型的数组
648                   varBLOB.parray = psa;                                     ///为varBLOB变量赋值
649                   m_record->GetFields()->GetItem("ImMsg")->Value=varBLOB;     ///加入BLOB类型的数据
650                 }
 
651                 m_record->Update();                                         ///保存我们的数据到库中
652            }

653            break;
654
655        //[12/15]删除好友///////////////////////////////////////////////////////////////////
656        case DELETEFRIEND:
657            {
658                //int nPos=str.Find("(");
659                //CString strNo=str.Mid(nPos+1,str.GetLength()-nPos-2);
660                CString strFrom,strTo;
661                strFrom=pMsg->peer.ImNo;
662                strTo=(char *)(pMsg+1);   //发过来的消息里含有用户名
663                int nPos=strTo.Find("(");
664                strTo=strTo.Mid(nPos+1,strTo.GetLength()-nPos-2);
665   
666                vSQL="delete from friend where ImNo1='"+strFrom+"' and ImNo2='"+strTo+"'";
667                if(pThis->ado.ExecuteSQL(vSQL))
668                {
669                    //写到日志列表中
670                
671                    time=COleDateTime::GetCurrentTime();
672                    strTime=time.Format("%H:%M:%S ");
673                    strLog=strTime+strFrom+"--->"+strNo+" 删除好友";
674                    ::PostMessage(g_pPage3->m_hWnd,WM_NEWLOG,(WPARAM)(LPCTSTR)strLog,0);
675
676                    pMsg->nMessageType=DELETEFRIENDACK;
677                    ::sendto(pThis->g_s,(char*)pMsg,sizeof(CP2PMessage)+MAX_USERNAME*2,0,(sockaddr*)&remoteAddr,sizeof(remoteAddr));
678
679                }

680
681            }

682            break;
683
684        //[13/15]得到用户自己信息//////////////////////////////////////////////////////////
685        case GETUSERINFO: 
686            {
687                strNo=(char *)(pMsg+1);
688                vSQL="select * from imuser where ImNo='"+strNo+"'";
689                m_record=pThis->ado.GetRecordSet(vSQL);
690                if(!m_record->adoEOF)
691                {
692
693                    strName=(LPCTSTR)(_bstr_t)m_record->GetCollect("ImName");
694                    strIp=(LPCTSTR)(_bstr_t)m_record->GetCollect("ImIp");
695                    strPort=(LPCTSTR)(_bstr_t)m_record->GetCollect("ImPort");
696                    strPhone=(LPCTSTR)(_bstr_t)m_record->GetCollect("ImPhone");
697            
698                    strName.TrimRight(" ");
699                    strIp.TrimRight(" ");
700                    strPort.TrimRight(" ");
701                    strPhone.TrimRight(" ");
702                    
703                    //登陆成功,并返回用户自己的信息
704                    pMsg->nMessageType=GETUSERINFOACK;
705                    strcpy((char*)pMsg->peer.ImName,(LPCSTR)(LPCTSTR)strName);
706                    strcpy((char*)pMsg->peer.ImNo,(LPCSTR)(LPCTSTR)strNo);
707                    strcpy((char*)pMsg->peer.ImPhone,(LPCSTR)(LPCTSTR)strPhone);
708                    pMsg->peer.p2pAddr.dwIp=::inet_addr(strIp);
709                    pMsg->peer.p2pAddr.nPort=::atoi(strPort);
710                    pMsg->nMessageType=GETUSERINFOACK;
711                    ::sendto(pThis->g_s,(char*)pMsg,sizeof(CP2PMessage),0,(sockaddr*)&remoteAddr,sizeof(remoteAddr));
712
713
714                }

715            }

716            break;
717
718        //[14/15]群聊技术的实现:*关键技术*////////////////////////////////////////////////////////
719        case GROUPMESSAGE:
720            {
721                strNo=pMsg->peer.ImNo;
722                vSQL="select ImIp,ImPort,ImOnline from imuser where ImNo!='"+strNo+"'and ImOnline=1";  //只要IP地址和端口号
723                m_record=pThis->ado.GetRecordSet(vSQL);
724                while(!m_record->adoEOF)
725                {
726                    //要发送对象的地址
727                    strIp=(LPCTSTR)(_bstr_t)m_record->GetCollect("ImIp");
728                    strPort=(LPCTSTR)(_bstr_t)m_record->GetCollect("ImPort");
729                    strOnline=(LPCTSTR)(_bstr_t)m_record->GetCollect("ImOnline");
730                    strIp.TrimRight(" ");
731                    strPort.TrimRight(" ");
732                    strOnline.TrimRight(" ");
733
734                    remoteAddr.sin_addr.S_un.S_addr=inet_addr(strIp);
735                    remoteAddr.sin_port=htons(atoi(strPort));
736                    if(strOnline=="1" && strPort!="0")
737                        ::sendto(pThis->g_s,(char *)pMsg,dwRecv,0,(sockaddr *)&remoteAddr,sizeof(remoteAddr));
738                    m_record->MoveNext();
739                }

740
741                CString strText,strMessage;
742                MessageInfo *pInfo=(MessageInfo *)(pMsg+1);
743                strNo=pMsg->peer.ImNo;
744                strName=pMsg->peer.ImName;
745                strText=strName+"("+strNo+"";
746                strMessage=(char *)(pInfo+1);
747
748                PlaySound("wav\\msg.wav", NULL, SND_FILENAME | SND_ASYNC);
749                if(g_pDlg->m_pGroupMessage)
750                {
751                    g_pDlg->m_pGroupMessage->AddName(FALSE,strText);
752                    g_pDlg->m_pGroupMessage->AddText(strMessage+"\r\n",pInfo);
753                }

754                else
755                {    
756                    //群窗口没打开的话,则发送该消息
757                    ::SendMessage(g_pDlg->m_hWnd,WM_NEWGROUPMESSAGE,(WPARAM)pMsg,(LPARAM)(LPTSTR)(LPCTSTR)strText);
758                    
759                }

760
761            }

762            break;
763
764                
765
766        }

767
768    }

769    return 0;
770}

771
772
773
774
775//查找链表,返回用户名等于 str 的对话框句柄
776HWND CP2PServer::FindMessage(CString str)
777{
778    critical_section.Lock();
779    MessageUser user;
780    POSITION nPos=g_MessageList.GetHeadPosition();
781    while(nPos)
782    {
783        user=g_MessageList.GetNext(nPos);
784        if(user.m_strText==str)
785        {
786            critical_section.Unlock();
787            return user.m_hWnd;
788        }

789
790    }

791    critical_section.Unlock();
792    return NULL;
793        
794}

795