当今的网络程序通用体系结构大多为C/S模式,服务器监听收到来自客户端的请求,然后响应并作出应答。

界面对话框如下,输入IP信息进行通信后再进行连接,连接成功即可开始通信。左侧为客户端,右侧为服务端。

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

客户端只需要一个进行通信,服务器端需要两个,一个用于监听,一个用于通信(头文件包含在h中与cpp中会有差别)。

1)客户端:新建基于CAsyncSocket的类CClientSocket用于客户端通信;并在CXXXDlg.h中声明创建实例对象m_ClientSocket;

2)服务端:新建基于CAsyncSocket的类CServerSocket用于服务,类CListenSocket用于监听;并在CXXXDlg.h中声明创建实例对象。m_ListenSocket/m_ServerSocket。注意互相在头文件中包含双方的头文件,便于后续使用。

2、在客户端/服务器端界面中编辑完善相应的控件,并增加相应的变量

 

 

Tips:部分控件设置Control变量主要是用于实现按钮在不同时期的可用性。

3、客户端实现连接/断开函数,服务器端实现监听与断开事件

1)客户端:发起连接,调用Create函数创建套接字并执行connect建立连接:断开时执行通信断开即可,并在对话框中显示通知。

     #include "ClientSocket.h"

 void Ccase003Dlg::OnBnClickedBnConnect()
{
// TODO: 在此添加控件通知处理程序代码
// TODO: 在此添加控件通知处理程序代码
BYTE nfield[];
CString strIP;
int sPort;
UpdateData(); //默认为true,将输入值传入控件,否则将控件变量值输出到文本框中 if(ServerIP.IsBlank()||m_str_port.IsEmpty())//判断输入变量是否合法
{
AfxMessageBox("IP地址与端口不能为空");
return ; //如不执行return 则会继续执行赋IP操作
} //将IP传给地址框
sPort=atoi(m_str_port);
ServerIP.GetAddress(nfield[],nfield[],nfield[],nfield[]);
strIP.Format("%d.%d.%d.%d",nfield[],nfield[],nfield[],nfield[]);
//初始化套接字创建socket句柄,默认有winsock自动选择端口号,默认为流式套接字
//第三个参数用来指定感兴趣的网络事件掩码位,默认全部包含,最后一个是指定套接字的网络地址
//成功返回非零,可调用GetLastError获取错误信息,客户端不接收其他客户连接,默认即可
m_clientsocket.Create();
// m_clientsocket.Connect(strIP,atoi(m_str_port)); //用于建立连接;第二种结构为sockaddr结构指针,用于winapi
m_clientsocket.Connect(strIP,sPort);
} void Ccase003Dlg::OnBnClickedBnDisconnect()
{
// TODO: 在此添加控件通知处理程序代码 m_clientsocket.Close(); //关闭套接字并释放描述符
m_listbox.AddString("已断开连接!");
m_listbox.SetTopIndex(m_listbox.GetCount()-);
//权限控制的内容过于重复, 暂时剔除
}

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

 void Ccase004Dlg::OnBnClickedListen()
{
// TODO: 在此添加控件通知处理程序代码
BYTE nfield[];
CString strIP;
int sPort;
UpdateData(); //默认为true,将输入值传入控件,否则将控件变量值输出到文本框中 if(ServerIP.IsBlank())//判断输入变量是否合法
{
AfxMessageBox("IP地址与端口不能为空");
return ; //如不执行return 则会继续执行赋IP操作
} sPort = atoi(m_str_port);
//将IP传给地址框
ServerIP.GetAddress(nfield[],nfield[],nfield[],nfield[]);
strIP.Format("%d.%d.%d.%d",nfield[],nfield[],nfield[],nfield[]);
//初始化套接字创建socket句柄,默认有winsock自动选择端口号,默认为流式套接字,1
//第三个参数用来指定感兴趣的网络事件掩码位,默认全部包含,最后一个是指定套接字的网络地址
//成功返回非零,可调用GetLastError获取错误信息,客户端不接收其他客户连接,默认即可
m_listensocket.Create(sPort,,FD_ACCEPT,strIP); m_listensocket.Listen(); //参数用于指定愿意接受客户端数目,必须调用
m_listbox.AddString("开始监听");
m_listbox.AddString("地址"+strIP+"端口"+m_str_port);
m_listbox.AddString("等待客户连接...");
m_listbox.SetTopIndex(m_listbox.GetCount()-); //设置为信息往下滚动 } void Ccase004Dlg::OnBnClickedStopListen()
{
// TODO: 在此添加控件通知处理程序代码
m_listensocket.Close();
m_listbox.AddString("停止监听");
m_listbox.SetTopIndex(m_listbox.GetCount()-); //设置为信息往下滚动
}

3)完成其他对应的功能模块:可以看出,除了监听/连接设置了不同的函数,发送信息、接收信息、断开连接、停止监听、清空列表、重新输入都是一样的代码。也就是说除了创建连接时有所差异,其余的代码都可以通用。

 void Ccase003Dlg::OnBnClickedBnDisconnect()
{
// TODO: 在此添加控件通知处理程序代码
m_clientsocket.Close();
m_listbox.AddString("已断开连接");
m_listbox.SetTopIndex(m_listbox.GetCount()-); } void Ccase003Dlg::OnBnClickedBnSend()
{
// TODO: 在此添加控件通知处理程序代码
UpdateData();    //此处可加一个判定,如果输入为空则提示并返回
m_clientsocket.Send(m_str_words,m_str_words.GetLength());
m_listbox.AddString("发送: "+m_str_words); //在本端显示发送的内容
m_listbox.SetTopIndex(m_listbox.GetCount()-); //设置为信息往下滚动
m_editbox.SetWindowText(""); //注意发送后清空输入内容
m_editbox.SetFocus(); //发送后焦点指定在编辑栏
} void Ccase003Dlg::OnBnClickedBnRewrite()
{
// TODO: 在此添加控件通知处理程序代码
m_editbox.SetWindowText("");
m_editbox.SetFocus();
} void Ccase003Dlg::OnBnClickedBnClear()
{
// TODO: 在此添加控件通知处理程序代码
m_listbox.ResetContent();
}

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

