本例程对应的服务端请看https://blog.csdn.net/qq_34911636/article/details/88254711
本例程通信的流程和操作请看https://blog.csdn.net/qq_34911636/article/details/88256403
编译环境:系统:WIN7,Visual C++ 2015,创建ClientDlg对话框应用程序
1、基于MFC创建对话框应用程序,在创建对话框应用程序时“windows套接字”选项需要打钩
二、创建ClientSocket类,继承CAsyncSocket类
项目->添加类->选择MFC类->添加,如下图所示:
类名:ClientSocket,基类选择:CAsyncSocket,点击完成,这时在项目中生成ClientSocket.h;ClientSocket.cpp两个文件
三、重写CAsyncSocket类的回调函数OnConnect(int nErrorCode);OnClose(int nErrorCode);OnReceive(int nErrorCode)等
打开类视图,选中左边ClientSocket类,点击右边工具栏的属性,在用鼠标点击对应的OnConnect,OnClose,OnReceive
等函数,这时在ClientSocket.h文件中生成virtual void OnConnect(int nErrorCode); virtual void OnClose(int nErrorCode);
virtual void OnReceive(int nErrorCode)三个函数,如下图示:
四、客户端界面的搭建,如下图所示:
五、添加控件的变量和处理函数
CClientDlg.h头文件中的变量:
ClientSocket *m_ClientSocket; //Socket指针
CIPAddressCtrl m_IP; //IP控件的变量
CString m_Port; //端口控件变量(String类型)
afx_msg void OnInternectConnect(); //“连接”按键对应的处理函数
afx_msg void OnInternectBreak(); //“断开”按键对应的处理函数
afx_msg void OnMessageSend(); //“发送”按键对应的处理函数
afx_msg void OnInternectQuit(); //“退出”按键对应的处理函数
CListBox m_Message; //接收信息列表框的变量(Control类型)
CString m_Word; //发送信息编辑框的变量(String类型)
afx_msg HBRUSH OnCtlColor(CDC* pDC, CWnd* pWnd, UINT nCtlColor);
CButton m_Connect; //“连接”按键对应的变量(Control类型)
CButton m_Break; //“断开”按键对应的变量(Control类型)
CButton m_SendClean; //“发送清除”按键的变量(Control类型)
CButton m_SendMessage; //“发送”按键对应的变量(Control类型)
CButton m_RecordClean; //“清除记录”按键对应的变量(Control类型)
CEdit m_SendWord; //发送信息编辑框的变量(Control类型)
afx_msg void OnBnSendClean(); //“发送清除”按键对应的处理函数
afx_msg void OnBnRecordClean(); //“记录清除”按键对应的处理函数
CEdit m_ControlPort; //端口控件变量(Control类型)
CClientDlg.cpp文件中添加的处理函数
void CClientDlg::OnInternectConnect()
{
// TODO: 在此添加控件通知处理程序代码
BYTE IPADDRESS[4];
CString IpString;
UINT port;
UpdateData(TRUE);
port = _ttoi(m_Port); //把端口数据转换成整形变量
m_IP.GetAddress(IPADDRESS[0],IPADDRESS[1],IPADDRESS[2],IPADDRESS[3]); //获取IP地址
if (!(m_IP.IsBlank())) //判断是否有输入IP地址
{
IpString.Format(_T("%d.%d.%d.%d"), IPADDRESS[0], IPADDRESS[1], IPADDRESS[2], IPADDRESS[3]); //把IP地址转换成字符串类型
m_ClientSocket->Create(); //创建Socket
m_ClientSocket->Connect(IpString, port); //连接网络
m_Break.EnableWindow(TRUE); //使断开按键有效
m_SendMessage.EnableWindow(TRUE); //使发送按键有效
m_Connect.EnableWindow(FALSE); //使连接按键失效
m_IP.EnableWindow(FALSE); //使IP控件输入无效
m_ControlPort.EnableWindow(FALSE); //使断开无效
}
//CButton *cbutton = (CButton*)GetDlgItem(IDC_CONNECT);
//HWND hbutton = cbutton->GetSafeHwnd();
CDC *buttonpdc = GetDlgItem(IDC_CONNECT)->GetDC();
buttonpdc->SetBkMode(TRANSPARENT);
buttonpdc->SetBkColor(RGB(255,0,0));
ReleaseDC(buttonpdc);
}
void CClientDlg::OnInternectBreak()
{
// TODO: 在此添加控件通知处理程序代码
m_ClientSocket->Close(); //断开网络连接
m_IP.EnableWindow(TRUE); //使IP控件有效
m_ControlPort.EnableWindow(TRUE); //使断开有效
m_Connect.EnableWindow(TRUE); //使连接按键有效
m_Message.AddString(_T("已断开连接"));
m_Message.SetTopIndex(m_Message.GetCount() - 1);
}
void CClientDlg::OnMessageSend()
{
// TODO: 在此添加控件通知处理程序代码
CString sendmessage;
UpdateData(TRUE);
if (m_Word.GetLength() != 0) //判断发送信息框是否有信息要发送
{
m_ClientSocket->Send(m_Word, 2*m_Word.GetLength()); //发送信息
sendmessage.Format(_T("发送:%s"), m_Word);
m_Message.AddString(sendmessage);
m_Message.SetTopIndex(m_Message.GetCount() - 1);
}
}
void CClientDlg::OnInternectQuit()
{
// TODO: 在此添加控件通知处理程序代码
m_ClientSocket->Close(); //退出网络连接
CDialog::OnCancel();
}
void CClientDlg::OnBnRecordClean()
{
// TODO: 在此添加控件通知处理程序代码
m_Message.ResetContent(); //清除列表框内容
}
void CClientDlg::OnBnSendClean()
{
// TODO: 在此添加控件通知处理程序代码
m_SendWord.SetWindowTextW(_T("")); //清除发送框内容
}
六、ClientSocket.cpp中添加以下代码
void ClientSocket::OnConnect(int nErrorCode) //Socket的回调函数,若TCP连接成功,则调用该函数
{
// TODO: 在此添加专用代码和/或调用基类
if (nErrorCode)
{
AfxMessageBox(_T("网络连接失败"));
return;
}
((CClientDlg*)(AfxGetApp()->m_pMainWnd))->m_Message.AddString(_T("网络连接成功"));
((CClientDlg*)AfxGetApp()->m_pMainWnd)->m_Message.SetTopIndex(((CClientDlg*)AfxGetApp()->m_pMainWnd)->m_Message.GetCount() + 1);
CAsyncSocket::OnConnect(nErrorCode);
}
void ClientSocket::OnClose(int nErrorCode) //Socket的回调函数,若TCP网络断开,则调用该函数
{
// TODO: 在此添加专用代码和/或调用基类
((CClientDlg*)AfxGetApp()->m_pMainWnd)->m_Message.AddString(_T("服务器端断开连接"));
((CClientDlg*)AfxGetApp()->m_pMainWnd)->m_Message.SetTopIndex(((CClientDlg*)AfxGetApp()->m_pMainWnd)->m_Message.GetCount() - 1);
CAsyncSocket::OnClose(nErrorCode);
}
void ClientSocket::OnReceive(int nErrorCode) //Socket的回调函数,若TCP收到信息,则调用该函数接收信息
{
// TODO: 在此添加专用代码和/或调用基类
char RBYTE[200] = {NULL};
CString RWord;
int n = Receive(RBYTE, 200);
RBYTE[n] = _T('\0');
RWord.Format(_T("收到:%s"), RBYTE);
((CClientDlg*)AfxGetApp()->m_pMainWnd)->m_Message.AddString(RWord);
((CClientDlg*)AfxGetApp()->m_pMainWnd)->m_Message.SetTopIndex(((CClientDlg*)AfxGetApp()->m_pMainWnd)->m_Message.GetCount() - 1);
CAsyncSocket::OnReceive(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);”就是对窗口的列表框加载字符串操作。
七、运行代码