异步与同步通信相比较,前者是非阻塞模式,后者是阻塞模式。有关两者差异在此博主中有详细讲解,推荐: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:相比异步通信,新增了三个指针对象,分别用于收发和缓冲。

 // XXXSocke.h中

 class CXXXDlg;     //类声明,创建指针对象
 class XXXSocket : public CSocket
 {
 public:
     CXXXXDlg *m_dlg;
     void pGetDlg(CXXXXDlg*dlg);

     CArchive *m_archiveIn;
     CArchive *m_archiveOut;
     CSocketFile *m_socketFile;
        ......
 }

 void CxxxxSocket::pGetDlg(CxxxxDlg* dlg)
 {
     m_dlg=dlg;
 }

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

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

 class CXXXXDlg : public CDialog
  {
  public:
      CXXXXSocket * m_xxxxsocket;  //客户端一个,服务器端两个,一个用于监听,一个用于服务
      CArchive *m_archiveIn;
      CArchive *m_archiveOut;
      CSocketFile *m_socketFile;        CString recvfile;    //用于临时接收文件
      void OnReceive();
      void OnClose();
 //   void OnConnect();不需要
      void Reset();    //用于释放套接字对象
          ......
  }
  //在Dlg.cpp中实现Reset函数,即删除套接字对象,并将指针赋空
  void CXXXDlg::Reset()
  {         m_XXXXXsocket->Close();     //如果不关闭的话,直接点击中断会引发程序崩溃         m_ArchiveIn->Close();         m_ArchiveOut->Close();         m_socketFile->Close();
       if(m_xxxxSocket!=NULL)    //注意用于监听的套接字不能释放,因为监听处于打开状态,与连接是并列关系
       {
            delete m_xxxxSocket;
            m_xxxxSocket=NULL;
       }
       if(m_archiveIn!=NULL)
       {
            delete m_archiveIn;
            m_archiveIn=NULL;
        }
        if(m_archiveOut!=NULL)
        {
            delete m_archiveOut;
            m_archiveOut=NULL;
         }
         if(m_socketFile!=NULL)
         {
            delete m_socketFile;
            m_socketFile=NULL;

          }

  }

 void CXXXXDlg::OnBnClickedCancel()    //采用指针机制,在退出时需确保指针释放
 {
     // TODO: 在此添加控件通知处理程序代码
     Reset();
     OnCancel();
 }

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

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

 void Ccase005Dlg::OnBnClickedBnConnect()
 {
     // TODO: 在此添加控件通知处理程序代码
     if(!AfxSocketInit())   //套接字初始化失败提示
     {
         MessageBox("windowsocket initial failed ","Receive",MB_ICONSTOP);
         return;
     }
     m_clientsocket=new CMySocket;
     m_clientsocket->pGetDlg(this);
     m_clientsocket->Create();

     BYTE nFild[];
     CString strIP;
     UpdateData();

     m_edit_ip.GetAddress(nFild[],nFild[],nFild[],nFild[]);
     strIP.Format(],nFild[],nFild[],nFild[]);

     if(!m_clientsocket->Connect(strIP,atoi(m_str_port)))   //创建失败提示,异步通信是在网络事件响应时触发nErrorCoe

     {
         AfxMessageBox("连接失败,请您重试!");
         return ;
     }
     else
     {
         m_listbox.AddString("连接成功!");
 //        m_listbox.SetTopIndex(m_listbox.GetCount()-1);
         m_socketFile=new CSocketFile(m_clientsocket);
         m_ArchiveIn=new CArchive(m_socketFile,CArchive::load);
         m_ArchiveOut=new CArchive(m_socketFile,CArchive::store);    //用于发送写

         m_edit_ip.EnableWindow(FALSE);
         m_edit_port.EnableWindow(FALSE);
         m_bn_connect.EnableWindow(FALSE);
         m_bn_disconnect.EnableWindow(TRUE);
         m_bn_clear.EnableWindow(TRUE);
         m_bn_send.EnableWindow(TRUE);
         m_bn_rewrite.EnableWindow(TRUE);
         m_editbox.EnableWindow(TRUE);
     }
 }

 void Ccase005Dlg::OnBnClickedBnDisconnect()
 {
     // TODO: 在此添加控件通知处理程序代码
     m_listbox.AddString("断开连接!");
     Reset();

     m_edit_ip.EnableWindow(TRUE);
     m_edit_port.EnableWindow(TRUE);
     m_bn_connect.EnableWindow(TRUE);
     m_bn_disconnect.EnableWindow(FALSE);
     m_bn_clear.EnableWindow(TRUE);
     m_bn_send.EnableWindow(FALSE);
     m_bn_rewrite.EnableWindow(FALSE);
     m_editbox.EnableWindow(FALSE);
 }

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

 void Ccase006Dlg::OnBnClickedBnListen()
 {
     // TODO: 在此添加控件通知处理程序代码
     if(!AfxSocketInit())
     {
         MessageBox("Windowsocket initial failed!","Send",MB_ICONSTOP);
         return ;
     }
     m_listensocket=new CMySocket;        //创建套接字对象
     m_listensocket->pGetDlg(this);
     BYTE nFild[];
     CString strIP;
     UpdateData();                 //更新获取数据
     m_edit_ip.GetAddress(nFild[],nFild[],nFild[],nFild[]);
     strIP.Format(],nFild[],nFild[],nFild[]);
     m_listensocket->Create(atoi(m_str_port),,strIP);      //此处的Create是三参数
     m_listensocket->Listen();
     m_listbox.AddString("监听开始");
     m_listbox.AddString("地址"+strIP+" 端口"+m_str_port);
     m_listbox.AddString("等待客户端连接....");

 }

 void Ccase006Dlg::OnBnClickedBnStoplisten()
 {
     // TODO: 在此添加控件通知处理程序代码
     m_listensocket->Close();
     if(m_listensocket!=NULL)
     {
         delete m_listensocket;
         m_listensocket=NULL;
     }
     m_listbox.AddString("停止监听");
 }

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

 void Ccase006Dlg::OnAccept(void)
 {
     m_serversocket=new CMySocket;
     m_serversocket->pGetDlg(this);
     m_listensocket->Accept(*m_serversocket);
     m_serversocket->AsyncSelect(FD_READ|FD_CLOSE);

     m_socketfile=new CSocketFile(m_serversocket);
     m_archiveIn=new CArchive(m_socketfile,CArchive::load);
     m_archiveOut=new CArchive(m_socketfile,CArchive::store);
     m_listbox.AddString("接收到连接请求");
     m_listbox.SetTopIndex(m_listbox.GetCount()-);
 }

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

 void Ccase005Dlg::OnBnClickedBnSend()
 {
     // TODO: 在此添加控件通知处理程序代码
     UpdateData();
     *m_ArchiveOut<<m_str_words;
     m_ArchiveOut->Flush();
     m_listbox.AddString("发送: "+m_str_words);
     m_listbox.SetTopIndex(m_listbox.GetCount()-);

     m_editbox.SetWindowText("");    //注意发送后清空输入内容
     m_editbox.SetFocus();    //发送后焦点指定在编辑栏
 }
 void Ccase005Dlg::OnBnClickedBnRewrite()
 {
     // TODO: 在此添加控件通知处理程序代码
     m_editbox.SetWindowText("");
     m_editbox.SetFocus();
 }

 void Ccase005Dlg::OnBnClickedBnClear()
 {
     // TODO: 在此添加控件通知处理程序代码
     m_listbox.ResetContent();
 }

 void Ccase005Dlg::OnReceive(void)
 {
     *m_ArchiveIn>>recvfile;
     m_ArchiveIn->Flush();
     m_listbox.AddString("收到: "+recvfile);
     m_listbox.SetTopIndex(m_listbox.GetCount()-);
 }

 void Ccase005Dlg::OnClose(void)
 {
     Reset();
     m_listbox.AddString("服务器断开了");
 //    m_listbox.SetTopIndex(m_listbox.GetCount()-1);

     m_edit_ip.EnableWindow(TRUE);
     m_edit_port.EnableWindow(TRUE);
     m_bn_connect.EnableWindow(TRUE);
     m_bn_disconnect.EnableWindow(FALSE);
     m_bn_clear.EnableWindow(TRUE);
     m_bn_send.EnableWindow(FALSE);
     m_bn_rewrite.EnableWindow(FALSE);
     m_editbox.EnableWindow(FALSE);
 }

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

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

 void CMySocket::OnClose(int nErrorCode)
 {
     // TODO: 在此添加专用代码和/或调用基类
     m_dlg->OnClose();
     CSocket::OnClose(nErrorCode);
 }

 void CMySocket::OnReceive(int nErrorCode)
 {
     // TODO: 在此添加专用代码和/或调用基类
     m_dlg->OnReceive();
     AsyncSelect(FD_READ|FD_CLOSE|FD_WRITE);    //需使用此条用于随时接收信息
     CSocket::OnReceive(nErrorCode);
 }

