飞鸽传书源码分析四-消息发送

来源:互联网 发布:阿里云产品主要优势 编辑:程序博客网 时间:2024/05/22 09:02

转载请注明出处:http://blog.csdn.net/mxway/article/details/44569831
本篇文章是在飞鸽传书源码v2.06的基础上进行分析的
飞鸽传书是一款工作在局域网的软件,支持局域网里不同设备之间的消息发送及文件的传输(消息发送使用udp,文件传输使用tcp)。
发送消息及文件传输是在飞鸽传书的发送对话框中进行,而发送对话框的打开是通过双击拖盘(win7系统)到任务栏的图标。而拖盘到任务栏的这个图标就是飞鸽传书的主窗口,对应的源码就是Mainwin.cpp中的TMainWin类。下面是TMainWin类处理双击事件,鼠标双击事件为什么是由EventButton进行处理可以参见第二篇文章-消息机制

BOOL TMainWin::EventButton(UINT uMsg, int nHitTest, POINTS pos){    switch (uMsg)    {         ...    case WM_LBUTTONDBLCLK:    case WM_NCLBUTTONDBLCLK:        if (cfg->OneClickPopup == FALSE)            SendDlgOpen();        return  TRUE;         ...    }}BOOL TMainWin::SendDlgOpen(HWND hRecvWnd, MsgBuf *msg){    TSendDlg *sendDlg;    ...    if ((sendDlg = new TSendDlg(msgMng, shareMng, &hosts, cfg, logmng, hRecvWnd, msg)) == NULL)        return  FALSE;    sendList.AddObj(sendDlg);    sendDlg->Create(), sendDlg->Show();    ...}BOOL TDlg::Create(HINSTANCE hInstance){    TApp::AddWin(this);    if ((hWnd = ::CreateDialog(hInstance ? hInstance : TApp::hI, resId ? (LPCSTR)resId : resName, parent ? parent->hWnd : NULL, (DLGPROC)TApp::WinProc)) == NULL)        return  TApp::DelWin(this), FALSE;    else        return  TRUE;}

发送消息的对话框类似如下
这里写图片描述

在发送对话框中在编辑框中输入要发送的内容,在用户列表中选择要发送的对象,点send按钮就可以将消息发送到对方。这里可以选择对发送的消息是否进行加密,为了简化问题这里只分析最简单的消息发送。

一、飞鸽传书消息发送格式
飞鸽传书版本:数据包唯一编号:用户名:机器名称:命令:真实消息
(1)飞鸽传书版本是一个宏定义

#define IPMSG_VERSION           0x0001

(2)数据包唯一编号由飞鸽传书程序自动生成的。
(3)用户名:如果用户没有使用飞鸽传书设置自己的名称,默认使用的是当前登录系统的用户名
(4)机器名称,设备的名称如PC设置的计算机名称
(5)命令,命令是一个32位的无符号数,其功能分为两部分,后8位用于必选的命令,表示当对方收到该命令后要什么事,如用户上线通知,用户退出通知,发送消息。前面24位用于可选命令,如对传送消息进行加密,获取加密的密钥等。
(6)真实消息,这个是真正要发给对方的信息,包括要发送对方的消息及要传送给对方的文件信息。
生成上述格式消息的代码如下

