1、目录2012.7.5.3BatBite 开发档案 .3一、 目标需求 .3二、 功能概述 .3三、 使用的技术 .3四、 功能设计 .31. 在线列表显示局域网内所有在线用户 .3五、 主要代码 .5获取主机名和 ip 地址 .5由句柄获取指针 .6添加(Post 非阻塞) 消息的步骤 .6创建非模态对话框步骤 .6消息内容传递给非模态对话框并且判断是否已经打开相同 ip 的对话框 .7ip 网络字节序转换为点分十进制字符串 CString .7遇到的第一个比较棘手的问题(野指针问题,已解决) .8非模态对话框总是在前 .92012.7.7.9遇到第二个比较棘手的问题(野指针已解决) .9遇到
2、第三个比较棘手的问题(CString 和 CPtrList 的查找 已解决!) .10手动添加对话框窗口的关闭事件 .11从 IpAddressCtrl 获取 DWORD 类型 ip 然后转换成 CString 显示 .12CPtrList 的简单用法 .122012.7.8.13遇到第四个问题 .13设计在线名单显示 .14通过链表记录在线成员 .14TreeListCtrl 用法 .15第五个棘手的问题 .262012.7.9 发送广播 .26改用 CListCtrl 显示在线信息 .26设计 ShowList()函数 .27所有要发送的广播信息数据如下: .28难点!设计如何知道是否离线
3、 .29通过句柄 m_hWnd 获取该窗口指针 .292012.7.10 接收广播 .29打包数据 拆包 数据,错误 .29第六个棘手的问题(线程的 while 循环位置 已解决!) .30接收广播分 4 类 .311 接收信息广播 .31更新显示 CListCtrl.322012.7.11.32发送聊天内容 .322012.7.15.32发送群聊 .322012.7.16.33接收显示群聊 .332012.7.5BatBite 开发档案一、 目标需求BB 实现类似于飞鸽传书的功能,可以显示局域网在线用户,首发消息文件。并能多发或者建立群聊。总之就是局域网内功能很强大的通信工具。二、 功能概述
4、1. 在线列表显示局域网内所有在线用户三、 使用的技术多线程消息机制线程同步:临界区套接字编程,udp 广播,熟悉各种类型和字节序转换非模态对话框链表的增删改查四、 功能设计1. 在线列表显示局域网内所有在线用户通过 udp 套接字在程序启动的时候对本局域网内所有主机发送广播,每秒发送一次,再通过一个侦听的套接字收取此广播,然后拆包,通过 ip 信息等数据显示在列表中.实现:添加 CBroadcastSocket 类:其中添加 SOCK m_broadcast 和 bool CBroadcastSocket: InitBroadcast()bool CBroadcastSocket: Init
5、Broadcast()m_broadcast = socket(AF_INET, SOCK_DGRAM, 0);if(INVALID_SOCKET = m_broadcast)AfxMessageBox(“broadcast套接字创建失败!“);return FALSE;SOCKADDR_IN addrbroadcast;addrbroadcast.sin_family = AF_INET;addrbroadcast.sin_addr.S_un.S_addr = htonl(INADDR_ANY);addrbroadcast.sin_port = htons(6000);int retval;
6、retval = bind(m_broadcast, (SOCKADDR *)if(SOCKET_ERROR = retval)AfxMessageBox(“broadcast套接字绑定失败!“);return FALSE;return TRUE;线程函数用于发送广播DWORD WINAPI BroadProc(void *lpParameter)SOCKET sock = (broadcastParam *)lpParameter)-sock;HWND hwnd = (broadcastParam *)lpParameter)-hwnd;SOCKADDR_IN addrto;addrto.s
7、in_family=AF_INET;addrto.sin_addr.s_addr=htonl(INADDR_BROADCAST);addrto.sin_port=htons(6000);int len = sizeof(SOCKADDR);int ret;char buffer20=“broadcast msg“;while(1)Sleep(3000);/发送广播ret = sendto(sock,buffer,sizeof(buffer),0,(SOCKADDR *)if(SOCKET_ERROR = ret)AfxMessageBox(“广播发送失败“);else/AfxMessageBo
8、x(“广播发送成功“);return 0;五、 主要代码获取主机名和 ip 地址char name100=“;gethostname(name,sizeof(name);struct hostent FAR *ph =gethostbyname(name);char buffer200 = “;memcpy(buffer,inet_ntoa(*(struct in_addr *)*ph-h_addr_list),strlen(ph-h_addr_list0)+1);因为 hostent 结构支持多种地址类型,所以其定义的 h_addr_list 是 char *型。gethostbyname
9、以后,实际存储情况是这样:cpp view plaincopy1. hostent-h_addr_list00 = 127 2. hostent-h_addr_list01 = 0 3. hostent-h_addr_list02 = 0 4. hostent-h_addr_list03 = 1 得到的地址是网络字节顺序。转成正常的字符:cpp view plaincopy1. char *ip; 2. ip = inet_ntoa(*(struct in_addr *)*phost-h_addr_list); 由句柄获取指针CWnd:FromHandle static CWnd* PASCA
10、L FromHandle( HWND hWnd );Return ValueReturns a pointer to a CWnd object when given a handle to a window. If a CWnd object is not attached to the handle, a temporary CWnd object is created and attached. The pointer may be temporary and should not be stored for later use.ParametershWndAn HWND of a Wi
11、ndows window.添加(Post 非阻塞)消息的步骤1提报消息:PostMessage(hwnd,WM_RECVBROAD,0,(LPARAM)buffer);2. 在头文件加#define WM_RECVCHAT WM_USER+2 消息 ID3. .cpp的消息队列中加消息映射ON_MESSAGE(WM_RECVBROAD,OnRecvChat)4. 头文件加消息响应函数afx_msg LRESULT OnRecvChat(WPARAM wParam,LPARAM lParam);5. .cpp实现消息响应函数创建非模态对话框步骤m_pChatDlg = new CChatDlg(
12、);int ret;ret = m_pChatDlg-Create(IDD_DIALOG_CHAT,this);if(!ret)AfxMessageBox(“对话框窗口创建失败“);m_pChatDlg-ShowWindow(SW_SHOW);后处理:在关闭按钮事件中if(!DestroyWindow()AfxMessageBox(“对话框窗口关闭失败“);return;delete this;要手动关闭窗口,还要释放内存消息内容传递给非模态对话框并且判断是否已经打开相同ip 的对话框通过 ip 地址判断是否是同一个对话框在接受聊天信息线程函数中:/传递ip地址为wParamCString s
13、trIp = inet_ntoa(addrchat.sin_addr);memcpy(ip,strIp,strlen(strIp)+1);/添加消息机制/AfxMessageBox(buffer);if(:PostMessage(hwnd,WM_RECVCHAT,(WPARAM)ip,(LPARAM)buffer)提报消息的时候 wParam用上,传递 ip地址ip 网络字节序转换为点分十进制字符串 CString注意 addrchat.sin_addr 就可以了,不用再往下写了,不然格式不对了CString strIp = inet_ntoa(addrchat.sin_addr);同一个 i
14、p的只弹一个聊天对话框:遇到的第一个比较棘手的问题(野指针问题,已解决)问题是当点击发送给单一 ip 地址消息时,出现上图 assertion 错误。经过反复排查,发现:问题出现在指针 p 指向的是空,因为第一次 m_ChatDlgList 没有任何记录!MSDN 上也写了。所以,当使用指针的时候,要做检查,看是否为 NULL!非模态对话框总是在前可以设置父类指针为桌面,这样优先级就降级了pChatDlg-Create(IDD_DIALOG_CHAT,this-GetDesktopWindow()2012.7.7遇到第二个比较棘手的问题(野指针已解决)在实现收到同一个 ip 只激活一个非模态对
15、话框时出错。1 个 ip 的正确,但是当第二个 ip 的传来消息,再弹出的时候就会出现 Assertion 的错误!经过使用断点调试,发现问题出在:还是野指针问题,应为 pos 可能为 NULL 找不到,所以 p 指向不明确!而且 Search 函数有问题:修正 while 循环加一个 count 使 GetNext(pos)不出错遇到第三个比较棘手的问题(CString 和 CPtrList 的查找 已解决!)就是第二个窗口会弹出 2 次!经过调查,当一个 ip1 窗口创建后,创建 ip2 窗口的时候,会仍然用 ip1 窗口的 strIp 值,然后再次创建 ip2 窗口才用 strip2 的值。这里面应该是 CString 的问题,在问题重现中有简易的版本而且比原版问题更严重的是 自第二个节点起,没有了判断重复的功能经过一天的排查,终于发现根源所在: