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

1.FeiQ 项目

1.1 双击某个选中的IP之后弹出一个对话框(可以进行信息交互的对话框)

  • 1.与昨天双击获取的IP相关;
  • 2.首先新建一个对话框,在其上添加 listbox 控件用来显示交互的信息, edit 控件用来进行输入信息, button 按钮用来作为发送信息的按钮;
  • 3.给新建的对话框添加一个类 CSayDlg ;
  • 4.在主对话框的 listbox 类中添加一个容器来盛放IP和SayDlg对话框的对象;
  • 5.在 listbox 类中添加一个函数 InsertIP 函数;
  • 6.在 listbox 类的析构函数中清空这个容器;
CListBoxIPAddr::~CListBoxIPAddr()
{
	map<CString, CSayDlg*>::iterator ite = m_mp_sayDlg.begin();
	while (ite != m_mp_sayDlg.end())
	{
		delete ite->second;
		ite++;
	}
	m_mp_sayDlg.clear();
}

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->SetWindowTextW(strIP);
	p_sayDlg->ShowWindow(SW_HIDE);

	// 添加到容器中
	m_mp_sayDlg[strIP] = p_sayDlg;
}
  • 7.对于 InsertIP ,我们在主对话框类中将插入的IP处调用;
BOOL CFeiQProjDlg::OnInitDialog()
{
	... ...

	// ==========================3-21================================
	// 添加的测试数据
	m_lb_IPAddr.InsertIP(_T("192.168.3.25"));
	m_lb_IPAddr.InsertIP(_T("192.168.3.5"));
	m_lb_IPAddr.InsertIP(_T("192.168.3.15"));
	// ==========================3-21================================

	return TRUE;  // 除非将焦点设置到控件,否则返回 TRUE
}
  • 8.在选中IP之后,我们显示窗口;
void CListBoxIPAddr::OnLbnDblclk()
{
	// ====================3-21==================
	// 双击时选中ip
	CString str_ip;
	int index = this->GetCurSel();
	if(index != LB_ERR)
	{
		this->GetText(index, str_ip);
	}
	// ====================3-21==================

	// =====================3-22=============================
	if(m_mp_sayDlg.count(str_ip) == 1)
		m_mp_sayDlg[str_ip]->ShowWindow(SW_SHOW);
	// =====================3-22=============================
}

1.2 过滤回车键,在主对话框中重写 PreTranslateMessage 函数

BOOL CFeiQProjDlg::PreTranslateMessage(MSG* pMsg)
{
	// 过滤回车键(就是按回车键不会退出程序)
	if(pMsg->message == WM_KEYDOWN && pMsg->wParam == VK_RETURN)
	{
		return TRUE;
	}

	return CDialogEx::PreTranslateMessage(pMsg);
}

1.3 对于FeiQ项目,我们共需要4块内容:对话框、中间者、协议族、网络;因此我们新建三个文件夹用来存放这些文件;

  • 1.在FeiQ项目的同级目录下创建一个 PackDef 文件夹,其中创建一个文件 PackDef.h ;
  • 2.在FeiQ项目的同级目录下创建一个 Mediator 文件夹,其中创建 Mediator.h Mediator.cpp TCPMediator.h TCPMediator.cpp UDPMediator.h UDPMediator.cpp ;
  • 3.在FeiQ项目的同级目录下创建一个 Net 文件夹,其中创建 Net.h Net.cpp TCPNet.h TCPNet.cpp UDPNet.h UDPNet.cpp ;
  • 4.在项目下 添加->新建筛选器 ,创建三个新建筛选器,名字与新建的三个文件夹一一对应;在以上的所有 cpp 文件中都包含 stdafx.h 文件;
  • 5.若不想在引用时这样写 #include"…/PackDef/PackDef.h" 这样写,想这样 #include"PackDef.h" 写,就需要在项目的属性中选择 c/c++ -> 附加包含目录 ,将之前创建的文件夹的相对路径都添加进去;

1.4 完成 PackDef.h 文件

  • 这个文件中都是一些预定义的结构体或者宏定义,是在以后需要用到的;
#pragma once

#define PORT						4567				// 端口号
#define RECV_BUFFER					1024				// 容器大小
#define NAME_SIZE					48					// 
#define SAY_CONTEXT					512					// 聊天内容的大小

// ===========================协议===========================
#define PROTOCL_BASE				10
#define PROTOCL_ONLINE_RQ			PROTOCL_BASE+1
#define PROTOCL_ONLINE_RS			PROTOCL_BASE+2
#define PROTOCL_OFFLINE_RQ			PROTOCL_BASE+3
#define PROTOCL_OFFLINE_RS			PROTOCL_BASE+4
#define PROTOCL_SAY_CONTEXT			PROTOCL_BASE+5

// ==========================协议包=========================
typedef char PackType;									// 协议类型

