收发Jabber消息流程

原创 2007年09月25日 14:44:00

 

1.   聊天对话框基本信息:
1)        对话框模板:  IDD_MSG
2)        窗口过程:  DlgProcMessage
3)        “发送”按钮: IDOK
 
在聊天对话框初始化的时候关于该联系人所用协议等信息已经做为该窗口的用户数据设置好了,用到的时候只要以GWL_USERDATA 为参数调用GetWindowLong就可以得到该用户数据的指针,具体的过程参考<Miranda UI分析>中的 聊天窗口 创建部分, 该文主要关注的是jabber消息的收发流程.
2.   发送消息:
2.1.IDOK 发送 按钮
case IDOK:
                if (!IsWindowEnabled(GetDlgItem(hwndDlg, IDOK)))
                    break;
                {
                    int flags = SEND_FLAGS;
                    //this is a 'send' button
                    int bufSize = GetWindowTextLengthA(GetDlgItem(hwndDlg, IDC_MESSAGE)) + 1;
                    dat->sendBuffer = (char *) realloc(dat->sendBuffer, bufSize * (sizeof(TCHAR) + 1));
                    dat->bIsRtl = 0;
                    GetDlgItemTextA(hwndDlg, IDC_MESSAGE, dat->sendBuffer, bufSize);
                    if (dat->sendBuffer[0] == 0)
                        break;
                        dat->cmdList = tcmdlist_append(dat->cmdList, dat->sendBuffer);
                    dat->cmdListCurrent = 0;
                    if (dat->nTypeMode == PROTOTYPE_SELFTYPING_ON) {
                        NotifyTyping(dat, PROTOTYPE_SELFTYPING_OFF);
                    }
 
                    if (dat->hContact == NULL)
                        break;      //never happens
                    dat->sendCount = 1;
                    //发送消息
                    dat->hSendId = (HANDLE) CallContactService(dat->hContact, MsgServiceName(dat->hContact), flags, (LPARAM) dat->sendBuffer);
                    EnableWindow(GetDlgItem(hwndDlg, IDOK), FALSE);
                    SendDlgItemMessage(hwndDlg, IDC_MESSAGE, EM_SETREADONLY, TRUE, 0);
 
                    //create a timeout timer
                    SetTimer(hwndDlg, TIMERID_MSGSEND, DBGetContactSettingDword(NULL, SRMMMOD, SRMSGSET_MSGTIMEOUT, SRMSGDEFSET_MSGTIMEOUT), NULL);
                    if (DBGetContactSettingByte(NULL, SRMMMOD, SRMSGSET_AUTOMIN, SRMSGDEFSET_AUTOMIN))
                        ShowWindow(hwndDlg, SW_MINIMIZE);
                }
                return TRUE;
 
2.2.CallContactService
CallContactService
Proto_CallContactService
 JabberSendMessage
2.3.JabberSendMessage
int JabberSendMessage( WPARAM wParam, LPARAM lParam )
{
       CCSDATA *ccs = ( CCSDATA * ) lParam;
       JABBER_LIST_ITEM *item;
       int id;
 
       DBVARIANT dbv;
       if ( !jabberOnline || JGetStringT( ccs->hContact, "jid", &dbv )) {
              JSendBroadcast( ccs->hContact, ACKTYPE_MESSAGE, ACKRESULT_FAILED, ( HANDLE ) 1, 0 );
              return 0;
       }
 
       char* pszSrc = ( char* )ccs->lParam, *msg;
       int isEncrypted;
 
       char* pdest = strstr( pszSrc, PGP_PROLOG );//pdest-string+1 is index of first occurence
       if ( pdest != NULL ) {
              pdest = strstr( pszSrc, PGP_EPILOG );
              int result = ( pdest ) ? strlen( PGP_PROLOG ) : 0;
 
              char* tempstring = ( char* )alloca( strlen( pszSrc ));
              strncpy( tempstring, pszSrc+strlen(PGP_PROLOG), strlen(pszSrc)-strlen(PGP_EPILOG)-result );
              pszSrc = tempstring;
              isEncrypted = 1;
       }
       else isEncrypted = 0;
 
       if ( ccs->wParam & PREF_UNICODE )
              msg = JabberTextEncodeW(( wchar_t* )&pszSrc[ strlen( pszSrc )+1 ] );
       else
              msg = JabberTextEncode( pszSrc );
 
       if ( msg != NULL ) {
              char msgType[ 16 ];
              if ( JabberListExist( LIST_CHATROOM, dbv.ptszVal ) && _tcschr( dbv.ptszVal, '/' )==NULL )
                     strcpy( msgType, "groupchat" );
              else
                     strcpy( msgType, "chat" );
 
              //封装为xml格式消息
XmlNode m( "message" ); m.addAttr( "type", msgType );
              if ( !isEncrypted ) {
                     XmlNode* body = m.addChild( "body" );
                     body->sendText = msg;
              }
              else {
                     m.addChild( "body", "[This message is encrypted.]" );
                     XmlNode* x = m.addChild( "x" ); x->addAttr( "xmlns", "jabber:x:encrypted" );
                     x->sendText = msg;
              }
 
              XmlNode* active = m.addChild( "active" ); active->addAttr( "xmlns", _T("http://jabber.org/protocol/chatstates"));
 
              if ( !strcmp( msgType, "groupchat" ) || !JGetByte( "MsgAck", FALSE ) || !JGetByte( ccs->hContact, "MsgAck", TRUE )) {
                     if ( !strcmp( msgType, "groupchat" ))
                            m.addAttr( "to", dbv.ptszVal );
                     else {
                            id = JabberSerialNext();
                            TCHAR szClientJid[ 256 ];
                            JabberGetClientJID( dbv.ptszVal, szClientJid, SIZEOF( szClientJid ));
 
                            m.addAttr( "to", szClientJid ); m.addAttrID( id );
                            XmlNode* x = m.addChild( "x" ); x->addAttr( "xmlns", "jabber:x:event" ); x->addChild( "composing" );
                     }
 
                     jabberThreadInfo->send( m );
                     mir_forkthread( JabberSendMessageAckThread, ccs->hContact );
              }
              else {
                     id = JabberSerialNext();
                     if (( item=JabberListGetItemPtr( LIST_ROSTER, dbv.ptszVal )) != NULL )
                            item->idMsgAckPending = id;
 
                     TCHAR szClientJid[ 256 ];
                     JabberGetClientJID( dbv.ptszVal, szClientJid, SIZEOF( szClientJid ));
                     m.addAttr( "to", szClientJid ); m.addAttrID( id );
 
                     XmlNode* x = m.addChild( "x" ); x->addAttr( "xmlns", "jabber:x:event" );
                     x->addChild( "composing" ); x->addChild( "delivered" ); x->addChild( "offline" );
                     jabberThreadInfo->send( m );
       }     }
 
       JFreeVariant( &dbv );
       return 1;
}
 
