在工作线程创建发送和接收套接字,设置他们的属性,将接受套接字加入到会议组,之后进入无限循环再m_sRead套接字上读取到来的UDP封包,然后调用DispatchMSG可以处理这些封装包。DispatchMsg处理来自组中其他成员的消息,发送针对这些消息的响应,通知窗口。下面就是实现的代码.
// 处理到来的消息,将它们分发到主窗口
void CGroupTalk::DispatchMsg(GT_HDR *pHeader, int nLen)
{
if(pHeader->gt_type == MT_JION) // 新用户加入
{
// 向新加入用户发送自己的用户信息
char buff[sizeof(GT_HDR)] = { 0 };
GT_HDR *pSend = (GT_HDR*)buff;
strncpy(pSend->szUser, m_szUser, 15);
pSend->gt_type = MT_MINE;
pSend->nDataLength = 0;
Send(buff, sizeof(GT_HDR), pHeader->dwAddr);
}
else if(pHeader->gt_type == MT_MINE)
{
// 是否来自自己,如果是,则不处理
if(strcmp(pHeader->szUser, m_szUser) == 0)
return;
// 为简单起见,把在线用户当成新加入用户处理
pHeader->gt_type = MT_JION;
}
// 通知主窗口处理
::SendMessage(m_hNotifyWnd, WM_GROUPTALK, 0, (LPARAM)pHeader);
}
说明:
1.Send(buff, sizeof(GT_HDR), pHeader->dwAddr);
第一个参数buff是一个内存块,将它轻质类型转换成用于存储GT_HDR结构数据的地方,这个GT_HDR的内存块就是所谓的“包”。
2.::SendMessage(m_hNotifyWnd, WM_GROUPTALK, 0, (LPARAM)pHeader);
发送一个WM_GROUPTALK消息到窗口函数,其中第四个参数是消息的WM_GROUPTALK的额外信息,也就是关于GT_HDR包的信息。
最后,提供给用户的发送消息函数SendText的实现代码如下:
int CGroupTalk::Send(char *szText, int nLen, DWORD dwRemoteAddr)
{
// 发送UDP封包
sockaddr_in dest;
dest.sin_family = AF_INET;
dest.sin_addr.S_un.S_addr = dwRemoteAddr;
dest.sin_port = ::ntohs(GROUP_PORT);
return ::sendto(m_sSend, szText, nLen, 0, (sockaddr*)&dest, sizeof(dest));
}
程序界面部分:
BOOL __stdcall DlgProc(HWND hDlg, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
switch(uMsg)
{
case WM_INITDIALOG:
{
// 创建CGroupTalk对象
g_pTalk = new CGroupTalk(hDlg, ::inet_addr("234.5.6.7"));
::CheckDlgButton(hDlg, IDC_SELGROUP, 1);
::SendMessage(hDlg, WM_SETICON, ICON_SMALL,
(long)::LoadIcon(::GetModuleHandle(NULL), (LPCTSTR)IDI_MAIN));
}
break;
case WM_GROUPTALK:
{
// 处理CGroupTalk对象发来的消息
if(wParam != 0)
::MessageBox(hDlg, (LPCTSTR)lParam, "出错!", 0);
else
HandleGroupMsg(hDlg, (GT_HDR*)lParam);
}
break;
case WM_COMMAND:
switch(LOWORD(wParam))
{
case IDC_SEND: // 用户按下发送消息按钮
{
// 取得要发送的消息
char szText[1024];
int nLen = ::GetWindowText(::GetDlgItem(hDlg, IDC_SENDMSG), szText, 1024);
if(nLen == 0)
break;
// 是面向组,还是面向用户?
BOOL bToAll = ::IsDlgButtonChecked(hDlg, IDC_SELGROUP);
DWORD dwAddr;
if(bToAll)
{
dwAddr = 0;
}
else
{
int nIndex = ::SendDlgItemMessage(hDlg, IDC_USERS, CB_GETCURSEL, 0, 0);
if(nIndex == -1)
{
::MessageBox(hDlg, "请选择一个用户!", "GroupTalk", 0);
break;
}
// 取得用户IP地址
dwAddr = ::SendDlgItemMessage(hDlg, IDC_USERS, CB_GETITEMDATA, nIndex, 0);
}
// 发送消息
if(g_pTalk->SendText(szText, nLen, dwAddr) == nLen)
::SetWindowText(::GetDlgItem(hDlg, IDC_SENDMSG), "");
}
break;
case IDC_CLEAR: // 用户按下清除按钮
::SendDlgItemMessage(hDlg, IDC_RECORD, LB_RESETCONTENT, 0, 0);
break;
case IDCANCEL: // 用户关闭程序
{
delete g_pTalk;
::EndDialog (hDlg, IDCANCEL);
}
break;
}
break;
}
return 0;
}
说明:
1. HandleGroupMsg(hDlg, (GT_HDR*)lParam);
所有的CGroupTalk发送来的消息都由HandleGroupMsg函数来处理,它将消息通知个用户,维护组员的列表,具体代码如下:
oid HandleGroupMsg(HWND hDlg, GT_HDR *pHeader)
{
switch(pHeader->gt_type)
{
case MT_JION: // 新用户加入
{
// 显示给用户
char szText[56];
wsprintf(szText, " 用户:《%s》加入!", pHeader->szUser);
::SetWindowText(::GetDlgItem(hDlg, IDC_SYSMSG), szText);
// 将新用户信息添加到列表框中
int nCurSel = ::SendDlgItemMessage(hDlg, IDC_USERS, CB_GETCURSEL, 0, 0);
int nIndex = ::SendDlgItemMessage(hDlg, IDC_USERS, CB_ADDSTRING, 0, (long)pHeader->szUser);
::SendDlgItemMessage(hDlg, IDC_USERS, CB_SETITEMDATA, nIndex, (long)pHeader->dwAddr);
if(nCurSel == -1)
nCurSel = nIndex;
::SendDlgItemMessage(hDlg, IDC_USERS, CB_SETCURSEL, nCurSel, 0);
}
break;
case MT_LEAVE: // 用户离开
{
// 显示给用户
char szText[56];
wsprintf(szText, " 用户:《%s》离开!", pHeader->szUser);
::SetWindowText(::GetDlgItem(hDlg, IDC_SYSMSG), szText);
// 将离开的用户从列表框中移除
int nCount = ::SendDlgItemMessage(hDlg, IDC_USERS, CB_GETCOUNT, 0, 0);
for(int i=0; i<nCount; i++)
{
int nIndex = ::SendDlgItemMessage(hDlg, IDC_USERS, CB_FINDSTRING, i, (long)pHeader->szUser);
if((DWORD)::SendDlgItemMessage(hDlg, IDC_USERS, CB_GETITEMDATA, nIndex, 0) == pHeader->dwAddr)
{
::SendDlgItemMessage(hDlg, IDC_USERS, CB_DELETESTRING, nIndex, 0);
break;
}
}
}
break;
case MT_MESG: // 用户发送消息
{
char *psz = pHeader->data();
psz[pHeader->nDataLength] = '/0';
char szText[1024];
wsprintf(szText, "【%s 说】", pHeader->szUser);
strncat(szText, psz, 1024 - strlen(szText));
::SendDlgItemMessage(hDlg, IDC_RECORD, LB_INSERTSTRING, 0, (long)szText);
}
break;
}
}
代码分析结束,陆续会将一些细节问题进行讨论,然后发布。