struct STRU_ONLINE_RQ									// 上线回复请求包
{
	PackType pack_type;
	char sz_user_name[NAME_SIZE];						// 用户信息
}STRU_ONLINE_RS;

struct STRU_OFFLINE_RQ									// 下线回复请求包
{
	PackType pack_type;
	char sz_user_name[NAME_SIZE];
}STRU_OFFLINE_RS;

struct STRU_SAY
{
	PackType pack_type;
	char sz_user_name[NAME_SIZE];
	char sz_say_context[SAY_CONTEXT];					// 聊天的内容
};

1.5 完成 Net.h

  • Net.h 是作为 TCPNet.h 以及 UDPNet.h 的父类而定义的,因此其中都是虚函数;
#pragma once

#include <windows.h>

class INet
{
public:
	virtual ~INet(){};

public:
	virtual bool OpenNet()=0;
	virtual void CloseNet()=0;
	virtual bool SendData(ULONG uIP, const char* pszSendBuffer, int nSendLen)=0;
};

1.6 完成 UDPNet 类

  • 添加几个成员变量: SOCKET m_socket(通信接口) ; bool m_b_qiut_thread(退出线程标记) ; HANDLE m_h_thread(线程句柄) ;
  • 除了重写父类的函数,还需添加一个线程处理函数;
#pragma once

#include "Net.h"

class CUDPNet:public INet
{
public:
	SOCKET m_socket;						// 通信接口
	bool m_b_qiut_thread;					// 退出线程标记
	HANDLE m_h_thread;						// 线程句柄

public:
	CUDPNet();
	virtual ~CUDPNet();

public:
	virtual bool OpenNet();
	virtual void CloseNet();
	virtual bool SendData(ULONG uIP, const char* pszSendBuffer, int nSendLen);

public:
	static unsigned int _stdcall ThreadProc(LPVOID lPvoid);
};
  • 在构造函数中初始化,在析构函数中清除;
CUDPNet::CUDPNet()
{
	m_socket = 0;
	m_b_qiut_thread = true;
	m_h_thread = 0;
}

CUDPNet::~CUDPNet()
{
	this->CloseNet();
	m_socket = 0;
	m_b_qiut_thread = true;
	m_h_thread = 0;
}
  • 在 OpenNet 函数中,完成的过程基本与之前创建 TCPNet 的过程相同,不同之处在于发送的数据是广播;
bool CUDPNet::OpenNet()
{
	// 1.加载库
	WORD wVersionRequested;
	WSADATA wsaData;
	int err;
	wVersionRequested = MAKEWORD(2, 2);

	err = WSAStartup(wVersionRequested, &wsaData);
	if (err != 0) 
		return false;

	if (LOBYTE(wsaData.wVersion) != 2 || HIBYTE(wsaData.wVersion) != 2) 
	{
		WSACleanup();
		return false;
	}

	// 2.创建监听套接字
	m_socket = ::socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
	if(m_socket == INVALID_SOCKET)
	{
		int error_code = WSAGetLastError();
		::closesocket(m_socket);
		m_socket = 0;
		WSACleanup();
		return false;
	}

	// 3.将监听套接字与本机进行绑定
	sockaddr_in addr_server;
	addr_server.sin_family = AF_INET;
	addr_server.sin_addr.S_un.S_addr = INADDR_ANY;			// 连接本地任意网卡
	addr_server.sin_port = htons(PORT);
	if(::bind(m_socket, (const sockaddr*)&addr_server, sizeof(sockaddr_in)) == SOCKET_ERROR)
	{
		int error_code = ::WSAGetLastError();
		::closesocket(m_socket);
		m_socket = 0;
		WSACleanup();
		return false;
	}

	// 4.发送广播
	bool b_flag = true;
	if(setsockopt(m_socket, SOL_SOCKET, SO_BROADCAST, (const char*)&b_flag, sizeof(b_flag)) == SOCKET_ERROR)
	{
		int error_code = ::WSAGetLastError();
		::closesocket(m_socket);
		m_socket = 0;
		WSACleanup();
		return false;
	}

	// 5.创建线程
	m_h_thread = (HANDLE)_beginthreadex(0, 0, &CUDPNet::ThreadProc, this, 0, 0);
	if(m_h_thread == 0)
		return false;

	return true;
}

1.7 在 app 类中创建主对话框之前 OpenNet

BOOL CFeiQProjApp::InitInstance()
{
	... ...

	// ==========================3-22 打开网络==========================
	INet* p = new CUDPNet;
	if(p->OpenNet() == false)
	{
		// 删除上面创建的 shell 管理器。
		if (pShellManager != NULL)
		{
			delete pShellManager;
		}
		return FALSE;
	}
	// ==========================3-22 打开网络==========================

	... ...
}

猜你喜欢

转载自blog.csdn.net/weixin_42896619/article/details/88770761
今日推荐