2.4.jabberThreadInfo->send( m )
2.4.1.    ThreadData::send
int ThreadData::send( XmlNode& node )
{
       if ( this == NULL )
              return 0;
 
       char* str = node.getText();
       int size = strlen( str ), result;
 
       EnterCriticalSection( &iomutex );
 
       if ( ssl != NULL ) {
              if ( DBGetContactSettingByte( NULL, "Netlib", "DumpSent", TRUE ) == TRUE ) {
                     char* szLogBuffer = ( char* )alloca( size+32 );
                     strcpy( szLogBuffer, "( SSL ) Data sent/n" );
                     memcpy( szLogBuffer+strlen( szLogBuffer ), str, size+1 ); // also copy /0
                     Netlib_Logf( hNetlibUser, "%s", szLogBuffer );   // %s to protect against when fmt tokens are in szLogBuffer causing crash
              }
 
              result = pfn_SSL_write( ssl, str, size );
       }
       else result = JabberWsSend( s, str, size );
       LeaveCriticalSection( &iomutex );
 
       mir_free( str );
       return result;
}
 
 
 
 
2.4.1.1.          JabberWsSend
int JabberWsSend( JABBER_SOCKET hConn, char* data, int datalen )
{
       int len;
 
       if (( len=Netlib_Send( hConn, data, datalen, MSG_DUMPASTEXT ))==SOCKET_ERROR || len!=datalen ) {
              JabberLog( "Netlib_Send() failed, error=%d", WSAGetLastError());
              return FALSE;
       }
       return TRUE;
}
 
2.4.1.1.1.       Netlib_Send
static __inline int Netlib_Send(HANDLE hConn,const char *buf,int len,int flags) {
       NETLIBBUFFER nlb={(char*)buf,len,flags};
       return CallService(MS_NETLIB_SEND,(WPARAM)hConn,(LPARAM)&nlb);
}
 
2.4.1.1.1.1.   NetlibSend
int NetlibSend(WPARAM wParam,LPARAM lParam)
{
       struct NetlibConnection *nlc=(struct NetlibConnection*)wParam;
       NETLIBBUFFER *nlb=(NETLIBBUFFER*)lParam;
       int result;
 
       if(nlb==NULL) {
              SetLastError(ERROR_INVALID_PARAMETER);
              return SOCKET_ERROR;
       }
 
       if(!NetlibEnterNestedCS(nlc,NLNCS_SEND)) return SOCKET_ERROR;
       if(nlc->usingHttpGateway && !(nlb->flags&MSG_RAW)) {
              if(!(nlb->flags&MSG_NOHTTPGATEWAYWRAP) && nlc->nlu->user.pfnHttpGatewayWrapSend) {
                     NetlibDumpData(nlc,nlb->buf,nlb->len,1,nlb->flags);
                     result=nlc->nlu->user.pfnHttpGatewayWrapSend((HANDLE)nlc,nlb->buf,nlb->len,nlb->flags|MSG_NOHTTPGATEWAYWRAP,NetlibSend);
              }
              else result=NetlibHttpGatewayPost(nlc,nlb->buf,nlb->len,nlb->flags);
       }
       else {
              NetlibDumpData(nlc,nlb->buf,nlb->len,1,nlb->flags);
              result=send(nlc->s,nlb->buf,nlb->len,nlb->flags&0xFFFF);
       }
       NetlibLeaveNestedCS(&nlc->ncsSend);
       return result;
}
2.4.1.1.1.1.1.send
最终调用socket api函数send,发送消息
2.5.JabberSendMessageAckThread
 
3.   发送超时处理:
3.1.设置超时timer
case WM_COMMAND:
            ……
            switch (LOWORD(wParam))
{
            case IDOK:
                    ……
                    //create a timeout timer
                    SetTimer(hwndDlg, TIMERID_MSGSEND, DBGetContactSettingDword(NULL, SRMMMOD, SRMSGSET_MSGTIMEOUT, SRMSGDEFSET_MSGTIMEOUT), NULL);
                    if (DBGetContactSettingByte(NULL, SRMMMOD, SRMSGSET_AUTOMIN, SRMSGDEFSET_AUTOMIN))
                        ShowWindow(hwndDlg, SW_MINIMIZE);
                }
                return TRUE;
 
 
3.2.响应超时timer
    case WM_TIMER:
        if (wParam == TIMERID_MSGSEND)
{
            KillTimer(hwndDlg, wParam);
            ShowWindow(hwndDlg, SW_SHOWNORMAL);
            EnableWindow(hwndDlg, FALSE);
            CreateDialogParam(g_hInst, MAKEINTRESOURCE(IDD_MSGSENDERROR), hwndDlg, ErrorDlgProc, (LPARAM) strdup( Translate("The message send timed out.")));
        }
        ……
 
4.   接收消息:
JabberServerThread函数中有一个for(;;)死循环,用于接收消息
JabberServerThread
JabberServerThread 是Jabber里非常重要的一个线程, 注册登录接收消息等都在这个线程里, 接收消息的线程是在设置jabber用户在线状态(即登录)时创建出来的,详细参考<注册注销登录退出jabber>. 在程序里加了注释, 仔细分析代码吧:
void __cdecl JabberServerThread( ThreadData* info )
{
……
 
        //zhangcong//main recv loop, 出现异常时,将break loop
              for ( ;; )
        {
                     int recvResult = info->recv( buffer+datalen, jabberNetworkBufferSize-datalen);
            int bytesParsed;
                     JabberLog( "recvResult = %d", recvResult );
                     if ( recvResult <= 0 )
                            break;
                     datalen += recvResult;
 
                     buffer[datalen] = '/0';
                     if ( info->ssl && DBGetContactSettingByte( NULL, "Netlib", "DumpRecv", TRUE ) == TRUE )
            {
                            // Emulate netlib log feature for SSL connection
                            char* szLogBuffer = ( char* )mir_alloc( recvResult+128 );
                            if ( szLogBuffer != NULL )
                {
                                   strcpy( szLogBuffer, "( SSL ) Data received/n" );
                                   memcpy( szLogBuffer+strlen( szLogBuffer ), buffer+datalen-recvResult, recvResult+1 /* also copy /0 */ );
                                   Netlib_Logf( hNetlibUser, "%s", szLogBuffer );   // %s to protect against when fmt tokens are in szLogBuffer causing crash
                                   mir_free( szLogBuffer );
                }    
            }
 
            //XmlParse接收到的数据
                     bytesParsed = JabberXmlParse( &xmlState, buffer );
                     JabberLog( "bytesParsed = %d", bytesParsed );
                     if ( bytesParsed > 0 )
            {
                            if ( bytesParsed < datalen )
                                   memmove( buffer, buffer+bytesParsed, datalen-bytesParsed );
                            datalen -= bytesParsed;
                     }
                     else if ( datalen == jabberNetworkBufferSize )
            {
                //如果缓冲区已满, 扩充缓冲区
                            jabberNetworkBufferSize += 65536;
                            JabberLog( "Increasing network buffer size to %d", jabberNetworkBufferSize );
                            if (( buffer=( char* )mir_realloc( buffer, jabberNetworkBufferSize+1 )) == NULL ) {
                                   JabberLog( "Cannot reallocate more network buffer, go offline now" );
                                   break;
                }    
            }
                     else
            {
                JabberLog( "Unknown state: bytesParsed=%d, datalen=%d, jabberNetworkBufferSize=%d", bytesParsed, datalen, jabberNetworkBufferSize );
            }
 
                     if (xmlStreamToBeInitialized)
                xmlStreamInitializeNow(info);
              }
 
 ……
 
}
 
 
 
 
 
 
 
 
 
 
 
显示接收到的消息
接收消息是相对简单的, 在聊天窗口显示消息则是经历了两万五千里的长征之后显示出来的,下面详细分析显示消息的过程:
JabberXmlParse
接收到的消息是xml格式的, 需要先解析接收到的xml格式的消息, Jabber是用SAX方式来解析xml的, 处理节点的时候会调用一些事先设置好的回调函数, 处理的过程比较复杂, 这里仅仅关心最重要的回调函数
 
