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()
{
... ...
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"));
return TRUE;
}
void CListBoxIPAddr::OnLbnDblclk()
{
CString str_ip;
int index = this->GetCurSel();
if(index != LB_ERR)
{
this->GetText(index, str_ip);
}
if(m_mp_sayDlg.count(str_ip) == 1)
m_mp_sayDlg[str_ip]->ShowWindow(SW_SHOW);
}
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()
{
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;
}
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;
}
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;
}
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;
}
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()
{
... ...
INet* p = new CUDPNet;
if(p->OpenNet() == false)
{
if (pShellManager != NULL)
{
delete pShellManager;
}
return FALSE;
}
... ...
}