文章目录
1.结构体
struct UserInfo//存储用户信息相关的信息
{ int op;
char name[maxn], pwd[maxn];
char ip[maxn];
char online[50][maxn];
char remsg[maxn];
int listlen;};
struct ChatMsg//存储聊天消息
{ int op;//标识消息类型
short port;//该消息相关的通信端口
char username[100];
char msg[1000];};
short LIST::find_port( )//寻找端口,自动分配端口
{ short MAX = 65434;
for (int i = 0; i < port_v.size(); i++)
{MAX = max(MAX, port_v[i]);}
return MAX+1;}
short LIST::find_port( )用于动态分配端口号,避免端口冲突。例如,当一个新客户端连接时,系统会选择一个尚未使用的端口。
2.list对话框
MFC 对话框类:定义对话框
LIST 类继承自 MFC 中的 CDialogEx 类,表示一个对话框
生成与类相关的消息映射机制,确保类能够处理 MFC 消息系统(例如按钮点击、窗口事件等)
IMPLEMENT_DYNAMIC(LIST, CDialogEx)//消息映射、类型信息
LIST::LIST(CWnd* pParent /*=nullptr*/)//完成对话框的初始化
: CDialogEx(IDD_LIST, pParent)
{
#ifndef _WIN32_WCE
EnableActiveAccessibility();//启用 Windows 的活动辅助功能,提高可访问性
#endif
}
MFC 中 LIST 类的对话框初始化函数
作用:它用于初始化和设置对话框中的控件以及处理一些与界面显示相关的操作
BOOL LIST::OnInitDialog()//对话框初始化时被调用,对话框类
{
this->ModifyStyleEx(0, WS_EX_APPWINDOW);//用于修改窗口的扩展样式,,使窗口始终显示在任务栏上
UpdateData(true);
CreateSock();
GetDlgItem(IDC_STATIC)->SetWindowText(username);
m_list.DeleteAllItems();//清空列表中的所有项
m_list.SetExtendedStyle(m_list.GetExtendedStyle() | LVS_EX_FULLROWSELECT | LVS_EX_GRIDLINES);// 为列表视图控件添加全行选中和栅格风格
m_list.InsertColumn(0, "用户名", LVCFMT_LEFT, 100);
m_list.InsertColumn(1, "状态", LVCFMT_LEFT, 120);
CString id;
CString a;
a.Format("列表长度 %d", listlen);
//MessageBox(a);
for (int i = 0; i < listlen; i++)
{ id.Format("%d", i);
if (strcmp(onlinelist[i], username.GetBuffer()) == 0) //避免当前用户自己显示在列表
continue;
m_list.InsertItem(i, onlinelist[i]);
m_list.SetItemText(i, 0, onlinelist[i]);
m_list.SetItemText(i, 1, "在线");}
return 1;
}
MFC 中 CDialogEx 类的 DoDataExchange 函数
作用:通常用于控件与类成员变量之间的数据交换
void LIST::DoDataExchange(CDataExchange* pDX)//用于控件和成员变量之间的数据交换
{ CDialogEx::DoDataExchange(pDX);
DDX_Control(pDX, IDC_LIST1, m_list);
return;}
//MFC 应用程序中的消息映射结构,它将用户的交互操作(例如按钮点击、列表控件的双击、窗口重绘等)与相应的事件处理函数绑定起来。MFC 使用消息映射机制来处理消息,这样可以将各种事件(如按钮点击、鼠标双击等)与响应函数进行关联,从而实现对用户操作的响应。
- ON_NOTIFY 用于处理控件通知消息,如双击列表项、列表项状态变化等。
- ON_BN_CLICKED 用于处理按钮点击事件。
- ON_MESSAGE 用于处理自定义消息或系统消息,如窗口重绘(WM_PAINT)和自定义消息(RecvMsg)。
BEGIN_MESSAGE_MAP(LIST, CDialogEx)
ON_NOTIFY(NM_DBLCLK, IDC_LIST1, &LIST::OnNMDblclkList1)// 用于将控件的通知消息与响应函数进行绑定
ON_BN_CLICKED(IDC_BUTTON1, &LIST::OnBnClickedButton1)//用于处理按钮点击事件
ON_MESSAGE(RecvMsg, &LIST::OnRecvmsg)//处理自定义消息
ON_NOTIFY(LVN_ITEMCHANGED, IDC_LIST1, &LIST::OnLvnItemchangedList1)
ON_MESSAGE(OnPaint, &LIST::OnOnpaint)//处理自定义的或系统的消息
ON_MESSAGE(WM_PAINT, &LIST::OnPaint)//自定义绘制窗口内容时使用,例如在窗口或控件重绘时执行自定义的绘制操作
ON_BN_CLICKED(IDC_BUTTON2, &LIST::OnBnClickedButton2)
ON_MESSAGE(RecvMsg, &LIST::OnRecvmsg)
END_MESSAGE_MAP()
3.LIST 消息处理程序
MFC 中列表控件(CListCtrl)的双击事件处理函数 OnNMDblclkList1 的实现
功能是:
处理用户双击列表控件中的一项;
获取该项的文本内容,并将其保存到一个 UserInfo 结构体中;
通过网络(使用套接字)将 UserInfo 数据发送到远程主机;
最后设置返回值,表明事件已经被处理。
获取列表项的信息,并发送包含该信息的数据包
void LIST::OnNMDblclkList1(NMHDR *pNMHDR, LRESULT *pResult)//处理列表控件双击事件
{LPNMITEMACTIVATE pNMItemActivate = reinterpret_cast<LPNMITEMACTIVATE>(pNMHDR);
int index = -1;
if (pNMItemActivate != NULL)//确保能够安全访问其成员
{
index = pNMItemActivate->iItem;
}
LV_ITEM lvitem = { 0 };//用于获取或设置列表控件项的详细信息
lvitem.iItem = index;
lvitem.iSubItem = 0;
lvitem.mask = LVIF_TEXT | LVIF_IMAGE | LVIF_PARAM;
m_list.GetItem(&lvitem);
m_list.GetItemText(index, 0);
CString name = m_list.GetItemText(index, 0);
UserInfo userInfo;
userInfo.op = 4;
strcpy(userInfo.name,name.GetBuffer());
send(c_sock, (char *)&userInfo, sizeof(userInfo), 0);
*pResult = 0;
}
刷新列表控件,更新在线用户列表,重新加载列表控件
void LIST::relist()
{ m_list.DeleteAllItems();
m_list.SetExtendedStyle(m_list.GetExtendedStyle() | LVS_EX_FULLROWSELECT | LVS_EX_GRIDLINES);// 为列表视图控件添加全行选中和栅格风格,设置列表控件的扩展样式
CString id;
CString a;
a.Format("列表长度 %d", listlen);
for (int i = 0; i < listlen; i++)
{ id.Format("%d", i);
if (strcmp(onlinelist[i], username.GetBuffer()) == 0) continue;
m_list.InsertItem(i, onlinelist[i]);
m_list.SetItemText(i, 0, onlinelist[i]);
m_list.SetItemText(i, 1, "在线");}
UpdateData(false);}
4.询问用户是否退出,并将消息发送给服务端
void LIST::OnBnClickedButton1()
{if (MessageBox("确定要退出吗?", "提示", MB_YESNO) == IDYES)
{UserInfo userinfo;//保存用户信息
strcpy(userinfo.name, username);
userinfo.op = 3;//操作码,用户要求退出或断开连接
send(c_sock, (char *)&userinfo, sizeof(userinfo), 0);//含用户信息的结构体发送到服务器
PostQuitMessage(0);//向消息循环发送退出消息
}
else
{return;}
}
具体操作:
- 删除列表中的所有现有项。
- 设置列表控件的扩展样式,使整行可选且带有栅格线。
- 遍历在线用户列表(onlinelist),对于每个用户,检查是否是当前用户,如果是则跳过。
- 插入在线用户到列表控件中,并为每个用户设置用户名和在线状态。
- 最后,调用 UpdateData(false) 更新控件中的数据。
5.UDP 客户端程序,可以异步接收数据并进行处理
- 创建一个 UDP 套接字 DGramSock。 将该套接字与本地地址(INADDR_ANY 和 PORT1)绑定。
- 将该套接字设置为异步
- I/O 模式,并注册事件(当套接字可读时触发 RecvMsg 回调函数)。
- 如果在创建套接字、绑定地址、设置异步 I/O时发生错误,弹出错误提示框并关闭套接字,最后退出对话框。
void LIST::CreateSock()
{if ((DGramSock = socket(AF_INET, SOCK_DGRAM, 0)) < 0)//创建UDP套接字
{CString str1("创建套接字失败");
MessageBox(str1);
CDialogEx::OnOK();
}
memset((void *)&addr, 0, sizeof(addr));//保存的是网络地址信息(IP 地址和端口号等)
addr.sin_family = AF_INET;
addr.sin_port = htons(PORT1);
addr.sin_addr.s_addr = htonl(INADDR_ANY);
if (bind(DGramSock, (LPSOCKADDR)&addr, sizeof(addr)) != 0)
{MessageBox("绑定失败!");
closesocket(DGramSock);
CDialogEx::OnOK();
}
if (WSAAsyncSelect(DGramSock, m_hWnd, RecvMsg, FD_READ) != 0)
{CString str;
str.Format("异步套接字失败!错误代码:%d", WSAGetLastError());
MessageBox(str);
closesocket(DGramSock);
}
}
6.MFC应用程序中的消息处理函数
- 响应用户点击某个列表项;
- 处理选中项改变时的事件
- 更新界面中的其他控件或数据等
当列表中的项发生变化时(如状态变化、选中状态变化等),此函数被调用
void LIST::OnLvnItemchangedList1(NMHDR *pNMHDR, LRESULT *pResult)
{//指向 NMLISTVIEW 结构的指针
LPNMLISTVIEW pNMLV = reinterpret_cast<LPNMLISTVIEW>(pNMHDR);
// TODO: 在此添加控件通知处理程序代码
*pResult = 0;}
7.MFC 应用程序中的 OnPaint 消息处理函数
处理了窗口的绘制事件
afx_msg LRESULT LIST::OnPaint(WPARAM wParam, LPARAM lParam)//消息映射函数
{if (IsIconic())//判断窗口是否最小化
{CPaintDC dc(this); // 用于绘制的设备上下文
SendMessage(WM_ICONERASEBKGND, reinterpret_cast<WPARAM>(dc.GetSafeHdc()), 0);
// 使图标在工作区矩形中居中
int cxIcon = GetSystemMetrics(SM_CXICON);
int cyIcon = GetSystemMetrics(SM_CYICON);
CRect rect;
GetClientRect(&rect);
int x = (rect.Width() - cxIcon + 1) / 2;
int y = (rect.Height() - cyIcon + 1) / 2;
// 绘制图标
dc.DrawIcon(x, y, m_hIcon);
}
else
{//CDialogEx::OnPaint();
CPaintDC dc(this);
CRect rc;
GetClientRect(&rc);
CDC dcMem;
dcMem.CreateCompatibleDC(&dc);
CBitmap bmpBackground;
bmpBackground.LoadBitmap(IDB_BITMAP9);
BITMAP bitmap;
bmpBackground.GetBitmap(&bitmap);
CBitmap* pbmpPri = dcMem.SelectObject(&bmpBackground);
dc.StretchBlt(0, 0, rc.Width(), rc.Height(), &dcMem, 0, 0, bitmap.bmWidth, bitmap.bmHeight, SRCCOPY);}
return 0;}
使用的MFC里的函数
1.CDialogEx:
CDialogEx是MFC库中的一个对话框类,它是CDialog类的扩展。CDialogEx提供了创建和管理对话框的工具和功能,对话框通常用于收集用户输入或显示信息。相比基本的CDialog类,CDialogEx添加了一些额外的功能,比如支持扩展的样式和更好的Unicode支持,以及界面美化的功能,如修改对话框的背景颜色、标题栏的颜色等。
2.OnInitDialog
对话框的OnInitDialog成员函数是对话框在被创建激活后调用。,此时各种控件已经被创建,因此与控件相关的操作应该放在这里面。
3.CString
CStringT(属于MFC 和 ATL 之间共享的类) 的类模板的专用化,没有基类