JabberXmlProcessElem
static BOOL JabberXmlProcessElem( XmlState *xmlState, XmlElemType elemType, char* elemText, char* elemAttr )
{
       ……
       switch ( elemType ) {
       case ELEM_OPEN:
              ……
              break;
       case ELEM_OPENCLOSE:
              ……
              break;
       case ELEM_CLOSE:
              if ( node->name!=NULL && !strcmp( node->name, elemText )) {
                     node->state = NODE_CLOSE;
                     if ( node->depth==1 && xmlState->callback1_close!=NULL ) {
                            ( *( xmlState->callback1_close ))( node, xmlState->userdata1_close );
                            JabberXmlRemoveChild( parentNode, node );
                     }
                     else if ( node->depth==2 && xmlState->callback2_close!=NULL ) {
                            // 调用JabberProcessProtocol
                            ( *xmlState->callback2_close )( node, xmlState->userdata2_close );
                            JabberXmlRemoveChild( parentNode, node );
              }     }
              else {
                     JabberLog( "XML: Closing </%s> without opening tag", elemText );
                     return FALSE;
              }
              break;
 
       case ELEM_TEXT:
              JabberUtfToTchar( elemText, strlen( elemText ), node->text );
              break;
 
       default:
              return FALSE;
       }
 
       return TRUE;
}
JabberProcessProtocol
static void JabberProcessProtocol( XmlNode *node, void *userdata )
{
       ThreadData* info = ( ThreadData* ) userdata;
       if ( !strcmp( node->name, "proceed" )) {
              JabberProcessProceed( node, userdata );
              return;
       }
 
       if ( !strcmp( node->name, "stream:features" ))
              JabberProcessFeatures( node, userdata );
       else if ( !strcmp( node->name, "success"))
              JabberProcessSuccess( node, userdata );
       else if ( !strcmp( node->name, "failure"))
              JabberProcessFailure( node, userdata );
       else if ( !strcmp( node->name, "stream:error"))
              JabberProcessError( node, userdata );
       else if ( !strcmp( node->name, "challenge" ))
              JabberProcessChallenge( node, userdata );
       else if ( info->type == JABBER_SESSION_NORMAL ) {
              if ( !strcmp( node->name, "message" ))
                     JabberProcessMessage( node, userdata );
              else if ( !strcmp( node->name, "presence" ))
                     JabberProcessPresence( node, userdata );
              else if ( !strcmp( node->name, "iq" ))
                     JabberProcessIq( node, userdata );
              else
                     JabberLog( "Invalid top-level tag ( only <message/> <presence/> and <iq/> allowed )" );
       }
       else if ( info->type == JABBER_SESSION_REGISTER ) {
              if ( !strcmp( node->name, "iq" ))
                     JabberProcessRegIq( node, userdata );
              else
                     JabberLog( "Invalid top-level tag ( only <iq/> allowed )" );
}     }
JabberProcessMessage
static void JabberProcessMessage( XmlNode *node, void *userdata )
{
……
              char* buf = ( char* )alloca( cbAnsiLen+1 + (cbWideLen+1)*sizeof( WCHAR ));
              memcpy( buf, szAnsiMsg, cbAnsiLen+1 );
              memcpy( buf + cbAnsiLen + 1, wszMessage, (cbWideLen+1)*sizeof( WCHAR ));
 
       ……
 
              PROTORECVEVENT recv;
              recv.flags = PREF_UNICODE;
              recv.timestamp = ( DWORD )msgTime;
              recv.szMessage = buf;
              recv.lParam = 0;
 
              CCSDATA ccs;
              ccs.hContact = hContact;
              ccs.wParam = 0;
              ccs.szProtoService = PSR_MESSAGE;
              ccs.lParam = ( LPARAM )&recv;
              JCallService( MS_PROTO_CHAINRECV, 0, ( LPARAM )&ccs );
 
              mir_free( szMessage );
}
}
MS_PROTO_CHAINRECV
static int Proto_ChainRecv(WPARAM wParam,LPARAM lParam)
{
       /* this will switch threads just like before */
       return CallServiceSync(MS_PROTO_CHAINRECV "ThreadSafe",wParam,lParam);
}
 
CallServiceSync
int CallServiceSync(const char *name, WPARAM wParam, LPARAM lParam)
{
 
       extern HWND hAPCWindow;
 
       if (name==NULL) return CALLSERVICE_NOTFOUND;
       // the service is looked up within the main thread, since the time it takes
       // for the APC queue to clear the service being called maybe removed.
       // even thou it may exists before the call, the critsec can't be locked between calls.
       if (GetCurrentThreadId() != mainThreadId) {
              TServiceToMainThreadItem item;
              item.wParam = wParam;
              item.lParam = lParam;
              item.name = name;
              item.hDoneEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
              QueueUserAPC(CallServiceToMainAPCFunc, hMainThread, (DWORD) &item);
              //post WM_NULL使hAPCWindow所属线程进入告警状态,从而使投递的异步函
//数CallServiceToMainAPCFunc得以执行
              PostMessage(hAPCWindow,WM_NULL,0,0); // let this get processed in its own time
              WaitForSingleObject(item.hDoneEvent, INFINITE);
              CloseHandle(item.hDoneEvent);
              return item.result;
       }
 
   return CallService(name, wParam, lParam);
}
 
CallServiceToMainAPCFunc
static void CALLBACK CallServiceToMainAPCFunc(DWORD dwParam)
{
       TServiceToMainThreadItem *item = (TServiceToMainThreadItem*) dwParam;
       // item->name : CallRecvChain
       item->result = CallService(item->name, item->wParam, item->lParam);
       SetEvent(item->hDoneEvent);
}
 
 CallRecvChain
static int CallRecvChain(WPARAM wParam,LPARAM lParam)
{
       CCSDATA *ccs=(CCSDATA*)lParam;
       int i,ret;
       char str[10];
       DBVARIANT dbv;
 
       if(wParam==(WPARAM)(-1)) return 1;   //shouldn't happen - sanity check
       if(wParam==0) {      //begin processing by finding end of chain
              for(;;wParam++) {
                     _itoa(wParam,str,10);
                     if(DBGetContactSetting(ccs->hContact,"_Filter",str,&dbv)) break;
                     mir_free(dbv.pszVal);
              }
       }
       else wParam--;
       for(i=wParam-1;i>=0;i--) {
              _itoa(i,str,10);
              if(DBGetContactSetting(ccs->hContact,"_Filter",str,&dbv)) return 1; //never happens
              if((ret=CallProtoService(dbv.pszVal,ccs->szProtoService,i+1,lParam))!=CALLSERVICE_NOTFOUND) {
                     //chain was started, exit
                     mir_free(dbv.pszVal);
                     return ret;
              }
              mir_free(dbv.pszVal);
       }
       //end of chain, call network protocol again
       if(DBGetContactSetting(ccs->hContact,"Protocol","p",&dbv)) return 1;
       if((ret=CallProtoService(dbv.pszVal,ccs->szProtoService,(WPARAM)(-1),lParam))!=CALLSERVICE_NOTFOUND) {
              mir_free(dbv.pszVal);
              return ret;
       }
       mir_free(dbv.pszVal);
       return 1;
}
    JabberRecvMessage
