飞鸽传书源码分析四-消息发送
来源:互联网 发布:阿里云产品主要优势 编辑:程序博客网 时间: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抓取的使用飞鸽传书发送消息的一个数据包。
- 飞鸽传书源码分析四-消息发送
- IPMsg(飞鸽传书)文件发送源码分析
- 飞鸽传书源码分析二消息机制
- 飞鸽传书源码分析二消息机制
- IPMSG飞鸽传书7——文件发送源码分析
- RocketMQ源码分析----发送消息
- 飞鸽传书源码分析三-网络
- 飞鸽传书源码分析五-文件传输
- 飞鸽传书源码分析三-网络
- 飞鸽传书源码分析-程序启动过程
- 飞鸽传书源码分析-程序启动过程
- 飞鸽传书源码分析六-文件传输续
- 飞鸽传书源码分析-程序启动过程
- RTMPdump源码分析: 发送消息(Message)(8)
- MPTCP 源码分析(四) 发送和接收数据
- MPTCP 源码分析(四) 发送和接收数据
- 飞鸽传书源码
- 飞鸽传书最新C++源码:这两个消息钩子
- 基本的数据类型和引用数据类型的区别
- Failed to read row 0, column -1 from a CursorWindow which h
- Android--数据传递
- C语言深度解剖——跟程序员有关
- 20个 Unix/Linux 命令技巧
- 飞鸽传书源码分析四-消息发送
- Jsoup解析html的一些使用技巧
- 【字符串】KMP算法
- 解决php-memcache set,用python-memcache get报错的问题
- 【C语言】编写一个程序从标准输入读取字符,并把他们写到标准输出。除了大写字母转换成小写字母之外,其他的原样输出。
- 测试网络是否连接
- java中字符串快速追加
- 动作识别之STIP (Space-Time Interest Points)(一)
- MATLAB神经网络编程(三)——线性神经网络的构建与实现