在执行相应按钮操作后,系统会根据程序运行自动触发响应。在socket实例对象中重写相应的处理函数。客户端系统发起连接触发connect进行跟进,服务器端系统接收到connect请求触发accept响应,此时建立起连接,通过receive接收程序发送的数据,最后close关闭释放套接字。

1)客户端:客户端发起send在类中重写函数OnConnect

 void CClientSocket::OnConnect(int nErrorCode)
{
// TODO: 在此添加专用代码和/或调用基类
Ccase003Dlg* plist=(Ccase003Dlg*)(AfxGetApp()->m_pMainWnd); if(nErrorCode)
{
AfxMessageBox("不能连接,请重试");
plist->m_clientsocket.Close(); //如果不关闭套接字,则因为前一次的套接字还未释放,故重按会失败
return ;
} plist->m_listbox.AddString("连接成功!");
plist->m_listbox.SetTopIndex(plist->m_listbox.GetCount()-);
/*
plist->m_edit_ip.EnableWindow(FALSE);
plist->m_edit_port.EnableWindow(FALSE);
plist->m_bn_connect.EnableWindow(FALSE);
plist->m_bn_disconnect.EnableWindow(TRUE);
plist->m_bn_clear.EnableWindow(TRUE);
plist->m_bn_send.EnableWindow(TRUE);
plist->m_bn_rewrite.EnableWindow(TRUE);
plist->m_editbox.EnableWindow(TRUE);
*/
CAsyncSocket::OnConnect(nErrorCode);
}