5、大功告成。

小结:

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

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

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

004之MFCSocket同步编程(指针机制)的更多相关文章

  1. 003之MFCSocket异步编程(指针机制)

    002篇是采用传统方式创建,不适应动态的网络环境,服务器为客户端保留着断开连接时的套接字,不够灵活.而采用指针机制不仅可以更加灵活性,而且能使代码更集中,更具有条理性.将其转变成指针机制.功能及运行保 ...

  2. javaEE开发中使用session同步和token机制来防止并发重复提交

    javaEE开发中使用session同步和token机制来防止并发重复提交 通常在普通的操作当中,我们不需要处理重复提交的,而且有很多方法来防止重复提交.比如在登陆过程中,通过使用redirect,可 ...

  3. Linux 信号量同步编程

    前一篇文章概述了Linux 系统中信号量互斥编程,这篇文章正好是前一篇的姊妹篇----信号量同步.说它们是姊妹篇是因为它们都是利用了内核的信号量机制实现了进程间的通信.因为两者所解决的问题不同,因此它 ...

  4. js同步 异步 运行机制

    需要知道的那些事: 1.JS是单线程的(为什么?因为能提高效率.作为浏览器脚本语言,js的主要用途是与用户互动,操作DOM.而这也就决定它只能为单线程,否则会带来很复杂的同步问题),也就是说无法同时执 ...

  5. 【java回调】同步/异步回调机制的原理和使用方法

    回调(callback)在我们做工程过程中经常会使用到,今天想整理一下回调的原理和使用方法. 回调的原理可以简单理解为:A发送消息给B,B处理完后告诉A处理结果.再简单点就是A调用B,B调用A. 那么 ...

  6. lua 面向对象编程类机制实现

    lua no class It is a prototype based language. 在此语言中没有class关键字来创建类. 现代ES6, 已经添加class类. prototype bas ...

  7. 002之MFCSocket异步编程

    当今的网络程序通用体系结构大多为C/S模式,服务器监听收到来自客户端的请求,然后响应并作出应答. 界面对话框如下,输入IP信息进行通信后再进行连接,连接成功即可开始通信.左侧为客户端,右侧为服务端. ...

  8. Windows网络通信(一):socket同步编程

    网络通信常用API 1. WSAStartup用于初始化WinSock环境 int WSAStartup( WORD wVersionRequested, LPWSADATA lpWSAData ); ...

  9. Linux多线程编程——多线程与线程同步

    多线程 使用多线程好处: 一.通过为每种事件类型的处理单独分配线程,可以简化处理异步事件的代码,线程处理事件可以采用同步编程模式,启闭异步编程模式简单 二.方便的通信和数据交换 由于进程之间具有独立的 ...