int JabberRecvMessage( WPARAM wParam, LPARAM lParam )
{
       CCSDATA *ccs = ( CCSDATA* )lParam;
       PROTORECVEVENT *pre = ( PROTORECVEVENT* )ccs->lParam;
 
       DBEVENTINFO dbei = { 0 };
       dbei.cbSize = sizeof( dbei );
       dbei.szModule = jabberProtoName;
       dbei.timestamp = pre->timestamp;
       dbei.flags = pre->flags&PREF_CREATEREAD?DBEF_READ:0;
       dbei.eventType = EVENTTYPE_MESSAGE;
       dbei.cbBlob = strlen( pre->szMessage ) + 1;
       if ( pre->flags & PREF_UNICODE )
              dbei.cbBlob *= ( sizeof( wchar_t )+1 );
 
       dbei.pBlob = ( PBYTE ) pre->szMessage;
       //保存到历史消息记录,同时在richedit中显示消息
       JCallService( MS_DB_EVENT_ADD, ( WPARAM ) ccs->hContact, ( LPARAM )&dbei );
       return 0;
}
 
      MS_DB_EVENT_ADD
//Db3x/dbevents.c
static int AddEvent(WPARAM wParam,LPARAM lParam)
{
       DBEVENTINFO *dbei=(DBEVENTINFO*)lParam;
       struct DBContact dbc;
       struct DBEvent dbe,*dbeTest;
       DWORD ofsNew,ofsModuleName,ofsContact,ofsThis;
 
       if(dbei==NULL||dbei->cbSize!=sizeof(DBEVENTINFO)) return (int)NULL;
       if(dbei->timestamp==0) return (int)NULL;
       if (NotifyEventHooks(hEventFilterAddedEvent,wParam,lParam)) {
              return (int)NULL;
       }
       EnterCriticalSection(&csDbAccess);
       if(wParam==0) ofsContact=dbHeader.ofsUser;
       else ofsContact=wParam;
       dbc=*(struct DBContact*)DBRead(ofsContact,sizeof(struct DBContact),NULL);
       if(dbc.signature!=DBCONTACT_SIGNATURE) {
              LeaveCriticalSection(&csDbAccess);
            return (int)NULL;
       }
       ofsNew=CreateNewSpace(offsetof(struct DBEvent,blob)+dbei->cbBlob);
       ofsModuleName=GetModuleNameOfs(dbei->szModule);
 
       dbe.signature=DBEVENT_SIGNATURE;
       dbe.ofsModuleName=ofsModuleName;
       dbe.timestamp=dbei->timestamp;
       dbe.flags=dbei->flags;
       dbe.eventType=dbei->eventType;
       dbe.cbBlob=dbei->cbBlob;
       //find where to put it - sort by timestamp
       if(dbc.eventCount==0) {
              dbe.ofsPrev=wParam;
              dbe.ofsNext=0;
              dbe.flags|=DBEF_FIRST;
              dbc.ofsFirstEvent=dbc.ofsLastEvent=ofsNew;
       }
       else {
              dbeTest=(struct DBEvent*)DBRead(dbc.ofsFirstEvent,sizeof(struct DBEvent),NULL);
              // Should new event be placed before first event in chain?
              if (dbei->timestamp < dbeTest->timestamp) {
                     dbe.ofsPrev=wParam;
                     dbe.ofsNext=dbc.ofsFirstEvent;
                     dbe.flags|=DBEF_FIRST;
                     dbc.ofsFirstEvent=ofsNew;
                     dbeTest=(struct DBEvent*)DBRead(dbe.ofsNext,sizeof(struct DBEvent),NULL);
                     dbeTest->flags&=~DBEF_FIRST;
                     dbeTest->ofsPrev=ofsNew;
                     DBWrite(dbe.ofsNext,dbeTest,sizeof(struct DBEvent));
              }
              else {
                     // Loop through the chain, starting at the end
                     ofsThis = dbc.ofsLastEvent;
                     dbeTest = (struct DBEvent*)DBRead(ofsThis, sizeof(struct DBEvent), NULL);
                     for(;;) {
                            // If the new event's timesstamp is equal to or greater than the
                            // current dbevent, it will be inserted after. If not, continue
                            // with the previous dbevent in chain.
                            if (dbe.timestamp >= dbeTest->timestamp) {
                                   dbe.ofsPrev = ofsThis;
                                   dbe.ofsNext = dbeTest->ofsNext;
                                   dbeTest->ofsNext = ofsNew;
                                   DBWrite(ofsThis, dbeTest, sizeof(struct DBEvent));
                                   if (dbe.ofsNext == 0)
                                          dbc.ofsLastEvent = ofsNew;
                                   else {
                                          dbeTest = (struct DBEvent*)DBRead(dbe.ofsNext, sizeof(struct DBEvent), NULL);
                                          dbeTest->ofsPrev = ofsNew;
                                          DBWrite(dbe.ofsNext, dbeTest, sizeof(struct DBEvent));
                                   }
                                   break;
                            }
                            ofsThis = dbeTest->ofsPrev;
                            dbeTest = (struct DBEvent*)DBRead(ofsThis, sizeof(struct DBEvent), NULL);
                     }
              }
       }
       dbc.eventCount++;
       if(!(dbe.flags&(DBEF_READ|DBEF_SENT))) {
              if(dbe.timestamp<dbc.timestampFirstUnread || dbc.timestampFirstUnread==0) {
                     dbc.timestampFirstUnread=dbe.timestamp;
                     dbc.ofsFirstUnreadEvent=ofsNew;
              }
       }
       DBWrite(ofsContact,&dbc,sizeof(struct DBContact));
       DBWrite(ofsNew,&dbe,offsetof(struct DBEvent,blob));
       DBWrite(ofsNew+offsetof(struct DBEvent,blob),dbei->pBlob,dbei->cbBlob);
       DBFlush(0);
       LeaveCriticalSection(&csDbAccess);
       log1("add event @ %08x",ofsNew);
       // dbaddedevent将被调用
       NotifyEventHooks(hEventAddedEvent,wParam,(LPARAM)ofsNew);
       return (int)ofsNew;
}
        Dbaddedevent
 
