博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
004之MFCSocket同步编程(指针机制)
阅读量:4358 次
发布时间:2019-06-07

本文共 7919 字,大约阅读时间需要 26 分钟。

        异步与同步通信相比较,前者是非阻塞模式,后者是阻塞模式。有关两者差异在此博主中有详细讲解,推荐:https://www.cnblogs.com/wzsblogs/p/4671559.html。

        采用同步socket,同时可与CArchive、CSocketFile 配合使用(这两者能否与异步socket配合使用呢?还待验证)。两者的运行机制基本相同,但是在同步机制中OnConnect与OnSend永远不会被系统调用。(为啥?CSocket在Connect()返回WSAEWOULDBLOCK错误时,不是在OnConnect(),OnReceive()这些事件终端函数里去等待。它马上调用一个用于提取消息的函数PumpMessage(...),就是从当前线程的消息队列里取关心的消息。原文:https://www.cnblogs.com/yuanzfy/archive/2011/08/26/2155189.html)

        如果不使用CArchive/CSocketFile,则同步与异步最大的区别在于没有调用系统通知的OnConnect与OnSend。下例采用串行化进行说明。

 

1、创建基于对话框的MFC项目,包含Windows套接字。在工程中创建基于CSocket的类MySocket用于通信。

1)客户端:在MySocket类中新增函数pGetDlg用户快速获取主窗口指针,并声明一个Dlg类的指针用于绑定,CXXXDlg.h中声明指针对象m_ClientSocket;

2)服务端:在MySocket类中新增函数pGetDl用户快速获取主窗口指针,并声明一个Dlg类的指针用于实现函数快速获取指针。CXXXDlg.h中声明指针对象m_ListenSocket/m_ServerSocket。

 

Tips:相比异步通信,新增了三个指针对象,分别用于收发和缓冲。

1 // XXXSocke.h中 2  3 class CXXXDlg;     //类声明,创建指针对象 4 class XXXSocket : public CSocket 5 { 6 public: 7     CXXXXDlg *m_dlg; 8     void pGetDlg(CXXXXDlg*dlg); 9 10     CArchive *m_archiveIn;11     CArchive *m_archiveOut;12     CSocketFile *m_socketFile;13        ......14 }15 16 void CxxxxSocket::pGetDlg(CxxxxDlg* dlg)17 {18     m_dlg=dlg;19 }

 

2、在Dlg类中对指针对象初始化,并声明通信处理函数

        因指定为指针型,在Dlg.cpp的初始化InitInstance函数中中进行指针初始化(=NULL),并新增一个CString变量用于接收信息. 

1 class CXXXXDlg : public CDialog 2  { 3  public: 4      CXXXXSocket * m_xxxxsocket;  //客户端一个,服务器端两个,一个用于监听,一个用于服务 5      CArchive *m_archiveIn; 6      CArchive *m_archiveOut; 7      CSocketFile *m_socketFile;         CString recvfile;    //用于临时接收文件 8      void OnReceive(); 9      void OnClose();10 //   void OnConnect();不需要11      void Reset();    //用于释放套接字对象12          ......13  }14  //在Dlg.cpp中实现Reset函数,即删除套接字对象,并将指针赋空15  void CXXXDlg::Reset()16  {
  m_XXXXXsocket->Close(); //如果不关闭的话,直接点击中断会引发程序崩溃  m_ArchiveIn->Close();   m_ArchiveOut->Close();  m_socketFile->Close();17 if(m_xxxxSocket!=NULL) //注意用于监听的套接字不能释放,因为监听处于打开状态,与连接是并列关系18 {19 delete m_xxxxSocket;20 m_xxxxSocket=NULL;21 }22 if(m_archiveIn!=NULL)23 {24 delete m_archiveIn;25 m_archiveIn=NULL;26 }27 if(m_archiveOut!=NULL)28 {29 delete m_archiveOut;30 m_archiveOut=NULL;31 }32 if(m_socketFile!=NULL)33 {34 delete m_socketFile;35 m_socketFile=NULL;36 37 }38 39 }40 41 void CXXXXDlg::OnBnClickedCancel() //采用指针机制,在退出时需确保指针释放42 {43 // TODO: 在此添加控件通知处理程序代码44 Reset();45 OnCancel();46 }

 

 3、实例化套接字对象,并更新Dlg.cpp中的函数

1)客户端:在连接时实例化一个Socket对象,并绑定指针到主窗口,创建串行化对象用于接发写;

