• 004之MFCSocket同步编程(指针机制)


            异步与同步通信相比较,前者是非阻塞模式,后者是阻塞模式。有关两者差异在此博主中有详细讲解,推荐: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)))   //创建失败提示,异步通信是在网络事件响应时触发nErrorCoe
    21 
    22     {
    23         AfxMessageBox("连接失败,请您重试!");
    24         return ;
    25     }
    26     else
    27     {
    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<<m_str_words;
     6     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)基本操作要烂熟于心。

  • 相关阅读:
    配置管理puppet
    ruby安装
    angularjs 安装篇
    idea 快捷键
    rabbitmq java queue
    spring cloud bus rabbitmq
    rabbitmq 安装篇
    spring cloud eureka
    spring cloud config
    postgre 导入sql文件
  • 原文地址:https://www.cnblogs.com/maxonzou/p/10633399.html
Copyright © 2020-2023  润新知