static int dbaddedevent(WPARAM wParam, LPARAM lParam) {
       if (wParam) {
              HWND h = WindowList_Find(g_dat->hMessageWindowList, (HANDLE)wParam);
              if(h) SendMessage(h, HM_DBEVENTADDED, wParam, lParam);
       }
       return 0;
}
 
          HM_DBEVENTADDED
Srmm/msgdialogs.c line 1374
 
DlgProcMessage
{
       ……
           case DM_APPENDTOLOG:   //takes wParam=hDbEvent
              StreamInEvents(hwndDlg, (HANDLE) wParam, 1, 1);
              break;
           case HM_DBEVENTADDED:
              if ((HANDLE) wParam != dat->hContact)
                     break;
              {
                     DBEVENTINFO dbei = { 0 };
 
                     dbei.cbSize = sizeof(dbei);
                     dbei.cbBlob = 0;
                     CallService(MS_DB_EVENT_GET, lParam, (LPARAM) & dbei);
                     if (dat->hDbEventFirst == NULL)
                            dat->hDbEventFirst = (HANDLE) lParam;
                     if (dbei.eventType == EVENTTYPE_MESSAGE && (dbei.flags & DBEF_READ))
                            break;
                     if (DbEventIsShown(&dbei, dat)) {
                            if (dbei.eventType == EVENTTYPE_MESSAGE && !(dbei.flags & (DBEF_SENT))) {
                                   if (GetForegroundWindow()==hwndDlg)
                                          SkinPlaySound("RecvMsgActive");
                                   else SkinPlaySound("RecvMsgInactive");
                            }
                            if (dbei.eventType == EVENTTYPE_MESSAGE && dat->hwndStatus && !(dbei.flags & (DBEF_SENT))) {
                                   dat->lastMessage = dbei.timestamp;
                                   SendMessage(hwndDlg, DM_UPDATELASTMESSAGE, 0, 0);
                            }
                            if ((HANDLE) lParam != dat->hDbEventFirst && (HANDLE) CallService(MS_DB_EVENT_FINDNEXT, lParam, 0) == NULL)
                                   SendMessage(hwndDlg, DM_APPENDTOLOG, lParam, 0);
                            else
                                   SendMessage(hwndDlg, DM_REMAKELOG, 0, 0);
                            if ((GetActiveWindow() != hwndDlg || GetForegroundWindow() != hwndDlg) && !(dbei.flags & DBEF_SENT)
                                   && dbei.eventType != EVENTTYPE_STATUSCHANGE) {
                                          SetTimer(hwndDlg, TIMERID_FLASHWND, TIMEOUT_FLASHWND, NULL);
                                   }
                     }
                     break;
              }……
}
 
            StreamInEvents
void StreamInEvents(HWND hwndDlg, HANDLE hDbEventFirst, int count, int fAppend)
{
       EDITSTREAM stream = { 0 };
       struct LogStreamData streamData = { 0 };
       struct MessageWindowData *dat = (struct MessageWindowData *) GetWindowLong(hwndDlg, GWL_USERDATA);
       CHARRANGE oldSel, sel;
 
       SendDlgItemMessage(hwndDlg, IDC_LOG, EM_HIDESELECTION, TRUE, 0);
       SendDlgItemMessage(hwndDlg, IDC_LOG, EM_EXGETSEL, 0, (LPARAM) & oldSel);
       streamData.hContact = dat->hContact;
       streamData.hDbEvent = hDbEventFirst;
       streamData.dlgDat = dat;
       streamData.eventsToInsert = count;
       streamData.isEmpty = fAppend ? GetWindowTextLength(GetDlgItem(hwndDlg, IDC_LOG)) == 0 : 1;
       stream.pfnCallback = LogStreamInEvents;
       stream.dwCookie = (DWORD_PTR) & streamData;
       if (fAppend) {
              sel.cpMin = sel.cpMax = GetWindowTextLength(GetDlgItem(hwndDlg, IDC_LOG));
              SendDlgItemMessage(hwndDlg, IDC_LOG, EM_EXSETSEL, 0, (LPARAM) & sel);
       }
       else dat->bIsFirstAppend = TRUE;
 
       strcpy(szSep2, fAppend ? "//par//sl0" : "//sl1000");
       strcpy(szSep2_RTL, fAppend ? "//rtlpar//rtlmark//par//sl1000" : "//sl1000");
       //更新richedit数据, 详细参考下面的 RichEdit参考
       SendDlgItemMessage(hwndDlg, IDC_LOG, EM_STREAMIN, fAppend ? SFF_SELECTION | SF_RTF : SFF_SELECTION | SF_RTF, (LPARAM) & stream);
       SendDlgItemMessage(hwndDlg, IDC_LOG, EM_EXSETSEL, 0, (LPARAM) & oldSel);
       SendDlgItemMessage(hwndDlg, IDC_LOG, EM_HIDESELECTION, FALSE, 0);
       dat->hDbEventLast = streamData.hDbEventLast;
       if (GetWindowLong(GetDlgItem(hwndDlg, IDC_LOG), GWL_STYLE) & WS_VSCROLL)
              PostMessage(hwndDlg, DM_SCROLLLOGTOBOTTOM, 0, 0);
}
             LogStreamInEvents
