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

19 篇文章 3 订阅

转载请注明出处: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抓取的使用飞鸽传书发送消息的一个数据包。
这里写图片描述

  • 2
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值