异步与同步通信相比较,前者是非阻塞模式,后者是阻塞模式。有关两者差异在此博主中有详细讲解,推荐: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)基本操作要烂熟于心。

随机推荐

  1. #研发解决方案介绍#IdCenter(内部统一认证系统)

    郑昀 基于朱传志的设计文档 最后更新于2014/11/13 关键词:LDAP.认证.权限分配.IdCenter. 本文档适用人员:研发   曾经一个IT内部系统配一套帐号体系和授权   线上生产环境里 ...

  2. 一种基于Orleans的分布式Id生成方案

    基于Orleans的分布式Id生成方案,因Orleans的单实例.单线程模型,让这种实现变的简单,贴出一种实现,欢迎大家提出意见 public interface ISequenceNoGenerat ...

  3. VMware Workstation 下进行 桥连接

    大家都知道进行桥连接的时候,需要我们的宿主机与虚拟机同处于一个网络段, 使得mask与默认网关相同即可进行连接 ; 本地的IP .掩码 . 网关: 虚拟机的Ip 掩码,网关: // 当然这里的DNS ...

  4. Codeforces Round #253 (Div. 1) B. Andrey and Problem

    B. Andrey and Problem time limit per test 2 seconds memory limit per test 256 megabytes input standa ...

  5. CodeIgniter学习一:基础知识

    1. url片段(CI域名组成说明)        example.com/index.php/test/index   第一部分(test):控制器 第二部分(index):方法,动作 如果第二部分 ...

  6. [BZOJ2761] [JLOI2011] 不重复数字 (set)

    Description 给出N个数,要求把其中重复的去掉,只保留第一次出现的数. 例如,给出的数为1 2 18 3 3 19 2 3 6 5 4,其中2和3有重复,去除后的结果为1 2 18 3 19 ...

  7. 浅谈 C# SQL防注入

    1#region 防止sql注入式攻击(可用于UI层控制)  2  3///   4/// 判断字符串中是否有SQL攻击代码  5///   6/// 传入用户提交数据  7/// true-安全:f ...

  8. Go开发之路 -- 流程控制

    1. if else if 条件 { } else { // else必须写在这里 } // 写一个程序, 从终端读取输入, 并转成整数. 如果转成整数出错, // 则输出'can not conve ...

  9. BeautifulSoup学习 之结构

    Beautiful Soup将复杂HTML文档转换成一个复杂的树形结构,每个节点都是Python对象,所有对象可以归纳为4种: Tag NavigableString BeautifulSoup Co ...

  10. 华大单片机开发板HC32L13X上手入门

    HC32L136开发板(如下图所示)分为板载调试模块(左半部分)和MCU开发电路(右半部分).二者中间通过邮票孔相连,如果将板子从中间掰开,板载调试模块就可以当一个CMSIS-DAP的仿真器来使用.此 ...