1.FeiQ
1.1 在主对话框的下线请求里先关闭一下map中的对话框
LRESULT CFeiQProjDlg::OnOffLineRQ(WPARAM wParam, LPARAM lParam)
{
... ...
if(m_lb_IPAddr.m_mp_sayDlg.count(psz_user_name) == 1)
{
map<CString, CSayDlg*>::iterator ite = m_lb_IPAddr.m_mp_sayDlg.find(psz_user_name);
ite->second->CloseWindow();
delete ite->second;
m_lb_IPAddr.m_mp_sayDlg.erase(ite);
}
return 0;
}
1.2 在发送消息对话框中添加 发送 按钮的响应函数
- 1.给 Edit 控件添加一个 value CString 的变量 m_str_send_content ;
- 2.给 ListBox 控件添加一个 control 类型的变量 m_ls_say_content ;
- 3.将 Edit 控件中的字符串取下来: UpdateData(TRUE) ;
- 4.定义一个聊天结构体 STRU_SAY ss ,用来存储 m_str_send_content 里的数据;
- 5.定义一个 sockaddr_in 来取得本地的 IP ;
- 6.发送 ss 这个聊天数据包,使用中介者的 SendData 进行发送数据;
- 7.给 ListBox 控件添加刚刚发送的数据;
- 8.我们还需给 CSayDlg 类添加一个获取对面IP的变量,类型为 CString ;
- 9.在 CListBoxIPAddr 类中的 InsertIP 函数中,创建完对话框之后,我们获取对话框对应的 IP ;
void CListBoxIPAddr::InsertIP(CString strIP)
{
if(strIP == L"" || m_mp_sayDlg.count(strIP) == 1)
return;
this->AddString(strIP);
CSayDlg* p_sayDlg = new CSayDlg;
p_sayDlg->Create(IDD_DIALOG1, this);
p_sayDlg->SetWindowText(strIP);
p_sayDlg->ShowWindow(SW_HIDE);
p_sayDlg->m_str_title_ip = strIP;
m_mp_sayDlg[strIP] = p_sayDlg;
}
void CSayDlg::OnBnClickedButton1()
{
UpdateData(TRUE);
STRU_SAY ss;
ss.pack_type = PROTOCL_SAY_CONTEXT;
strcpy_s(ss.sz_say_context, SAY_CONTEXT, m_str_send_content.GetBuffer());
sockaddr_in addr_in;
addr_in.sin_addr.S_un.S_addr = theApp.m_p_mediator->p_net->GetLocalIPAddr();
strcpy_s(ss.sz_user_name, NAME_SIZE, inet_ntoa(addr_in.sin_addr));
bool b_flag = theApp.m_p_mediator->SendData(inet_addr(m_str_title_ip), (const char*)&ss, sizeof(ss));
m_ls_say_content.AddString("I Say:");
m_ls_say_content.AddString(m_str_send_content);
m_str_send_content = L"";
UpdateData(FALSE);
}
1.3 当我们发送了一个聊天数据包之后,我们需要给中介者的处理数据包的函数中添加一个新的 case
- 1.将数据包强转为 STRU_SAY* ;
- 2.给主对话框发送一个自定义消息,这个自定义消息用来找到对应的IP并弹出相应的对话框,并将发送的数据进行显示;
bool CUDPMediator::DealData(ULONG uIP, char* pszSendBuffer, int nSendLen)
{
PackType* p_type = (PackType*)pszSendBuffer;
switch (*p_type)
{
... ...
case PROTOCL_SAY_CONTEXT:
{
STRU_SAY* ss = (STRU_SAY*)pszSendBuffer;
theApp.m_pMainWnd->SendMessage(UM_SAY_CONTEXT, 0, (LPARAM)ss);
}
break;
}
return true;
}
LRESULT CFeiQProjDlg::OnSayContext(WPARAM wParam, LPARAM lParam)
{
STRU_SAY* ss = (STRU_SAY*)lParam;
if(m_lb_IPAddr.m_mp_sayDlg.count(ss->sz_user_name) == 1)
{
m_lb_IPAddr.m_mp_sayDlg[ss->sz_user_name]->ShowWindow(SW_SHOW);
CString str;
str.Format("%s Say:", ss->sz_user_name);
m_lb_IPAddr.m_mp_sayDlg[ss->sz_user_name]->m_ls_say_content.AddString(str);
m_lb_IPAddr.m_mp_sayDlg[ss->sz_user_name]->m_ls_say_content.AddString(ss->sz_say_context);
}
return 0;
}
1.4 现在我们的协议较少,因此使用 switch case 语句并没有什么不妥,但是当我们的协议达到20多条甚至更多时,我们不可能写20+的case语句,因此我们需要想办法解决这个问题,提高算法的复用性
- 1.我们仿照消息映射表将这个 switch case 语句改写为 协议-函数指针 映射表;
- 2.首先定义一个结构体用来存放 协议 和 函数指针;
- 3.函数指针我们定义一个 C++成员函数指针 ,这是为了=TODO=====
- 4.在中介者的源文件中定义一个结构体数组,用来存放已经完成的四个对应关系;
typedef bool (CUDPMediator::*PFUN)(ULONG uIP, char* pszSendBuffer, int nSendLen);
struct PROTOCOL_MAP
{
PackType my_type;
PFUN p_fun;
};
PROTOCOL_MAP protocol_map[] = {
{PROTOCL_ONLINE_RQ, &CUDPMediator::OnOnLineRQ},
{PROTOCL_ONLINE_RS, &CUDPMediator::OnOnLineRS},
{PROTOCL_OFFLINE_RQ, &CUDPMediator::OnOffLineRQ},
{PROTOCL_SAY_CONTEXT, &CUDPMediator::OnSayText},
{0, 0}};
- 5.最后那个多出来的 {0, 0} 是用来作为结束标志的;
- 6.创建协议响应函数: bool OnOnLineRQ(ULONG uIP, char* pszSendBuffer, int nSendLen) ; bool OnOnLineRS(ULONG uIP, char* pszSendBuffer, int nSendLen) ; bool OnOffLineRQ(ULONG uIP, char* pszSendBuffer, int nSendLen) ; bool OnSayText(ULONG uIP, char* pszSendBuffer, int nSendLen) ;
- 7.在源文件中完成这些函数,每个函数的代码与其各自对应的 case 中的代码完全一样;
bool CUDPMediator::OnOnLineRQ(ULONG uIP, char* pszSendBuffer, int nSendLen)
{
STRU_ONLINE_RQ* soRQ = (STRU_ONLINE_RQ*)pszSendBuffer;
theApp.m_pMainWnd->SendMessage(UM_ONLINE_RQ, 0, (LPARAM)(soRQ->sz_user_name));
STRU_ONLINE_RS soRS;
soRS.pack_type = PROTOCL_ONLINE_RS;
sockaddr_in addr;
addr.sin_addr.S_un.S_addr = p_net->GetLocalIPAddr();
strcpy_s(soRS.sz_user_name, NAME_SIZE, inet_ntoa(addr.sin_addr));
this->SendData(inet_addr(soRQ->sz_user_name), (const char*)&soRS, sizeof(soRS));
return true;
}
bool CUDPMediator::OnOnLineRS(ULONG uIP, char* pszSendBuffer, int nSendLen)
{
STRU_ONLINE_RS* soRS = (STRU_ONLINE_RS*)pszSendBuffer;
theApp.m_pMainWnd->SendMessage(UM_ONLINE_RS, 0, (LPARAM)(soRS->sz_user_name));
return true;
}
bool CUDPMediator::OnOffLineRQ(ULONG uIP, char* pszSendBuffer, int nSendLen)
{
STRU_OFFLINE_RQ* soRQ = (STRU_OFFLINE_RQ*)pszSendBuffer;
theApp.m_pMainWnd->SendMessage(UM_OFFLINE_RQ, 0, (LPARAM)soRQ->sz_user_name);
return true;
}
bool CUDPMediator::OnSayText(ULONG uIP, char* pszSendBuffer, int nSendLen)
{
STRU_SAY* ss = (STRU_SAY*)pszSendBuffer;
theApp.m_pMainWnd->SendMessage(UM_SAY_CONTEXT, 0, (LPARAM)ss);
return true;
}
- 8.在中介者的数据处理函数中遍历 协议-函数指针 映射表,若当前协议在表中,则通过函数指针调用函数完成相关操作;
- 9.其中的 protocol_map 为结构体数组;
bool CUDPMediator::DealData(ULONG uIP, char* pszSendBuffer, int nSendLen)
{
PackType* p_type = (PackType*)pszSendBuffer;
PROTOCOL_MAP* p_temp = protocol_map;
while (p_temp->my_type != 0)
{
if(p_temp->my_type == *p_type && p_temp->p_fun != 0)
{
return (this->*(p_temp->p_fun))(uIP, pszSendBuffer, nSendLen);
}
p_temp++;
}
return false;
}
#define PROTOCOL_MAP_BEGIN() PROTOCOL_MAP protocol_map[] = {
#define PROTOCOL_MAP_END() {0, 0}};
#define PROTOCOL_MESSAGE(PACKTYPE, PROTOCOLFUN) {PACKTYPE, PROTOCOLFUN},
PROTOCOL_MAP_BEGIN()
PROTOCOL_MESSAGE(PROTOCL_ONLINE_RQ, &CUDPMediator::OnOnLineRQ)
PROTOCOL_MESSAGE(PROTOCL_ONLINE_RS, &CUDPMediator::OnOnLineRS)
PROTOCOL_MESSAGE(PROTOCL_OFFLINE_RQ, &CUDPMediator::OnOffLineRQ)
PROTOCOL_MESSAGE(PROTOCL_SAY_CONTEXT, &CUDPMediator::OnSayText)
PROTOCOL_MAP_END()