2)服务器端:客户端发起在ListenSocket类中重写函数OnAccept。

 void CListenSocket::OnAccept(int nErrorCode)
{
// TODO: 在此添加专用代码和/或调用基类
Ccase004Dlg* plist=(Ccase004Dlg*)(AfxGetApp()->m_pMainWnd); Accept(plist->m_serversocket); //接收连接请求,并取出第一个连接创建套接字用于通信,原始套接字依然保持打开并监听
plist->m_serversocket.AsyncSelect(FD_READ|FD_WRITE|FD_CLOSE); //调用侦查 plist->m_listbox.AddString("连接成功!");
plist->m_listbox.SetTopIndex(plist->m_listbox.GetCount()-);
/* //如需设置权限,也可在此处获取对话框指针后进行设置,增强程序的鲁棒性
plist->m_edit_ip.EnableWindow(FALSE);
plist->m_edit_port.EnableWindow(FALSE);
plist->m_bn_listen.EnableWindow(FALSE);
plist->m_bn_stoplisten.EnableWindow(TRUE);
plist->m_bn_disconnect.EnableWindow(TRUE);
plist->m_bn_clear.EnableWindow(TRUE);
plist->m_bn_send.EnableWindow(TRUE);
plist->m_bn_rewrite.EnableWindow(TRUE);
plist->m_editbox.EnableWindow(TRUE);
*/
CAsyncSocket::OnAccept(nErrorCode);
}

3)完成OnReceive(接收)和OnClose(关闭)函数,客户端、监听端与服务器端功能一样。因需要接收信息,还需在ServerSocket类中重写OnConnect(不贴出)。

 void CClientSocket::OnReceive(int nErrorCode)
{
// TODO: 在此添加专用代码和/或调用基类
char temp[];
int n=Receive(temp,);
temp[n]='\0';
CString message;
message.Format("收到: %s",temp);
Ccase003Dlg* plist=(Ccase003Dlg*)(AfxGetApp()->m_pMainWnd);
plist->m_listbox.AddString(message);
plist->m_listbox.SetTopIndex(plist->m_listbox.GetCount()-); CAsyncSocket::OnReceive(nErrorCode);
} void CClientSocket::OnClose(int nErrorCode)
{
// TODO: 在此添加专用代码和/或调用基类
Ccase003Dlg* plist=(Ccase003Dlg*)(AfxGetApp()->m_pMainWnd);
plist->m_clientsocket.Close();
plist->m_listbox.AddString("关闭连接");
plist->m_listbox.SetTopIndex(plist->m_listbox.GetCount()-); /* plist->m_edit_ip.EnableWindow(TRUE);
plist->m_edit_port.EnableWindow(TRUE);
plist->m_bn_connect.EnableWindow(TRUE);
plist->m_bn_disconnect.EnableWindow(FALSE);
plist->m_bn_clear.EnableWindow(TRUE);
plist->m_bn_send.EnableWindow(FALSE);
plist->m_bn_rewrite.EnableWindow(FALSE);
plist->m_editbox.EnableWindow(FALSE);
*/
CAsyncSocket::OnClose(nErrorCode);
}

4、大功告成。可以补充完善相应的优化控制,比如点击连接之前不可以点击断开;连接之后断开应释放对应套接字,否则再点击会崩溃。

5、小结:会者不难,先了解原理,然后再码代码。

  1)对于控件控制,初始化窗口时可设置初始状态(最好是全部初始化,这样在函数响应时可以按流程思路进行调整,也可同时设置对话框标题);

2)异步通信:服务器端打开监听(Listen),触发Accept接收请求,客户端发出连接,触发connect发送请求,至此可实现连接。使用发送Send,有信息触发Receive接收显示信息。

