文章目录
- 前言
- 一、补充说明
- 二、客户端和服务端身份状态的切换
- 三、点击关闭窗口按钮
- 总结
前言
C++打造局域网聊天室第十二课: 客户端和服务端的切换
一、补充说明
在C++打造局域网聊天室第十一课: 程序关闭及线程的结束中描述的服务端线程的关闭和结束是存在bug的,这里进行补充说明。
按照上述链接的程序,在关闭服务端时,会出现如下bug:
即在第一次点击服务端停止按钮后,客户端不会出现服务端断开提示,在第二次点击服务端停止按钮后,客户端才出现提示。经过博主Debug发现造成这种现象的原因是:在点击服务端停止按钮后,由于服务端对应的bShutDown为1,Server.cpp中的程序会跳出死循环,但是之前对于Server.cpp中的程序只实现了关闭监听线程,而与客户端通信的线程没有调用closesocket()关闭。所以WaitForMultipleObjects()会超时,调用TerminateThread()进行强制与客户端连接线程的结束,由于第一次点击服务端停止按钮后WaitForMultipleObjects()超时,客户端的SOCKET_Select(m_ClientItem.m_Socket, 100, TRUE)函数不会返回1,所以客户端不会出现服务端断开提示。而在第二次点击服务端停止按钮时,服务端的监听线程和与客户端的通信线程已经全都关闭,WaitForMultipleObjects()不会超时,所以客户端会正常提示。解决办法为在Server.cpp的如图位置加入框中代码:
此时,在第一次点击服务端停止按钮后,不光对服务端监听线程进行了关闭,也对与客户端通信的线程进行了关闭,WaitForMultipleObjects()不会超时,客户端会正常提示:
二、客户端和服务端身份状态的切换
如果本程序为客户端,点击服务端选项后,要提醒用户,并停止客户端。添加后,点击服务器选项和客户端选项的MFC消息映射机制函数变为:
void CchartroomDlg::OnBnClickedRadio2()//点击服务器选项的MFC消息映射机制
{
// TODO: 在此添加控件通知处理程序代码
INT iRet = -1;
// 添加客户端切换服务端程序
if (m_bIsServer == 0) // 如果当前程序为服务端
{
iRet = MessageBox(_T("您正在聊天中,要退出吗?"), 0, MB_OKCANCEL);
if (iRet == IDOK) // 如果用户真想关闭
{
StopClient(); // 停止服务端
}
else
{
CheckRadioButton(IDC_RADIO1, IDC_RADIO2, IDC_RADIO1); // 置为客户端
}
}
if (iRet == IDOK || m_bIsServer == -1)
{
EnableWindow(IDC_BUTTON2, 0);// 利用自己定义的函数禁用客户器端的停止按钮
EnableWindow(IDC_IPADDRESS1, 0);// 利用自己定义的函数禁用服务器IP
EnableWindow(IDC_EDIT6, 0);// 利用自己定义的函数禁用服务器端口
EnableWindow(IDC_BUTTON1, 0);// 利用自己定义的函数禁用连接服务器
EnableWindow(IDC_BUTTON2, 0);// 利用自己定义的函数禁用客户端的停止按钮
EnableWindow(IDC_EDIT7, 1);// 利用自己定义的函数允许本地监听端口
EnableWindow(IDC_BUTTON3, 1);// 利用自己定义的函数允许开启服务器
EnableWindow(IDC_BUTTON4, 1);// 利用自己定义的函数允许服务器端停止按钮
}
}
void CchartroomDlg::OnBnClickedRadio1()//点击客户端选项的MFC消息映射机制
{
// TODO: 在此添加控件通知处理程序代码
INT iRet = -1;
// 添加服务端切换客户端程序
if (m_bIsServer == 1) // 如果当前程序为服务端
{
iRet = MessageBox(_T("您是聊天室的服务端,如果您退出,所有客户端都会掉线"), 0, MB_OKCANCEL);
if (iRet == IDOK) // 如果用户真想关闭
{
StopServer(); // 停止服务端
}
else
{
CheckRadioButton(IDC_RADIO1, IDC_RADIO2, IDC_RADIO2); // 置为服务端
}
}
if (iRet == IDOK || m_bIsServer == -1)
{
EnableWindow(IDC_BUTTON2, 1);// 利用自己定义的函数允许客户器端的停止按钮
EnableWindow(IDC_IPADDRESS1, 1);// 利用自己定义的函数允许服务器IP
EnableWindow(IDC_EDIT6, 1);// 利用自己定义的函数允许服务器端口
EnableWindow(IDC_BUTTON1, 1);// 利用自己定义的函数允许连接服务器
EnableWindow(IDC_BUTTON2, 1);// 利用自己定义的函数允许客户端的停止按钮
EnableWindow(IDC_EDIT7, 0);// 利用自己定义的函数禁用本地监听端口
EnableWindow(IDC_BUTTON3, 0);// 利用自己定义的函数禁用开启服务器
EnableWindow(IDC_BUTTON4, 0);// 利用自己定义的函数禁用服务器端停止按钮
}
}
三、点击关闭窗口按钮
点击关闭后,不光要关闭窗口,还要终止服务端和客户端对应的线程
void CchartroomDlg::OnBnClickedButton6() // 点击关闭窗口按钮的MFC消息映射机制
{
// TODO: 在此添加控件通知处理程序代码
if (m_bIsServer == 1) // 如果为服务端
{
StopServer();
}
else if (m_bIsServer == 0) // 如果为客户端
{
StopClient();
}
OnCancel(); // 关闭窗口
}
总结
C++打造局域网聊天室第十二课: 客户端和服务端的切换