static DWORD CALLBACK LogStreamInEvents(DWORD_PTR dwCookie, LPBYTE pbBuff, LONG cb, LONG * pcb)
{
       struct LogStreamData *dat = (struct LogStreamData *) dwCookie;
 
       if (dat->buffer == NULL) {
              dat->bufferOffset = 0;
              switch (dat->stage) {
                     case STREAMSTAGE_HEADER:
                            dat->buffer = CreateRTFHeader(dat->dlgDat);
                            dat->stage = STREAMSTAGE_EVENTS;
                            break;
                     case STREAMSTAGE_EVENTS:
                            if (dat->eventsToInsert) {
                                   do {
                                          dat->buffer = CreateRTFFromDbEvent(dat->dlgDat, dat->hContact, dat->hDbEvent, dat);
                                          if (dat->buffer)
                                                 dat->hDbEventLast = dat->hDbEvent;
                                          dat->hDbEvent = (HANDLE) CallService(MS_DB_EVENT_FINDNEXT, (WPARAM) dat->hDbEvent, 0);
                                          if (--dat->eventsToInsert == 0)
                                                 break;
                                   } while (dat->buffer == NULL && dat->hDbEvent);
                                   if (dat->buffer) {
                                          dat->isEmpty = 0;
                                          break;
                                   }
                            }
                            dat->stage = STREAMSTAGE_TAIL;
                            //fall through
                     case STREAMSTAGE_TAIL:
                            dat->buffer = CreateRTFTail(dat->dlgDat);
                            dat->stage = STREAMSTAGE_STOP;
                            break;
                     case STREAMSTAGE_STOP:
                            *pcb = 0;
                            return 0;
              }
              dat->bufferLen = lstrlenA(dat->buffer);
       }
       *pcb = min(cb, dat->bufferLen - dat->bufferOffset);
       CopyMemory(pbBuff, dat->buffer + dat->bufferOffset, *pcb);
       dat->bufferOffset += *pcb;
       if (dat->bufferOffset == dat->bufferLen) {
              free(dat->buffer);
              dat->buffer = NULL;
       }
       return 0;
}
            调试时从dat->buffer中抓到的rtf数据:
内容是: 小图标 + testzc@im.hc: + 111
RTF数据(注意每行后面的回车换行符号是为了容易阅读后来添加的,实际的数据是一个整个的文本串,中间没有回车换行符号):
 
