MFC

Wesley13
• 阅读 663

一、CAsyncSocket类

CAsyncSocket属于异步非阻塞类。

CAsyncSocket类采用了windows socket中的WSAAsyncSelect模型。CAsyncSocket 类是在很低的层次上对windows socket API进行了封装,它的成员函数和winsock API的函数调用直接对应,一个CAsyncSocket对象代表了一个windows套接字,它是网络通信的端点。该类将根据不同的windows套接字消息调用CAsyncSocket类的回调函数。

OnAccept 

通知侦听套接字,它可以通过调用Accept,接受挂起连接请求

OnClose 

通知套接字,关闭对它的套接字连接

OnConnect 

通知连接套接字,连接尝试已经完成,无论成功或失败

OnOutOfBandData 

通知接收套接字,在套接字上有带外数据读入,通常是忙消息

OnReceive 

通知侦听套接字,通过调用Receive恢复数据

OnSend 

通知套接字,通过调用Send,它可以发送数据

 

网络应用程序一般采用客户端/服务器模式,他们使用的CAsyncSocket编程有所不同,下面以表格的形式方式看一下服务器和客户端之间的不同

序号

服务端

客户端

1

构造一个套接字

CAsyncSocket sockServer

构造一个套接字

CAsyncSocket sockClient

2

创建SOCKET句柄,绑定到指定的端口

sockServer.Create(nPort);

创建SOCKET句柄,使用默认参数

sockClient.Create();

3

启动监听,时刻准备接收连接请求

sockServer.Listen();

 

4

 

请求链接服务器

sockClient.Connect(strAddress,nPort)

5

构造一个新的空套接字

CAsyncSocket sockRecv;

接收连接

sockServer.Accept(sockRecv);

 

6

接收数据

sockRecv.Receive(pBuffer,nLen);

发送连接

sockClient.Send(pBuffer,nLen);

7

发送数据

sockRecv.Send(pBuffer,nLen);

接收数据

sockClient.Receive(pBuffer,nLen);

8

关闭套接字对象

sockRecv.Close();

关闭套接字对象

sockClient.Close();

ps:客户端与服务端都要首先构造一个CAsyncSocket对象,然后使用该对象的Create成员函数来创建底层的SOCKET句柄。服务器端要绑定到特定的端口

对于服务器端的套接字对象,应使用CAsyncSocket::Listen函数进行监听状态,一旦收到来自客户端的链接请求,就调用CAsyncSocket::Accept来接收。对于客户端的套接字对象,应当使用CAsyncSocket::Connect来连接到一个服务器端的套接字对象。建立链接之后,双方就可以按照应用层协议交换数据了。

这里需要注意,Accept是将一个新的空CAsyncSocket对象作为它的参数,在调用Accept之前必须构造这个对象。与客户端套接字的连接是通过它建立的,如果这个套接字对象退出,连接也就关闭。对于这个新的套接字对象,不需要调用Create来创建它的底层套接字

调用CAsyncSocket对象的其他成员函数,如Send和Receive执行与其他套接字对象的通信,这些成员函数与Windows Sockets API函数在形式和用法上基本是一致的。

关闭并销毁CAsyncSocket对象。如果在堆栈上创建了套接字对象,当包含此对象的函数退出时,会调用该类的析构函数,销毁该对象。在销毁该对象之前,析构函数会调用该对象的Close成员函数。如果在堆上使用new创建了套接字对象,可先调用Close成员函数关闭它,在使用delete来删除释放该对象。

CAsyncSocket编程注意问题:

a) 阻塞处理。CAsyncSocket对象专用于异步操作,不支持阻塞工作模式,如果应用程序需要支持阻塞操作,必须自己解决。

b) 字节顺序的转换。在不同的结构类型的计算机之间进行数据传输时,可能会有计算机之间字节存储顺序不一致的情况,需要自己对不用的字节顺序进行转换。

c) 字符串转换。同样不同结构类型的计算机的字符串顺序也可能不同,需要自行转换。

d)在使用CAsyncSocket之前,必须调用AfxSocketInit初始化WinSock环境,而AfxSocketInit会创建一个隐藏的CSocketWnd对象,由于这个对象由Cwnd派生,因此它能够接收Windows消息。一方面它会接受各个CAsyncSocket的状态报告,另一方面它能捕捉系统发出的各种SOCKET事件,其通信流程如下。

MFC

二、CSocket类

