ImageVerifierCode 换一换
格式:DOCX , 页数:16 ,大小:23.45KB ,
资源ID:18247920      下载积分:3 金币
快捷下载
登录下载
邮箱/手机:
温馨提示:
快捷下载时,用户名和密码都是您填写的邮箱或者手机号,方便查询和重复下载(系统自动生成)。 如填写123,账号就是123,密码也是123。
特别说明:
请自助下载,系统不会自动发送文件的哦; 如果您已付费,想二次下载,请登录后访问:我的下载记录
支付方式: 支付宝    微信支付   
验证码:   换一换

加入VIP,免费下载
 

温馨提示:由于个人手机设置不同,如果发现不能下载,请复制以下地址【https://www.bdocx.com/down/18247920.html】到电脑端继续下载(重复下载不扣费)。

已注册用户请登录:
账号:
密码:
验证码:   换一换
  忘记密码?
三方登录: 微信登录   QQ登录  

下载须知

1: 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。
2: 试题试卷类文档,如果标题没有明确说明有答案则都视为没有答案,请知晓。
3: 文件的所有权益归上传用户所有。
4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
5. 本站仅提供交流平台,并不能对任何下载内容负责。
6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。

版权提示 | 免责声明

本文(MFC中SOCKET类的设计和实现Word文件下载.docx)为本站会员(b****3)主动上传,冰豆网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对上载内容本身不做任何修改或编辑。 若此文所含内容侵犯了您的版权或隐私,请立即通知冰豆网(发送邮件至service@bdocx.com或直接QQ联系客服),我们立即给予删除!

MFC中SOCKET类的设计和实现Word文件下载.docx

1、所谓非阻塞函数,是指操作启动之后,如果可以立即得到结果就返回结果,否则返回表示结果需要等待的错误信息,不等待任务完成函数就返回。首先,异步函数是非阻塞函数;其次,获取远地信息的数据库函数是阻塞函数(因此,WinSock提供了其异步版本);在Berkeley socket函数部分中,不涉及网络I/O、本地端工作的函数是非阻塞函数;在Berkeley socket函数部分中,网络I/O的函数是可阻塞函数,也就是它们可以阻塞执行,也可以不阻塞执行。这些函数都使用了一个socket,如果它们使用的socket是阻塞的,则这些函数是阻塞函数;如果它们使用的socket是非阻塞的,则这些函数是非阻塞函数。

2、创建一个socket时,可以指定它是否阻塞。在缺省情况下,Berkerley的Socket函数和WinSock都创建“阻塞”的socket。阻塞socket通过使用select函数或者WSAAsynSelect函数在指定操作下变成非阻塞的。WSAAsyncSelect函数原型如下。int WSAAsyncSelect( SOCKET s, HWND hWnd, u_int wMsg, long lEvent );其中,参数1指定了要操作的socket句柄;参数2指定了一个窗口句柄;参数3指定了一个消息,参数4指定了网络事件,可以是多个事件的组合,如:FD_READ 准备读FD_WRITE 准备

3、写FD_OOB 带外数据到达FD_ACCEPT 收到连接FD_CONNECT 完成连接FD_CLOSE 关闭socket。用OR操作组合这些事件值,如FD_READ|FD_WRITEWSAAsyncSelect函数表示对socket s监测lEvent指定的网络事件,如果有事件发生,则给窗口hWnd发送消息wMsg。假定应用程序的一个socket s指定了监测FD_READ事件,则在FD_READ事件上变成非阻塞的。当read函数被调用时,不管是否读到数据都马上返回,如果返回一个错误信息表示还在等待,则在等待的数据到达后,消息wMsg发送给窗口hWnd,应用程序处理该消息读取网络数据。对于异步