1 void Ccase005Dlg::OnBnClickedBnConnect() 2 { 3     // TODO: 在此添加控件通知处理程序代码 4     if(!AfxSocketInit())   //套接字初始化失败提示 5     { 6         MessageBox("windowsocket initial failed ","Receive",MB_ICONSTOP); 7         return; 8     } 9     m_clientsocket=new CMySocket;10     m_clientsocket->pGetDlg(this);11     m_clientsocket->Create();12 13     BYTE nFild[4];14     CString strIP;15     UpdateData();16 17     m_edit_ip.GetAddress(nFild[0],nFild[1],nFild[2],nFild[3]);18     strIP.Format("%d.%d.%d.%d",nFild[0],nFild[1],nFild[2],nFild[3]);19 20     if(!m_clientsocket->Connect(strIP,atoi(m_str_port)))   //创建失败提示,异步通信是在网络事件响应时触发nErrorCoe21 22     {23         AfxMessageBox("连接失败,请您重试!");24         return ;25     }26     else27     {28         m_listbox.AddString("连接成功!");29 //        m_listbox.SetTopIndex(m_listbox.GetCount()-1);30         m_socketFile=new CSocketFile(m_clientsocket);31         m_ArchiveIn=new CArchive(m_socketFile,CArchive::load);32         m_ArchiveOut=new CArchive(m_socketFile,CArchive::store);    //用于发送写33 34         m_edit_ip.EnableWindow(FALSE);35         m_edit_port.EnableWindow(FALSE);36         m_bn_connect.EnableWindow(FALSE);37         m_bn_disconnect.EnableWindow(TRUE);38         m_bn_clear.EnableWindow(TRUE);39         m_bn_send.EnableWindow(TRUE);40         m_bn_rewrite.EnableWindow(TRUE);41         m_editbox.EnableWindow(TRUE);42     }43 }44 45 void Ccase005Dlg::OnBnClickedBnDisconnect()46 {47     // TODO: 在此添加控件通知处理程序代码48     m_listbox.AddString("断开连接!");49     Reset();50 51     m_edit_ip.EnableWindow(TRUE);52     m_edit_port.EnableWindow(TRUE);53     m_bn_connect.EnableWindow(TRUE);54     m_bn_disconnect.EnableWindow(FALSE);55     m_bn_clear.EnableWindow(TRUE);56     m_bn_send.EnableWindow(FALSE);57     m_bn_rewrite.EnableWindow(FALSE);58     m_editbox.EnableWindow(FALSE);59 }

 

2)服务器端:监听在获取IP地址后,调用Create创建套接字,并侦听连接请求。

1 void Ccase006Dlg::OnBnClickedBnListen() 2 { 3     // TODO: 在此添加控件通知处理程序代码 4     if(!AfxSocketInit())       5     { 6         MessageBox("Windowsocket initial failed!","Send",MB_ICONSTOP); 7         return ; 8     } 9     m_listensocket=new CMySocket;        //创建套接字对象10     m_listensocket->pGetDlg(this);11     BYTE nFild[4];12     CString strIP;13     UpdateData();                 //更新获取数据14     m_edit_ip.GetAddress(nFild[0],nFild[1],nFild[2],nFild[3]);15     strIP.Format("%d.%d.%d.%d",nFild[0],nFild[1],nFild[2],nFild[3]);16     m_listensocket->Create(atoi(m_str_port),1,strIP);      //此处的Create是三参数17     m_listensocket->Listen(1);18     m_listbox.AddString("监听开始");19     m_listbox.AddString("地址"+strIP+" 端口"+m_str_port);20     m_listbox.AddString("等待客户端连接....");21 22 }23 24 void Ccase006Dlg::OnBnClickedBnStoplisten()25 {26     // TODO: 在此添加控件通知处理程序代码27     m_listensocket->Close();28     if(m_listensocket!=NULL)29     {30         delete m_listensocket;31         m_listensocket=NULL;32     }33     m_listbox.AddString("停止监听");34 }

 完成OnAccept函数,并调用AsyncSelect准备随时接收信息。

1 void Ccase006Dlg::OnAccept(void) 2 { 3     m_serversocket=new CMySocket; 4     m_serversocket->pGetDlg(this); 5     m_listensocket->Accept(*m_serversocket); 6     m_serversocket->AsyncSelect(FD_READ|FD_CLOSE); 7      8     m_socketfile=new CSocketFile(m_serversocket); 9     m_archiveIn=new CArchive(m_socketfile,CArchive::load);10     m_archiveOut=new CArchive(m_socketfile,CArchive::store);11     m_listbox.AddString("接收到连接请求");12     m_listbox.SetTopIndex(m_listbox.GetCount()-1);13 }

 

 

3)完成其他对应的功能模块:发送信息、接收信息、断开连接、清空列表、重新输入、OnClose、OnReceive。可以通用。

