本例程对应的客户端请看https://blog.csdn.net/qq_34911636/article/details/88218891
本例程通信的流程和操作请看https://blog.csdn.net/qq_34911636/article/details/88256403
编译环境:系统:WIN7,Visual C++ 2015,建立一个ServerDlg的对话框程序
1、基于MFC创建对话框应用程序,在创建对话框应用程序时“windows套接字”选项需要打钩
二、创建ServerSocket类和ListenSocket类,两个都继承CAsyncSocket类
项目->添加类->选择MFC类->添加,如下图所示:
类名:ServerSocket类和ListenSocket类,基类选择:CAsyncSocket,点击完成,这时在项目中生成ServerSocket.h;ServerSocket.cpp和生成ListenSocket.h;ListenSocket.cpp四个文件
三、重写CAsyncSocket类的回调函数OnAccept(int nErrorCode);OnClose(int nErrorCode);OnReceive(int nErrorCode)等
打开类视图,选中左边ListenSocket类,点击右边工具栏的属性,在用鼠标点击对应的OnAccept等函数,这时在ListenSocket.h文件中生成OnAccept(int nErrorCode)函数。
打开类视图,选中左边ServerSocket类,点击右边工具栏的属性,在用鼠标点击对应的OnReceive和OnClose等函数,这时在ServerSocket.h文件中生成OnClose(int nErrorCode);OnReceive(int nErrorCode)函数。
如下图示:
四、服务端界面的搭建,如下图所示:
五、添加控件的变量和处理函数
ServerDlg.h头文件中的变量:
public:
CIPAddressCtrl m_IP; //IP控件变量(control类型)
CString m_Port; //端口控件变量(value)
CListBox m_StringWord; //列表框控件变量(control类型)
afx_msg void OnInternetQuit(); //“退出”按键处理函数
afx_msg void OnStartMonitor(); //“开始监听”按键处理函数
afx_msg void OnStopMonitor(); //“停止监听”按键处理函数
public:
ListenSocket m_listensocket; //网络监听类
ServerSocket m_serversocket; //网络服务类
afx_msg void OnBnaboutTo(); //“关于”程序版本按键
afx_msg void OnBnReceiveClean(); //“接收清空”按键处理函数
CEdit m_SendWork; //编辑框发送变量(control类型)
afx_msg void OnBnEditClean(); //“发送清除”按键处理函数
afx_msg void OnBnSendMessage(); //“发送”按键处理函数
afx_msg void OnBnInternectQuit(); //“断开连接”按键处理函数
afx_msg void OnBnSetAddress(); //“设置地址”按键处理函数
private:
BYTE SETIP[4];
public:
CEdit m_SetPort; //端口控件变量(control)
CButton m_SetAddress; //“设置地址”按键变量(control)
CButton m_InternectQuit; //“断开连接”按键变量(control)
CButton m_StartMonitor; //“开始监听”按键变量(control)
CButton m_StopMonitor; //“停止监听”按键变量(control)
ServerDlg.cpp文件中如下添加程序:
void CServerDlg::OnInternetQuit() //退出程序
{
// TODO: 在此添加控件通知处理程序代码
m_listensocket.Close();
CDialog::OnCancel();
}
void CServerDlg::OnStartMonitor()
{
// TODO: 在此添加控件通知处理程序代码
BYTE IPBYTE[4];
CString s_IP,ipport,port;
UINT sPort;
UpdateData(TRUE);
m_IP.GetAddress(IPBYTE[0], IPBYTE[1], IPBYTE[2], IPBYTE[3]);
s_IP.Format(_T("%d.%d.%d.%d"), IPBYTE[0], IPBYTE[1], IPBYTE[2], IPBYTE[3]);
sPort = _ttoi(m_Port);
m_listensocket.Create(sPort,1,FD_ACCEPT,s_IP);
m_listensocket.Listen(1);
port.Format(_T("地址:%s"), s_IP);
ipport.Format(_T("端口:%s"), m_Port);
m_StringWord.AddString(_T("开始监听"));
m_StringWord.AddString(port);
m_StringWord.AddString(ipport);
m_StringWord.AddString(_T("等待客户端连接......"));
m_StringWord.SetTopIndex(m_StringWord.GetCount() + 1);
m_IP.EnableWindow(FALSE);
m_SetPort.EnableWindow(FALSE);
m_SetAddress.EnableWindow(FALSE);
m_StartMonitor.EnableWindow(FALSE);
}
void CServerDlg::OnStopMonitor()
{
m_listensocket.Close();
m_StringWord.AddString(_T("停止监听"));
m_StringWord.SetTopIndex(m_StringWord.GetCount() - 1);
m_StartMonitor.EnableWindow(TRUE);
m_IP.EnableWindow(TRUE);
m_SetPort.EnableWindow(TRUE);
m_SetAddress.EnableWindow(TRUE);
// TODO: 在此添加控件通知处理程序代码
}
void CServerDlg::OnBnSendMessage()
{
// TODO: 在此添加控件通知处理程序代码
//UpdateData(TRUE);
CString sendwork,str;
m_SendWork.GetWindowTextW(sendwork);
if (sendwork.GetLength() != 0)
{
m_serversocket.Send(sendwork, 2*sendwork.GetLength());
//m_serversocket.Send(sendwork, strlen(sendwork));
str.Format(_T("发送:%s"), sendwork);
m_StringWord.AddString(str);
m_StringWord.SetTopIndex(m_StringWord.GetCount() - 1);
}
}
void CServerDlg::OnBnaboutTo()
{
// TODO: 在此添加控件通知处理程序代码
CAboutDlg aboutdlg;
aboutdlg.DoModal();
}
void CServerDlg::OnBnReceiveClean()
{
// TODO: 在此添加控件通知处理程序代码
m_StringWord.ResetContent();
}
void CServerDlg::OnBnEditClean()
{
// TODO: 在此添加控件通知处理程序代码
m_SendWork.SetWindowTextW(_T(""));
}
void CServerDlg::OnBnInternectQuit() //断开连接
{
// TODO: 在此添加控件通知处理程序代码
m_serversocket.Close();
m_StringWord.AddString(_T("断开连接"));
m_StringWord.SetTopIndex(m_StringWord.GetCount() + 1);
}
void CServerDlg::OnBnSetAddress()
{
// TODO: 在此添加控件通知处理程序代码
m_IP.SetAddress(SETIP[0], SETIP[1], SETIP[2], SETIP[3]);
m_SetPort.SetWindowTextW(_T("1035"));
}
六、ServerSocket.cpp中添加以下代码
void ServerSocket::OnReceive(int nErrorCode)
{
// TODO: 在此添加专用代码和/或调用基类
char Rmessage[200] = {NULL};
CString Rstring;
int n = Receive(Rmessage,200);
//int stringcount = ((CServerDlg*)AfxGetApp()->m_pMainWnd)->m_StringWord.GetCount();
Rmessage[n] = _T('\0');
Rstring.Format(_T("收到:%s"), Rmessage);
//for (int i = 0; i < stringcount; i++)
//{
// ((CServerDlg*)AfxGetApp()->m_pMainWnd)->m_StringWord.DeleteString(0);
//}
((CServerDlg*)AfxGetApp()->m_pMainWnd)->m_StringWord.AddString(Rstring);
((CServerDlg*)AfxGetApp()->m_pMainWnd)->m_StringWord.SetTopIndex(((CServerDlg*)AfxGetApp()->m_pMainWnd)->m_StringWord.GetCount() - 1);
CAsyncSocket::OnReceive(nErrorCode);
}
void ServerSocket::OnClose(int nErrorCode)
{
// TODO: 在此添加专用代码和/或调用基类
int stringcount = ((CServerDlg*)AfxGetApp()->m_pMainWnd)->m_StringWord.GetCount();
for (int i = 0; i < stringcount; i++)
{
((CServerDlg*)AfxGetApp()->m_pMainWnd)->m_StringWord.DeleteString(0);
}
((CServerDlg*)AfxGetApp()->m_pMainWnd)->m_StringWord.AddString(_T("已断开连接"));
CAsyncSocket::OnClose(nErrorCode);
}
在上述的代码中((CClientDlg*)(AfxGetApp()->m_pMainWnd))的作用;
其中AfxGetApp()获取的是应用程序类的指针,比如利用vs2010或vc6.0自动生成一个mfc工程,假设名称为Pro,若是基于单文档的程序,里面一定有CPro、CProDoc、CProView还有CMainFrame这几个类,其中,CPro代表了这个应用程序运行实例的对象,该类里面有很多成员变量,其中有不少代表窗口的变量,而m_pMainWnd正是其中的一个变量,它代表了程序的主体窗口,且它是属于CMainFrame类。MFC提供的应用程序设计理念就是文档、框架、视图 结构,这三个结构是由三个类来体现的,分别是上面提到的CProDoc(文档,代表程序的数据)、CMainFrame(框架,代表应用程序界面的框架窗口)、CProView(视图,代表应用程序的客户区界面部分),MFC基于单文档或多文档的应用程序,都是基于这个结构来实现的。而者三个结构成员是由应用程序实例的类来统一管理的,这就是上面所说的CPro类。AfxGetCpp()正是返回了这个类的指针。虽然m_pMainWnd和AfxGetCpp()的返回值都是指针,但他们的指向不同。
所以上述代码“((CClientDlg*)(AfxGetApp()->m_pMainWnd))”作用是在ClientSocket类中调用整个函数的实例,并且指向该实例的句柄,其该实例的句柄就是所看到程序运行的窗口,在通过指针引用到实例中的具体变量上,例如“ ((CClientDlg*)AfxGetApp()->m_pMainWnd)->m_Message.AddString(RWord);”就是对窗口的列表框加载字符串操作。
七、ListenSocket.cpp中添加以下代码
void ListenSocket::OnAccept(int nErrorCode)
{
// TODO: 在此添加专用代码和/或调用基类
int stringcount = ((CServerDlg*)AfxGetApp()->m_pMainWnd)->m_StringWord.GetCount();
Accept(((CServerDlg*)(AfxGetApp()->m_pMainWnd))->m_serversocket);
(((CServerDlg*)AfxGetApp()->m_pMainWnd)->m_serversocket).AsyncSelect(FD_READ|FD_CLOSE);
for (int i = 0; i < stringcount; i++)
{
(((CServerDlg*)AfxGetApp()->m_pMainWnd)->m_StringWord).DeleteString(0);
}
(((CServerDlg*)AfxGetApp()->m_pMainWnd)->m_StringWord.AddString(_T(" 接收客户的一个连接请求")));
CAsyncSocket::OnAccept(nErrorCode);
}
八、在BOOL CServerDlg::OnInitDialog()添加代码
SetIcon(m_hIcon, TRUE); // 设置大图标
SetIcon(m_hIcon, FALSE); // 设置小图标
上述代码后面添加如下代码:
SETIP[0] = 127;
SETIP[1] = 0;
SETIP[2] = 0;
SETIP[3] = 1;
九、运行代码