4、函数的调用,以类似的过程最终得到结果数据。以gethostbyname的异步版本的使用为例进行说明。该函数原型如下:HANDLE WSAAsyncGetHostByName( const char FAR *name, char FAR *buf, int buflen 在调用WSAAsyncGetHostByName启动操作时,不仅指定主机名字name,还指定了一个窗口句柄hWnd,一个消息ID wMsg,一个缓冲区及其长度。如果不能立即得到主机地址,则返回一个错误信息表示还在等待。当要的数据到达时,WinSock DLL给窗口hWnd发送消息wMsg告知得到了主机地址,窗口过程从指定的缓冲

5、区buf得到主机地址。使用异步函数或者非阻塞的socket,主要是为了不阻塞本线程的执行。在多进程或者多线程的情况下,可以使用两个线程通过同步手段来完成异步函数或者非阻塞函数的功能。2. Socket的使用 WinSock以DLL的形式提供,在调用任何WinSock API之前,必须调用函数WSAStartup进行初始化,最后,调用函数WSACleanUp作清理工作。MFC使用函数AfxSocketInit包装了函数WSAStartup,在WinSock应用程序的初始化函数IninInstance中调用AfxSocketInit进行初始化。程序不必调用WSACleanUp。Socket是网络通

6、信过程中端点的抽象表示。Socket在实现中以句柄的形式被创建,包含了进行网络通信必须的五种信息:连接使用的协议,本地主机的IP地址,本地进程的协议端口,远地主机的IP地址,远地进程的协议端口。要使用socket,首先必须创建一个socket;然后,按要求配置socket;接着,按要求通过socket接收和发送数据;最后,程序关闭此socket。 为了创建socket,使用socket函数得到一个socket句柄: socket_handle = socket(protocol_family. Socket_type, protocol);其中:protocol_family指定socket使

7、用的协议,取值PF_INET,表示Internet(TCP/IP)协议族;Socket_type指socket面向连接或者使用数据报;第三个参数表示使用TCP或者UDP协议。当一个socket被创建时,WinSock将为一个内部结构分配内存,在此结构中保存此socket的信息,到此,socket连接使用的协议已经确定。 创建了socket之后,配置socket。对于面向连接的客户,WinSock自动保存本地IP地址和选择协议端口,但是必须使用connect函数配置远地IP地址和远地协议端口:result = connect(socket_handle, remote_socket_addres

8、s, address_length)remote_socket_address是一个指向特定socket结构的指针,该地址结构为socket保存了地址族、协议端口、网络主机地址。面向连接的服务器则使用bind指定本地信息,使用listen和accept获取远地信息。使用数据报的客户或者服务器使用bind给socket指定本地信息,在发送或者接收数据时指定远地信息。bind给socket指定一个本地IP地址和协议端口,如下:result = bind( socket_hndle, local_socket_address, address_length)参数类型同connect。函数listen

9、监听bind指定的端口,如果有远地客户请求连接,使用accept接收请求,创建一个新的socket,并保存信息。socket_new = accept(socket_listen, socket_address, address_length) 在socket配置好之后,使用socket发送或者接收数据。面向连接的socket使用send发送数据,recv接收数据;使用数据报的socket使用sendto发送数据,recvfrom接收数据。1. MFC对WinSockt API的封装 MFC提供了两个类CAsyncSocket和CSocket来封装WinSock API,这给程序员提供了一个更

10、简单的网络编程接口。CAsyncSocket在较低层次上封装了WinSock API,缺省情况下,使用该类创建的socket是非阻塞的socket,所有操作都会立即返回,如果没有得到结果,返回WSAEWOULDBLOCK,表示是一个阻塞操作。CSocket建立在CAsyncSocket的基础上,是CAsyncSocket的派生类。也就是缺省情况下使用该类创建的socket是非阻塞的socket,但是CSocket的网络I/O是阻塞的,它在完成任务之后才返回。CSocket的阻塞不是建立在“阻塞”socket的基础上,而是在“非阻塞”socket上实现的阻塞操作,在阻塞期间,CSocket实现了

11、本线程的消息循环,因此,虽然是阻塞操作,但是并不影响消息循环,即用户仍然可以和程序交互。1. CAsyncSocket CAsyncSocket封装了低层的WinSock API,其成员变量m_hSocket保存其对应的socket句柄。使用CAsyncSocket的方法如下:首先,在堆或者栈中构造一个CAsyncSocket对象,例如:CAsyncSocket sock;或者CAsyncSocket *pSock = new CAsyncSocket;其次,调用Create创建socket,例如:使用缺省参数创建一个面向连接的socketsock.Create() 指定参数参数创建一个使用数

12、据报的socket,本地端口为30pSocket.Create(30, SOCK_DGRM);其三,如果是客户程序,使用Connect连接到远地;如果是服务程序,使用Listen监听远地的连接请求。其四,使用成员函数进行网络I/O。最后,销毁CAsyncSocket,析构函数调用Close成员函数关闭socket。下面,分析CAsyncSocket的几个函数,从中可以看到它是如何封装低层的WinSock API,简化有关操作的;还可以看到它是如何实现非阻塞的socket和非阻塞操作。2. socket对象的创建和捆绑 (1)Create函数首先,讨论Create函数,分析socket句柄如何被

13、创建并和CAsyncSocket对象关联。Create的实现如下:BOOL CAsyncSocket:Create(UINT nSocketPort, int nSocketType,long lEvent, LPCTSTR lpszSocketAddress)if (Socket(nSocketType, lEvent)if (Bind(nSocketPort,lpszSocketAddress)return TRUE;int nResult = GetLastError();Close();WSASetLastError(nResult);return FALSE;参数1表示本socket

14、的端口,缺省是0,如果要创建数据报的socket,则必须指定一个端口号。参数2表示本socket的类型,缺省是SOCK_STREAM,表示面向连接类型。参数3是屏蔽位,表示希望对本socket监测的事件,缺省是FD_READ | FD_WRITE | FD_OOB | FD_ACCEPT | FD_CONNECT | FD_CLOSE。参数4表示本socket的IP地址字符串,缺省是NULL。Create调用Socket函数创建一个socket,并把它捆绑在this所指对象上,监测指定的网络事件。参数2和3被传递给Socket函数,如果希望创建数据报的socket,不要使用缺省参数,指定参数2

15、是SOCK_DGRM。如果上一步骤成功,则调用bind给新的socket分配端口和IP地址。(2)Socket函数接着,分析Socket函数,其实现如下:Socket(int nSocketType, long lEvent,int nProtocolType, int nAddressFormat)ASSERT(m_hSocket = INVALID_SOCKET);m_hSocket = socket(nAddressFormat,nSocketType,nProtocolType);if (m_hSocket != INVALID_SOCKET)CAsyncSocket:AttachHa

16、ndle(m_hSocket, this, FALSE);return AsyncSelect(lEvent);参数1表示Socket类型,缺省值是SOCK_STREAM。参数2表示希望监测的网络事件,缺省值同Create,指定了全部事件。参数3表示使用的协议,缺省是0。实际上,SOCK_STREAM类型的socket使用TCP协议,SOCK_DGRM的socket则使用UDP协议。参数4表示地址族(地址格式),缺省值是PF_INET(等同于AF_INET)。对于TCP/IP来说,协议族和地址族是同值的。在socket没有被创建之前,成员变量m_hSocket是一个无效的socket句柄。So

17、cket函数把协议族、socket类型、使用的协议等信息传递给WinSock API函数socket,创建一个socket。如果创建成功,则把它捆绑在this所指对象。(3)捆绑(Attatch)捆绑过程类似于其他Windows对象,将在模块线程状态的WinSock映射中添加一对新的映射:this所指对象和新创建的socket对象的映射。另外,如果本模块线程状态的“socket窗口”没有创建,则创建一个,该窗口在异步操作时用来接收WinSock的通知消息,窗口句柄保存到模块线程状态的m_hSocketWindow变量中。函数AsyncSelect将指定该窗口为网络事件消息的接收窗口。函数Att

18、achHandle的实现在此不列举了。(4)指定要监测的网络事件在捆绑完成之后,调用AsyncSelect指定新创建的socket将监测的网络事件。AsyncSelect实现如下:AsyncSelect(long lEvent)ASSERT(m_hSocket != INVALID_SOCKET);_AFX_SOCK_THREAD_STATE* pState = _afxSockThreadState;ASSERT(pState-m_hSocketWindow != NULL);return WSAAsyncSelect(m_hSocket, pState-m_hSocketWindow,WM

19、_SOCKET_NOTIFY, lEvent) != SOCKET_ERROR;函数参数lEvent表示希望监视的网络事件。_ afxSockThreadState得到的是当前的模块线程状态,m_ hSocketWindow是本模块在当前线程的“socket窗口”,指定监视m_hSocket的网络事件,如指定事件发生,给窗口m_hSocketWindow发送WM_SOCKET_NOTIFY消息。被指定的网络事件对应的网络I/O将是异步操作,是非阻塞操作。例如:指定FR_READ导致Receive是一个异步操作,如果不能立即读到数据,则返回一个错误WSAEWOULDBLOCK。在数据到达之后,W

20、inSock通知窗口m_hSocketWindow,导致OnReceive被调用。指定FR_WRITE导致Send是一个异步操作,即使数据没有送出也返回一个错误WSAEWOULDBLOCK。在数据可以发送之后,WinSock通知窗口m_hSocketWindow,导致OnSend被调用。指定FR_CONNECT导致Connect是一个异步操作,还没有连接上就返回错误信息WSAEWOULDBLOCK,在连接完成之后,WinSock通知窗口m_hSocketWindow,导致OnConnect被调用。对于其他网络事件,就不一一解释了。所以,使用CAsyncSocket时,如果使用Create缺省创

21、建socket,则所有网络I/O都是异步操作,进行有关网络I/O时则必须覆盖以下的相关函数:OnAccept、OnClose、OnConnect、OnOutOfBandData、OnReceive、OnSend。(5)Bind函数经过上述过程,socket创建完毕,下面,调用Bind函数给m_hSocket指定本地端口和IP地址。Bind的实现如下:Bind(UINT nSocketPort, LPCTSTR lpszSocketAddress)USES_CONVERSION;/使用WinSock的地址结构构造地址信息SOCKADDR_IN sockAddr;memset(&sockAddr,

22、0,sizeof(sockAddr);/得到地址参数的值LPSTR lpszAscii = T2A(LPTSTR)lpszSocketAddress);/指定是Internet地址类型sockAddr.sin_family = AF_INET;if (lpszAscii = NULL)/没有指定地址,则自动得到一个本地IP地址/把32比特的数据从主机字节序转换成网络字节序sockAddr.sin_addr.s_addr = htonl(INADDR_ANY);else/得到地址DWORD lResult = inet_addr(lpszAscii);if (lResult = INADDR_N

23、ONE)WSASetLastError(WSAEINVAL);sockAddr.sin_addr.s_addr = lResult;/如果端口为0,则WinSock分配一个端口(10245000)/把16比特的数据从主机字节序转换成网络字节序sockAddr.sin_port = htons(u_short)nSocketPort);/Bind调用WinSock API函数bindreturn Bind(SOCKADDR*)&sockAddr, sizeof(sockAddr);函数参数1指定了端口;参数2指定了一个包含本地地址的字符串,缺省是NULL。函数Bind首先使用结构SOCKADDR

24、_IN构造地址信息。该结构的域sin_family表示地址格式(TCP/IP同协议族),赋值为AF_INET(Internet地址格式);域sin_port表示端口,如果参数1为0,则WinSock分配一个端口给它,范围在1024和5000之间;域sin_addr是表示地址信息,它是一个联合体,其中s_addr表示如下形式的字符串,“28.56.22.8”。如果参数没有指定地址,则WinSock自动地得到本地IP地址(如果有几个网卡,则使用其中一个的地址)。(6)总结Create的过程首先,调用socket函数创建一个socket;然后把创建的socket对象映射到CAsyncSocket对象

25、(捆绑在一起),指定本socket要通知的网络事件,并创建一个“socket窗口”来接收网络事件消息,最后,指定socket的本地信息。下一步,是使用成员函数Connect连接远地主机,配置socket的远地信息。函数Connect类似于Bind,把指定的远地地址转换成SOCKADDR_IN对象表示的地址信息(包括网络字节序的转换),然后调用WinSock函数Connect连接远地主机,配置socket的远地端口和远地IP地址。3. 异步网络事件的处理 当网络事件发生时,“socket窗口”接收WM_SOCKET_NOTIFY消息,消息处理函数OnSocketNotify被调用。“socket

26、窗口”的定义和消息处理是MFC实现的,这里不作详细的讨论。OnSocketNotify回调CAsyncSocket的成员函数DoCallBack,DoCallBack调用事件处理函数,如OnRead、OnWrite等。摘录DoCallBack的一段代码如下:switch (WSAGETSELECTEVENT(lParam)case FD_READ:DWORD nBytes;/得到可以一次读取的字节数pSocket-IOCtl(FIONREAD, &nBytes);if (nBytes != 0)OnReceive(nErrorCode);break;case FD_WRITE:OnSend(n

27、ErrorCode);case FD_OOB:OnOutOfBandData(nErrorCode);case FD_ACCEPT:OnAccept(nErrorCode);case FD_CONNECT:OnConnect(nErrorCode);case FD_CLOSE:OnClose(nErrorCode);lParam是WM_SOCKET_NOFITY的消息参数,OnSocketNotify传递给函数DoCallBack,表示通知事件。函数IOCtl是CAsyncSocket的成员函数,用来对socket的I/O进行控制。这里的使用表示本次调用Receive函数至多可以读nBytes个字节。从上面的讨论可以看出,从创建socket到网络I/O,CAsyncSocket直接封装了低层的WinSock API,简化了WinSock编程,实现了一个异步操作的界面。如果希望某个操作是阻塞操作,则在调用Create时

copyright@ 2008-2022 冰豆网网站版权所有

经营许可证编号:鄂ICP备2022015515号-1