CSocket是MFC在CAsyncSocket基础上派生的一个同步阻塞Socket的封装类。

CSocket类是从CAsyncsocket派生而来的,它继承了CAsyncsocket对WindowsSockets API的封装。与CAsyncsocket对象相比,CSocket对象代表了WindowsSockets API的更高一级的抽象化。

a)在使用MFC编写socket程序时,必须要包含<afxsock.h>都文件。

b) AfxSocketInit() 这个函数,在使用CSocket前一定要先调用该函数,否则使用CSocket会出错;并且该函数还有一个重要的使用方式,就是在某个线程下使用 CSocket 前一定要调用,就算主线程调用了该函数,在子线程下使用 CSocket 也要先调用该函数,要不会出错。

c) 还要注意的是, Create 方法已经包含了 Bind 方法,如果是以 Create 方法初始化的前提下不能再调用 Bind ,要不一定出错。

三、代码****实现

设计两个对话框应用程序,通过TCP/IP进行通信,使用MFC的CSocket类实现服务器端和客户端之间的相互通信。

3.1服务器端

服务器端的socket通信需要用到两个socket:

  1. 用来监听连接的socket
  2. 用来和客户端通信的socket,此socket中保存了客户端信息

因此服务器端需要添加两个继承自CSocket的类,分别起名为CServerSocket、CConnectSocket。

3.1.1 CServerSocket 

  此socket主要用来监听客户端请求,当有请求到来时,MFC框架将调用OnAccept函数,所以我们需要重写CSocket类的OnAccept函数。

 1 /////////////////////////////////////////////////////////
 2 # ServerSocket.h 文件
 3 /////////////////////////////////////////////////////////
 4 class CServerSocket : public CSocket
 5 {
 6 public:
 7     CServerSocket();
 8     virtual ~CServerSocket();
 9 
10     void OnAccept(int nErrorCode);
11     //开启socket服务
12     void StartServer(UINT nPort);
13     //发送消息函数
14     void MessageSend(const char* pMesg);
15 
16 private:
17     //保存的是客户端的socket信息
18     CConnectSocket m_clientSock;
19 };

  在OnAccept函数中调用Accept函数,接受客户端请求,另外将客户信息保存到m_clientSock中,使用此套接字对象与客户端进行通信,发送信息可以直接调用API函数Send。

  在StartServer函数中做了两件事儿,创建套接字,监听套接字。需要主要的是Create函数内部已经对套接字进行了绑定,所以不需要再次绑定。

 1 /////////////////////////////////////////////////////////
 2 # ServerSocket.cpp 文件
 3 /////////////////////////////////////////////////////////
 4 void CServerSocket::OnAccept(int nErrorCode)
 5 {
 6     Accept(m_clientSock);
 7     CSocket::OnAccept(nErrorCode);
 8 }
 9 
10 void CServerSocket::StartServer(UINT nPort)
11 {
12     if (!Create(nPort))
13     {
14         AfxMessageBox(_T("Socket 创建失败!"));
15         return;
16     }
17     if (!Listen(5))
18     {
19         AfxMessageBox(_T("Socket 监听失败!"));
20         return;
21     }
22 }
23 
24 void CServerSocket::MessageSend(const char* pMesg)
25 {
26     m_clientSock.Send(pMesg, strlen(pMesg) + 1);
27 }

3.1.2CConnectSocket类

  此类中保存了客户端信息,所以用来与客户端进行通信,重写了OnSend和OnReceive函数,这两个函数由MFC框架调用。

 1 /////////////////////////////////////////////////////////
 2 # ConnectSocket.h 文件
 3 /////////////////////////////////////////////////////////
 4 class CConnectSocket : public CSocket
 5 {
 6 public:
 7     CConnectSocket();
 8     virtual ~CConnectSocket();
 9 
10     //函数重写
11     void OnSend(int nErrorCode);
12     void OnReceive(int nErrorCode);
13 };

当客户端与客户端连接成功之后服务器端框架会调用OnSend函数给客户端发送通知。另外,当服务器端接收到客户端发来的消息之后,MFC框架会调用OnReceive函数,我们可以在此函数中对接收到的消息进行处理。

 1 /////////////////////////////////////////////////////////
 2 # ConnectSocket.cpp 文件
 3 /////////////////////////////////////////////////////////
 4 void CConnectSocket::OnSend(int nErrorCode)
 5 {
 6     char* pSend = "你好, 我是服务器,我们已经成功建立了连接!";
 7     Send(pSend, strlen(pSend) + 1);
 8     CSocket::OnSend(nErrorCode);
 9 }
