异步与同步通信相比较,前者是非阻塞模式,后者是阻塞模式。有关两者差异在此博主中有详细讲解,推荐: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("%d.%d.%d.%d",nFild[],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("%d.%d.%d.%d",nFild[],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. boot asio 非阻塞同步编程---非阻塞的accept和receive。

    boot asio 非阻塞同步编程---非阻塞的accept和receive. 客户端编程: #include<boost/timer.hpp> #include <iostream ...

  4. 网络编程基础----并发编程 ---守护进程----同步锁 lock-----IPC机制----生产者消费者模型

    1  守护进程: 主进程 创建 守护进程   辅助主进程的运行 设置进程的 daemon属性 p1.daemon=True 1 守护进程会在主进程代码执行结束后就终止: 2 守护进程内无法再开启子进程 ...

  5. Linux 信号量同步编程

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

  6. Linux环境编程进程间通信机制理解

    一.Linux系统调用主要函数 二.创建进程 1.创建子进程系统调用fork() 2.验证fork()创建子进程效果 3.系统调用fork()与挂起系统调用wait() 三.模拟进程管道通信 四.pi ...

  7. js同步 异步 运行机制

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

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

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

  9. Java线程同步的Monitor机制(Lock配合Condition)

    Monitor模式是一种常见的并行开发机制, 一个Monitor实例可以被多个线程安全使用, 所有的monitor下面的方法在运行时是互斥的, 这种互斥机制机制可以用于一些特性, 例如让线程等待某种条 ...

随机推荐

  1. 【druid 】数据库连接池

    一.数据库连接池架构 二.数据库连接池的过滤器 spi的思想,加载配置文件的Filter druid.filters.default=com.alibaba.druid.filter.stat.Sta ...

  2. [R] 繪圖 Par 函数

    本篇內文主引用 https://zhuanlan.zhihu.com/p/21394945 之內容再稍加整理並參照下方有用資源 [rdocumentation] https://www.rdocume ...

  3. Evosuite使用方法入门

    ​ Evosuite使用方法入门 ​ 1.简要介绍 EvoSuite开源工具可以基于Eclipse进行测试用例的自动生成,生成的测试用例符合Junit标准(直接生成可进行Junit的java文件),满 ...

  4. java web(四):request、response一些用法和文件的上传和下载

    上一篇讲了ServletContent.ServletCOnfig.HTTPSession.request.response几个对象的生命周期.作用范围和一些用法.今天通过一个小项目运用这些知识.简单 ...

  5. Luminar 3 for Mac(照片编辑工具)v3.1.0中文特别版

    Luminar for Mac是一款多功能照片编辑软件,使用独特的AI工具加快速度,具备AI Sky Enhancer.Accent AI.太阳光线等创新功能.当然也保留了原有的功能,帮助你轻松的修复 ...

  6. android studio 升级到3.3.1后,提示程序包不存在

    android studio 升级到3.3.1后,提示程序包不存在 原因 主Module--A 引用了其他Moduel--B里的jar库, 只需要把B的dependencies改成如下(implent ...

  7. 【C++】Lambda表达式

    转自 https://www.cnblogs.com/DswCnblog/p/5629165.html 作者:dsw846 C++11的一大亮点就是引入了Lambda表达式.利用Lambda表达式,可 ...

  8. win10 家庭版 升级 win10企业版

    更改秘钥 我的电脑(右键)->属性-> 更改产品秘钥 -> 96YNV-9X4RP-2YYKB-RMQH4-6Q72D->重启系统 如果秘钥过期了,就百度按时间搜索,总有一个是 ...

  9. 关于使用format()方法格式化字符串,读这一篇就够了!

    从Python 2.6开始,又出现了另外一种格式化字符串的方法——format()方法.format()方法是字符串众多方法中的一个,调用这个方法时要使用点操作符(.),该方法返回一个格式化好的字符串 ...

  10. Git常用的操作记录(自用)

    分支常用操作命令 $ git branch -a //查看分支 $ git checkout -b dev origin/master  //切换/创建分支 $ git branch -vv 或 gi ...