002之MFCSocket异步编程的更多相关文章

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

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

  2. C#异步编程(一)

    异步编程简介 前言 本人学习.Net两年有余,是第一次写博客,虽然写的很认真,当毕竟是第一次,肯定会有很多不足之处, 希望大家照顾照顾新人,有错误之处可以指出来,我会虚心接受的. 何谓异步 与同步相对 ...

  3. C#与C++的发展历程第三 - C#5.0异步编程巅峰

    系列文章目录 1. C#与C++的发展历程第一 - 由C#3.0起 2. C#与C++的发展历程第二 - C#4.0再接再厉 3. C#与C++的发展历程第三 - C#5.0异步编程的巅峰 C#5.0 ...

  4. 关于如何提高Web服务端并发效率的异步编程技术

    最近我研究技术的一个重点是java的多线程开发,在我早期学习java的时候,很多书上把java的多线程开发标榜为简单易用,这个简单易用是以C语言作为参照的,不过我也没有使用过C语言开发过多线程,我只知 ...

  5. 异步编程 In .NET

    概述 在之前写的一篇关于async和await的前世今生的文章之后,大家似乎在async和await提高网站处理能力方面还有一些疑问,博客园本身也做了不少的尝试.今天我们再来回答一下这个问题,同时我们 ...

  6. C#异步编程(二)

    async和await结构 序 前篇博客异步编程系列(一) 已经介绍了何谓异步编程,这篇主要介绍怎么实现异步编程,主要通过C#5.0引入的async/await来实现. BeginInvoke和End ...

  7. [.NET] 利用 async & await 的异步编程

    利用 async & await 的异步编程 [博主]反骨仔 [出处]http://www.cnblogs.com/liqingwen/p/5922573.html  目录 异步编程的简介 异 ...

  8. [.NET] 怎样使用 async & await 一步步将同步代码转换为异步编程

    怎样使用 async & await 一步步将同步代码转换为异步编程 [博主]反骨仔 [出处]http://www.cnblogs.com/liqingwen/p/6079707.html  ...

  9. [C#] 走进异步编程的世界 - 开始接触 async/await

    走进异步编程的世界 - 开始接触 async/await 序 这是学习异步编程的入门篇. 涉及 C# 5.0 引入的 async/await,但在控制台输出示例时经常会采用 C# 6.0 的 $&qu ...

随机推荐

  1. Fibonacci Heaps

    Mergeable heapsA mergeable heap is any data structure that supports the following five operations,in ...

  2. http/ftp等的URL匹配正则表达式 ZT

    网上流传着多种匹配URL的正则表达式版本,但我经过试验,最好用的还是从stackoverflow上查到的: (https?|ftp|file)://[-A-Za-z0-9+&@#/%?=~_| ...

  3. 自动化测试-3.selenium8种常用元素定位

    自动化只要掌握四步操作:获取元素,操作元素,获取返回结果,断言(返回结果与期望结果是否一致),最后自动出测试报告.本篇主要讲如何用firefox辅助工具进行元素定位. 元素定位在这四个环节中是至关重要 ...

  4. 我的第一个爬虫程序:利用Python抓取网页上的信息

    题外话 我第一次听说Python是在大二的时候,那个时候C语言都没有学好,于是就没有心思学其他的编程语言.现在,我的毕业设计要用到爬虫技术,在网上搜索了一下,Python语言在爬虫技术这方面获得一致好 ...

  5. windows环境下安装Python的Rtree包

    Rtree包是基于libspatialindex开发的,在安装Rtree之前必须先安装libspatialindex.关于libspatialindex,除了官网的英文外,这里有一个中文翻译过来的介绍 ...

  6. jquery移除元素时会自动解绑事件

    .html() When .html() is used to set an element's content, any content that was in that element is co ...

  7. Restful levels &HATEOAS基本介绍~

    本文所涉及的内容摘自:http://www.manongjc.com/article/93934.html 什么是RESTful REST这个词,是Roy Thomas Fielding在他2000年 ...

  8. 项目期复习:JS操作符,弹窗与调试,凝视,数据类型转换

    版权声明:本文为博主原创文章.未经博主同意不得转载. https://blog.csdn.net/huangyibin628/article/details/26364901 1.JS操作符 ① 除法 ...

  9. 解决ansible上传速度慢的问题

    问题: 假如有A.B.C.D....等机器,机器A为Ansible服务器,机器B.C.D...等为Ansible管理的节点服务器,A机器与其他机器都不在同一个网络,也就是A机器必须添加VPN之后才能与 ...

  10. debian系统下apache2开启ssi功能

    SSI (Server Side Include)的 html 文件扩展名 (.shtml), 通常称为"服务器端嵌入"或者叫"服务器端包含"说白了就是类似其他 ...