操作系统与网络 2019-3-27

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中的节点
		map<CString, CSayDlg*>::iterator ite = m_lb_IPAddr.m_mp_sayDlg.find(psz_user_name);

		// ==============3-27=============
		ite->second->CloseWindow();
		// ==============3-27=============

		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);

	// ===============3-27================
	p_sayDlg->m_str_title_ip = strIP;				// 把窗口上的IP地址记录下来
	// ===============3-27================

	// 添加到容器中
	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);

	// 清空 Edit 控件中的字符
	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;

	// 找到这个IP对应的对话框
	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.在中介者的源文件中定义一个结构体数组,用来存放已经完成的四个对应关系;
// 定义这两个东西在 UDPMediator.h 中
// C++成员函数指针,定义的时候 类名::* ,调用的时候 .* 或者 ->*
typedef bool (CUDPMediator::*PFUN)(ULONG uIP, char* pszSendBuffer, int nSendLen);		
struct PROTOCOL_MAP
{
	PackType my_type;
	PFUN p_fun;
};

// 定义结构体数组在 UDPMediator.cpp 文件中
// 协议映射表
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;
	// 1.把对方发来的IP插入到本地窗口上(通过给主对话框发送消息来进行)
	theApp.m_pMainWnd->SendMessage(UM_ONLINE_RQ, 0, (LPARAM)(soRQ->sz_user_name));
	// 2.给对方发送一个上线回复包
	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;
}
  • 10.将结构体数组更改为宏定义;
// 在 UDPMediator.h 文件中完成
// 将协议映射表改成类似于消息映射表的样式
#define PROTOCOL_MAP_BEGIN()							PROTOCOL_MAP protocol_map[] = {
#define PROTOCOL_MAP_END()								{0, 0}};
#define PROTOCOL_MESSAGE(PACKTYPE, PROTOCOLFUN)			{PACKTYPE, PROTOCOLFUN},

// 改写 UDPMediator.cpp 文件中的结构体数组
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()

猜你喜欢

转载自blog.csdn.net/weixin_42896619/article/details/88942115