{/rtf1/ansi/deff0
{/fonttbl
{/f0/fnil/fcharset134 宋体;}
{/f1/fnil/fcharset134 宋体;}
{/f2/fnil/fcharset134 宋体;}
{/f3/fnil/fcharset134 宋体;}
{/f4/fnil/fcharset134 宋体;}
{/f5/fnil/fcharset134 宋体;}
{/f6/fnil/fcharset134 宋体;}
{/f7/fnil/fcharset134 宋体;}
{/f8/fnil/fcharset134 宋体;}
{/f9/fnil/fcharset134 宋体;}}
{/colortbl /red106/green106/blue106;/red0/green0/blue0;
/red89/green89/blue89;/red0/green0/blue0;/red215/green0/blue0;
/red0/green0/blue0;/red90/green90/blue160;/red0/green0/blue128;}
/par/ltrpar/f0/fs14{/pict/dibitmap0/wbmbitspixel24/wbmplanes1
/wbmwidthbytes32/picw10/pich10
280000000A0000000A00000001001800000000004001000000000000000000000000000000000000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0000FFFFFFFFFFFFFFFFFF8E42358E42358E4235FFFFFFFFFFFFFFFFFFFFFFFF0000FFFFFF8E42358E4235FFFFFF8E4235CF864F8E4235FFFFFFFFFFFFFFFFFF0000FFFFFF8E4235CF864F8E4235FFFFFF8E4235CF864F8E4235FFFFFFFFFFFF0000FFFFFFFFFFFF8E4235E2A6728E4235FFFFFF8E4235E2A6728E4235FFFFFF0000FFFFFFFFFFFF8E4235E9B7858E4235FFFFFF8E4235E9B7858E4235FFFFFF0000FFFFFF8E4235F2C18E8E4235FFFFFF8E4235F2C18E8E4235FFFFFFFFFFFF0000FFFFFF8E42358E4235FFFFFF8E4235F2C18E8E4235FFFFFFFFFFFFFFFFFF0000FFFFFFFFFFFFFFFFFF8E42358E42358E4235FFFFFFFFFFFFFFFFFFFFFFFF0000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0000}
/f6/cf6/b0/i0/fs18 {/uc1 14:56:47}
/f5/cf5/b0/i0/fs18 {/uc1 testzc@im.hc}
/f7/cf7/b0/i0/fs18 :
/f1/cf1/b0/i0/fs18 {/uc1 111}
RichEdit 参考
EM_STREAMIN
The EM_STREAMIN message replaces the contents of a rich edit control with a stream of data provided by an application defined–EditStreamCallback callback function.
To send this message, call the SendMessage function with the following parameters.
SendMessage( 
  (HWND) hWnd,              // handle to destination window 
  EM_STREAMIN,              // message to send
 (WPARAM) wParam,          // format options
 (LPARAM) lParam           // data (EDITSTREAM *)
);
Parameters
wParam
Specifies the data format and replacement options. This value must be one of the following values.

Value
Meaning
SF_RTF
Rich Text Format (RTF)
SF_TEXT
Text
 
In addition, you can specify the following flags.

Value
Meaning
SFF_PLAINRTF
If specified, only keywords common to all languages are streamed in. Language-specific RTF keywords in the stream are ignored. If not specified, all keywords are streamed in. You can combine this flag with the SF_RTF flag.
SFF_SELECTION
If specified, the data stream replaces the contents of the current selection. If not specified, the data stream replaces the entire contents of the control. You can combine this flag with the SF_TEXT or SF_RTF flags.
SF_UNICODE
Rich Edit 2.0 and later: Indicates Unicode text. You can combine this flag with the SF_TEXT flag.
 
lParam
Pointer to an EDITSTREAM structure. On input, the pfnCallback member of this structure must point to an application defined–EditStreamCallback function. On output, the dwError member can contain a nonzero error code if an error occurred.
 
EDITSTREAM
The EDITSTREAM structure contains information that an application passes to a rich edit control in a EM_STREAMIN or EM_STREAMOUT message. The rich edit control uses the information to transfer a stream of data into or out of the control.
typedef struct _editstream { 
    DWORD_PTR dwCookie; 
    DWORD dwError; 
    EDITSTREAMCALLBACK pfnCallback; 
} EDITSTREAM; 
Members
dwCookie
Specifies an application-defined value that the rich edit control passes to the EditStreamCallback callback function specified by the pfnCallback member.
dwError
Indicates the results of the stream-in (read) or stream-out (write) operation. A value of zero indicates no error. A nonzero value can be the return value of the EditStreamCallback function or a code indicating that the control encountered an error.
pfnCallback
Pointer to an EditStreamCallback function, which is an application-defined function that the control calls to transfer data. The control calls the callback function repeatedly, transferring a portion of the data with each call.
 

RabbitMQ收发消息流程图

好像好久没写东西了,时隔三年回来写点东西,发现自己竟然跑偏了 大学时还整天想做图形方面的,怎么出来工作后走着走着就走弯了呢.....好悲伤... ​--------------------...
  • u010412301
  • u010412301
  • 2016年10月21日 17:20
  • 1761

jabber协议详解

Jabber 协议 概述 Peter Saint-Andre stpeter@jabber.org 1.4版Jabber服务协议的概述 1. 介绍     Jabber是一个由开源社区发起并...
  • dangfm
  • dangfm
  • 2014年06月11日 17:08
  • 2911

四大即时通讯协议之XMPP(Jabber)背景介绍(一)

四大即时通讯(IM)协议背景知识介绍 四大通讯协议分别为XMPP 协议、即时信息和空间协议(IMPP)、空间和即时信息协议(PRIM)、针对即时通讯和空间平衡扩充的进程开始协议SIP(SIMPLE)。...
  • kruskal123
  • kruskal123
  • 2016年02月23日 23:47
  • 1672

微信平台下两种消息处理流程

简单的微信公共账号的开发貌似很简单。相当于汇总了我们所有程序的入口。 但是微信的消息处理模式主要有两种,今天我们主要看看一下其间的区别。 1 编辑模式下的消息处理模式            ...
  • u010176014
  • u010176014
  • 2015年04月29日 16:50
  • 1487

jabber服务器搭建

经过几次努力,我终于在我的ubuntu上成功搭建起了jabber即时通讯服务器。由于是源码编译的所以没有放到ubuntu分类下。由于依赖于openssl和mysql,所以安装之前要把这两个准备好(如果...
  • hegaoye308444582
  • hegaoye308444582
  • 2016年07月28日 19:24
  • 715

短信的接收流程

从RIL开始从RIL开始,前面的分析已经知道发送短信在framework层最终是通过RILSender来进行的,接收短信也是从RIL的内部类RILReceiver开始的class RILReceive...
  • mockingbirds
  • mockingbirds
  • 2016年12月04日 12:52
  • 874

Android开发——Android的消息机制详解

1. 我们为什么需要Android的消息机制   我们知道,Android规定访问UI只能在主线程中进行。若在子线程中访问UI,就会抛出异常。这个验证由ViewRootImpl的checkThread...
  • SEU_Calvin
  • SEU_Calvin
  • 2016年08月04日 16:06
  • 12655

彩信发送和接收关键流程

MMS 的设计基于WAP协议; DataConnection 手机上网数据连接,其中5种常用类型的APN配置信息中就有MMS类型,即收发彩信时需要建立手机上网数据连接; MMS发送和接收,...
  • hongkang218
  • hongkang218
  • 2016年09月20日 16:48
  • 1327

Spring Boot教程(十四)Spring Boot整合ActiveQ实现消息收发和订阅

来源:素文宅博客 https://blog.yoodb.com/yoodb/article/detail/1426 javax.jms.ConnectionFactory接口提供了一个标准的用于创建...
  • afreon
  • afreon
  • 2017年11月13日 15:09
  • 166

gloox消息发送与接受剖析

解析的很好,赞个!原文地址:http://blog.csdn.net/qiuhong101/article/details/3514784 当你连接至服务器之后,你会获得一个客户端实例对象(Clien...
  • sinat_27242589
  • sinat_27242589
  • 2015年05月20日 15:14
  • 718
内容举报
返回顶部
收藏助手
不良信息举报
您举报文章:收发Jabber消息流程
举报原因:
原因补充:

(最多只允许输入30个字)