ULONG MsgMng::MakeMsg(char *buf, int _packetNo, ULONG command, const char *msg, const char *exMsg, int *packet_len){    int     len, ex_len = exMsg ? strlen(exMsg) + 1 : 0, max_len = MAX_UDPBUF;    if (packet_len == NULL)        packet_len = &len;        //版本:数据包编号:用户名:设备名称:命令:    *packet_len = wsprintf(buf, "%d:%ld:%s:%s:%ld:", IPMSG_VERSION, _packetNo, local.userName, local.hostName, command);    if (ex_len + *packet_len + 1 >= MAX_UDPBUF)        ex_len = 0;    max_len -= ex_len;    if (msg != NULL)    //LocalNewLineToUnix把\r\n转换为\n        *packet_len += LocalNewLineToUnix(msg, buf + *packet_len, max_len - *packet_len);    (*packet_len)++;    if (ex_len)    {        //如果有附加消息(如文件发送,同时发送附加的消息)        memcpy(buf + *packet_len, exMsg, ex_len);        *packet_len += ex_len;    }    return  _packetNo;}

二、消息发送
单击send按钮的实现代码如下:

BOOL TSendDlg::EvCommand(WORD wNotifyCode, WORD wID, LPARAM hWndCtl){    switch (wID)    {    case IDOK:        ...        SendMsg();        ...    }}BOOL TSendDlg::SendMsg(void){    command = IPMSG_SENDMSG|IPMSG_SENDCHECKOPT;    //获取选中数    if ((sendEntryNum = (int)SendDlgItemMessage(HOST_LIST, LVM_GETSELECTEDCOUNT, 0, 0)) <= 0 || (sendEntry = new SendEntry [sendEntryNum]) == NULL)        return  FALSE;    //获取要发送的消息数据    GetDlgItemText(SEND_EDIT, msg.msgBuf, MAX_UDPBUF);    int storeCnt = 0, status = 0, cnt;    int localStatus = sendEntryNum <= cfg->EncryptNum && (cfg->pubKey.Key() || cfg->smallPubKey.Key()) ? IPMSG_ENCRYPTOPT : 0;    //获取选中的host信息,host信息是由TMainWin传过来的    for (cnt=0; cnt < memberCnt && storeCnt < sendEntryNum; cnt++)    {        if ((SendDlgItemMessage(HOST_LIST, LVM_GETITEMSTATE, cnt, LVIS_SELECTED) & LVIS_SELECTED) == 0)            continue;        char        hostStr[MAX_LISTBUF];        Host        *host = hostArray[cnt];        SendEntry   *entry = &sendEntry[storeCnt++];        //发送的消息要进行加密        status |= host->hostStatus & IPMSG_ENCRYPTOPT;        MakeListString(cfg, host, hostStr);        logmng->WriteSendHead(hostStr);        entry->SetHost(host);        entry->SetStatus((localStatus & host->hostStatus) ? host->pubKey.Key() == NULL ? ST_GETCRYPT : ST_MAKECRYPTMSG : ST_MAKEMSG);        entry->SetCommand(command | (entry->Status() == ST_MAKEMSG ? 0 : IPMSG_ENCRYPTOPT));    }    //发送的消息太长进行截断    msg.msgBuf[MAX_CRYPTLEN] = 0;       if (status &= localStatus)          command |= IPMSG_ENCRYPTOPT;    logmng->WriteSendMsg(msg.msgBuf, command, shareInfo);    if (shareInfo && shareInfo->fileCnt)        // ...\0no:fname:size:mtime:    {        //如果选中的文件或文件夹进行传输,生成要传输的文件信息        char    buf[MAX_UDPBUF / 2];        EncodeShareMsg(shareInfo, buf, sizeof(buf));        shareStr = new char [strlen(buf) + 1];        strcpy(shareStr, buf);        shareMng->AddHostShare(shareInfo, sendEntry, sendEntryNum);    }    //真正进行发送消息的函数    SendMsgSub();    return  TRUE;}BOOL TSendDlg::SendMsgSub(void){    BOOL    makeNomalMsg = TRUE;        for (int cnt=0; cnt < sendEntryNum; cnt++)    {        //如果需要从要发送的客户那获取加密的密钥,先获取密钥        if (sendEntry[cnt].Status() == ST_GETCRYPT) {            char    spec_str[MAX_BUF];            int     spec = IPMSG_RSA_512 | IPMSG_RC2_40;            if (cfg->pubKey.Key())                spec |= IPMSG_RSA_1024 | IPMSG_BLOWFISH_128;            wsprintf(spec_str, "%x", spec);            msgMng->Send(&sendEntry[cnt].Host()->hostSub, IPMSG_GETPUBKEY, spec_str);        }        //对要发送的数据进行加密        if (sendEntry[cnt].Status() == ST_MAKECRYPTMSG) {            MakeEncryptPacket(sendEntry + cnt);     // ST_MAKECRYPTMSG -> ST_SENDMSG        }        if (sendEntry[cnt].Status() == ST_MAKEMSG) {            sendEntry[cnt].SetStatus(ST_SENDMSG);            if (makeNomalMsg)                msgMng->MakeMsg(msgBuf, packetNo, command & ~IPMSG_ENCRYPTOPT, msg.msgBuf, shareStr, &packetLen), makeNomalMsg = FALSE;        }        //在MakeEncryptPacket对消息加密完成后设置host状态为ST_SENDMSG        if (sendEntry[cnt].Status() == ST_SENDMSG) {            const char  *str = sendEntry[cnt].Msg() ? sendEntry[cnt].Msg() : msgBuf;            int     len = sendEntry[cnt].Msg() ? sendEntry[cnt].MsgLen() : packetLen;            //向选中的机器发送数据            msgMng->UdpSend(sendEntry[cnt].Host()->hostSub.addr, sendEntry[cnt].Host()->hostSub.portNo, str, len);        }    }    return  TRUE;}

下面是使用wireshark抓取的使用飞鸽传书发送消息的一个数据包。
这里写图片描述

1 0
原创粉丝点击