10 
11 void CConnectSocket::OnReceive(int nErrorCode)
12 {
13     char bufRecv[1024];
14     int nCount = Receive(bufRecv, 1024);
15     bufRecv[nCount] = 0;
16     CUnicodeAndChar uc;
17     CString str = uc.MultiToWide(string(bufRecv));
18     AfxMessageBox(str);
19     CSocket::OnReceive(nErrorCode);
20 }
点赞
收藏
评论区
推荐文章
blmius blmius
2年前
MySQL:[Err] 1292 - Incorrect datetime value: ‘0000-00-00 00:00:00‘ for column ‘CREATE_TIME‘ at row 1
文章目录问题用navicat导入数据时,报错:原因这是因为当前的MySQL不支持datetime为0的情况。解决修改sql\mode:sql\mode:SQLMode定义了MySQL应支持的SQL语法、数据校验等,这样可以更容易地在不同的环境中使用MySQL。全局s
Jacquelyn38 Jacquelyn38
2年前
2020年前端实用代码段,为你的工作保驾护航
有空的时候,自己总结了几个代码段,在开发中也经常使用,谢谢。1、使用解构获取json数据let jsonData  id: 1,status: "OK",data: 'a', 'b';let  id, status, data: number   jsonData;console.log(id, status, number )
皕杰报表之UUID
​在我们用皕杰报表工具设计填报报表时,如何在新增行里自动增加id呢?能新增整数排序id吗?目前可以在新增行里自动增加id,但只能用uuid函数增加UUID编码,不能新增整数排序id。uuid函数说明:获取一个UUID,可以在填报表中用来创建数据ID语法:uuid()或uuid(sep)参数说明:sep布尔值,生成的uuid中是否包含分隔符'',缺省为
Stella981 Stella981
2年前
KVM调整cpu和内存
一.修改kvm虚拟机的配置1、virsheditcentos7找到“memory”和“vcpu”标签,将<namecentos7</name<uuid2220a6d1a36a4fbb8523e078b3dfe795</uuid
Easter79 Easter79
2年前
Twitter的分布式自增ID算法snowflake (Java版)
概述分布式系统中,有一些需要使用全局唯一ID的场景,这种时候为了防止ID冲突可以使用36位的UUID,但是UUID有一些缺点,首先他相对比较长,另外UUID一般是无序的。有些时候我们希望能使用一种简单一些的ID,并且希望ID能够按照时间有序生成。而twitter的snowflake解决了这种需求,最初Twitter把存储系统从MySQL迁移
Wesley13 Wesley13
2年前
mysql设置时区
mysql设置时区mysql\_query("SETtime\_zone'8:00'")ordie('时区设置失败,请联系管理员!');中国在东8区所以加8方法二:selectcount(user\_id)asdevice,CONVERT\_TZ(FROM\_UNIXTIME(reg\_time),'08:00','0
Wesley13 Wesley13
2年前
00:Java简单了解
浅谈Java之概述Java是SUN(StanfordUniversityNetwork),斯坦福大学网络公司)1995年推出的一门高级编程语言。Java是一种面向Internet的编程语言。随着Java技术在web方面的不断成熟,已经成为Web应用程序的首选开发语言。Java是简单易学,完全面向对象,安全可靠,与平台无关的编程语言。
Stella981 Stella981
2年前
Django中Admin中的一些参数配置
设置在列表中显示的字段,id为django模型默认的主键list_display('id','name','sex','profession','email','qq','phone','status','create_time')设置在列表可编辑字段list_editable
Wesley13 Wesley13
2年前
MySQL部分从库上面因为大量的临时表tmp_table造成慢查询
背景描述Time:20190124T00:08:14.70572408:00User@Host:@Id:Schema:sentrymetaLast_errno:0Killed:0Query_time:0.315758Lock_
Python进阶者 Python进阶者
4个月前
Excel中这日期老是出来00:00:00,怎么用Pandas把这个去除
大家好,我是皮皮。一、前言前几天在Python白银交流群【上海新年人】问了一个Pandas数据筛选的问题。问题如下:这日期老是出来00:00:00,怎么把这个去除。二、实现过程后来【论草莓如何成为冻干莓】给了一个思路和代码如下:pd.toexcel之前把这