1 void Ccase005Dlg::OnBnClickedBnSend() 2 { 3     // TODO: 在此添加控件通知处理程序代码 4     UpdateData(); 5     *m_ArchiveOut<
Flush(); 7 m_listbox.AddString("发送: "+m_str_words); 8 m_listbox.SetTopIndex(m_listbox.GetCount()-1); 9 10 m_editbox.SetWindowText(""); //注意发送后清空输入内容11 m_editbox.SetFocus(); //发送后焦点指定在编辑栏12 }13 void Ccase005Dlg::OnBnClickedBnRewrite()14 {15 // TODO: 在此添加控件通知处理程序代码16 m_editbox.SetWindowText("");17 m_editbox.SetFocus();18 }19 20 void Ccase005Dlg::OnBnClickedBnClear()21 {22 // TODO: 在此添加控件通知处理程序代码23 m_listbox.ResetContent();24 }25 26 27 void Ccase005Dlg::OnReceive(void)28 {29 *m_ArchiveIn>>recvfile;30 m_ArchiveIn->Flush();31 m_listbox.AddString("收到: "+recvfile);32 m_listbox.SetTopIndex(m_listbox.GetCount()-1);33 }34 35 void Ccase005Dlg::OnClose(void)36 {37 Reset();38 m_listbox.AddString("服务器断开了");39 // m_listbox.SetTopIndex(m_listbox.GetCount()-1);40 41 m_edit_ip.EnableWindow(TRUE);42 m_edit_port.EnableWindow(TRUE);43 m_bn_connect.EnableWindow(TRUE);44 m_bn_disconnect.EnableWindow(FALSE);45 m_bn_clear.EnableWindow(TRUE);46 m_bn_send.EnableWindow(FALSE);47 m_bn_rewrite.EnableWindow(FALSE);48 m_editbox.EnableWindow(FALSE);49 }

 

4 、实现网络事件响应函数

在执行相应按钮操作后,系统会根据程序运行自动触发响应。因采用指针调用机制。所有处理事件的实现已经在主程序体中完成, 使用指针调回主程序接口即可. 

1 void CMySocket::OnClose(int nErrorCode) 2 { 3     // TODO: 在此添加专用代码和/或调用基类 4     m_dlg->OnClose(); 5     CSocket::OnClose(nErrorCode); 6 } 7  8 void CMySocket::OnReceive(int nErrorCode) 9 {10     // TODO: 在此添加专用代码和/或调用基类11     m_dlg->OnReceive();12     AsyncSelect(FD_READ|FD_CLOSE|FD_WRITE);    //需使用此条用于随时接收信息13     CSocket::OnReceive(nErrorCode);14 }

 

5、大功告成。

 

小结: 

1) 同步通信与异步通信各有千秋,理解其机制运行;

2)多保存,以免网页程序崩溃;

3)基本操作要烂熟于心。

转载于:https://www.cnblogs.com/maxonzou/p/10633399.html

你可能感兴趣的文章
angular页面刷新
查看>>
Leetcode:7- Reverse Integer
查看>>
C6表单(方成eform)---自定义流程标题格式
查看>>
GridView下DropDownList 的选择方法onselectedindexchanged 实现方法
查看>>
Python之set集合
查看>>
Generic Repository Pattern - Entity Framework, ASP.NET MVC and Unit Testing Triangle
查看>>
Win7 下新版本Windows Virtual PC 安装SharePoint步骤详解
查看>>
SQLSERVER 升级版本的方法
查看>>
正则表达式基本语法详解
查看>>
BZOJ2132: 圈地计划
查看>>
PHP数据库连接mysql与mysqli的区别与用法
查看>>
char * 与char []探究理解
查看>>
QT窗体显示在屏幕中间位置
查看>>
emmet使用技巧
查看>>
RPC-Thrift(二)
查看>>
MSSQL for Linux 安装指南
查看>>
【Golang 接口自动化08】使用标准库httptest完成HTTP请求的Mock测试
查看>>
洛谷 P1036 选数
查看>>
女性社区TOP10
查看>>
BP神经网络算法推导及代码实现笔记zz
查看>>