随机推荐

  1. mac OS.NE开发环境搭建

    合肥程序员群:49313181.    合肥实名程序员群:128131462 (不愿透露姓名和信息者勿加入,申请备注填写姓名+技术+工作年限) Q  Q:408365330     E-Mail:eg ...

  2. 图解集合5:不正确地使用HashMap引发死循环及元素丢失

    问题引出 前一篇文章讲解了HashMap的实现原理,讲到了HashMap不是线程安全的.那么HashMap在多线程环境下又会有什么问题呢? 几个月前,公司项目的一个模块在线上运行的时候出现了死循环,死 ...

  3. c中的指针

    一. 指针前奏 1. 指针的重要性 指针是C语言中非常重要的数据类型,如果你说C语言中除了指针,其他你都学得很好,那你干脆说没学过C语言. 2. 小需求 l void change(int  n)函数 ...

  4. RabbitMQ用户角色及权限控制

    RabbitMQ的用户角色分类:none.management.policymaker.monitoring.administrator RabbitMQ各类角色描述:none不能访问 managem ...

  5. 博客中最快捷的公式显示方式:Mathjax + Lyx

    经常为在博客园中显示公式而烦恼的同志们看过来!! 什么是mathjax? 答:就是在web中显示公式用的,基于JavaScript写的,关键是开源,网址http://www.mathjax.org/, ...

  6. 锋利的jQuery读书笔记---jQuery中操作DOM

    一般来说,DOM的操作分为3个方面,即DOM Core.HTML-DOM和CSS-DOM jQuery中的DOM操作主要包括以下种类: 查找节点 查找元素节点 查找属性节点 创建节点 创建元素节点 创 ...

  7. Android Dependencies小差号引起的问题

    问题是由于Android Dependencies小差号引起的,下午一搞Android的哥们在群里说最近导入的几个工程每个都是Android Dependencies报错,小差号,我先说解决方法: 方 ...

  8. 第五章 Spring3.0 、Hibernate3.3与Struts2的整合 基于Annotation

    Annotation的方式是通过注解的方式把Struts2中的Action.Dao层的实现类.Service层的实现类交由Spring管理,不需要在配置文件中进行配置.但为了方便,事务的管理依然使用的 ...

  9. js中substr、substring、slice的区别

    substr(start, length) substring(from, to) slice(from, to) 以上函数只传一个参数时,认为是起始位置,然后按照正方向截取 substring的参数 ...

  10. DataGridView 多列排序功能

    System.Data.DataTable dt = new System.Data.DataTable(); private void FillDataGridView() { dt.Columns ...