10网络编程.doc_第1页
10网络编程.doc_第2页
10网络编程.doc_第3页
10网络编程.doc_第4页
10网络编程.doc_第5页
已阅读5页,还剩67页未读 继续免费阅读

下载本文档

版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领

文档简介

第十章 网络编程作为本书的读者来说,大概不可能不知道网络吧。网络,尤其是Internet,发展速度超过了任何人任何大胆的预料。一个软件实现网络上的相关应用显然是必不可少的,因此,我们绝对有必要学习一下网络编程。在网络环境下如果需要实现一个应用程序与另一个应用程序的信息交换,可以有多种方式。在Windows 操作系统还没有出现之前,Internet 实际上就已经出现了,最早的站点就是一些 UNIX 机,当时一组称为Berkeley 的套接字成为Internet 上各UNIX站点实现TCP/IP通讯的标准。Windows Socket(Winsock) API就是基于BSD版本套接字发展而来的,我们可以利用这些特定接口开发网络应用程序。本章就首先介绍各种套接字,然后介绍有关MFC Winsock类及其编程内容,再介绍消息API及其应用,最后给出一个Internet应用实例。本章包括以下内容:l 套接字l MFC Winsock类l WinSock编程实例l 消息API(MAPI)l 实现Internet应用101 套接字Winsock 规范定义了一个动态连接库的接口,通常此DLL名为WINSOCK.DLL或 WSOCK32.DLL。开发商可以自行编写代码实现接口,在应用程序中可以调用这些接口函数。接口函数的使用与不同的开发商没有关系,尽管不同开发商实现过程有所不同 ,但函数调用时,函数名、参数设置以及返回值类型都由接口确定,是完全一致的。例如在Windows 95和Windows NT中套接字动态连接库文件就有所不同,但一个32位的Winsock 应用程序可以调用相应的动态连接库DLL中的Winsock函数,从而既可以在Windows 95环境下执行,又可以不加任何修改地在Windows NT环境中执行。Winsock在开发用于信息交换的应用程序时是非常有用的,我们可以利用套接字传递数据包,以达到通信的目的。套接字本身可以理解为通信端点,它所提供的数据收发机制可以方便地用于网络通信。套接字可以有两种方式,即数据报套接字和流式套接字。l 数据报套接字(Datagram Sockets)这种套接字所提供的通信方式并不可靠,即不能保证所传送的数据包一定合法,也不能保证数据包接收顺序与发送顺序完全一致,甚至数据包是否能够安全传送到目的端都不能保证。由于系统提供的数据报套接字功能有限,因此我们必须在编程时充分考虑各种可能出现的异常情况,例如,如何控制数据包发送和接收顺序,如何保证数据包不被重复发送,另外如何数据包没有发送应该如何处理,这些工作就需要我们自已完成了。更重要的是,在编写复杂的网络应用程序时,还必须考虑数据报的可靠性,因为如果只依赖于系统提供的支持,数据报套接字的可靠性是极差的,如果一个大型的网络通信应用程序经常出现崩溃,那么带来的损失将是不可估量的。数据报套接字的主要用途是以广播方式发送数据,如记录型数据等。l 流式套接字(Stream Sockets)流式套接字的含义是数据发送后,需要按顺序而且不重复地由目的端接收。这是一种可靠的数据传输方式,对于单个的数据报,以及完整的数据包,流式套接字都可以提供这种面向连接的数据传输。流式套接字的使用要广泛得多,特别是对于大量的数据发送,或要求数据接收顺序时,都需要使用流式套接字。在使用套接字进行网络编程时,有一个非常重要的概念即套接字的端口。在Internet 上每一个站点都有相应的数字化地址,我们称之为IP地址。形式一般为NUM1.NUM2.NUM3.NUM4即用小数点分隔的4个数字,如198.53.145.3。如果通过网络,有一个请求发到198.53.145.3对应的主机上,此请求将附带一个端口号信息,端口号是一个整数值,它标识了所请求的应用程序。必须注意,有一些端口是系统为标准应用预留的,如端口80主要用于Web 服务器监听客户应用程序如Netscape Navigator 浏览器发出的Web 文档请求。有一些应用程序在设计信息发送时并不是基于连接的,但这种发送方式是没有保障的,即我们不能确定数据确实发送出去了,也不能确定数据接收方的身份。不过对于一些广播性质的信息发送,这种方式是比较适合的。如典型的时间服务,时间服务器将当前时间发送给其所在范围内的每一台机器,至于对方是否接收,或对方接收到时间信息后作何响应,时间服务器都不关心。我们在应用程序中所使用的套接字一般是基于连接的,即两个应用程序各自生成一个套接字,这样在两个对应的套接字之间就建立了一个连接,两个应用程序就是连接的两端。如果已经建立了连接,意味着两端的应用程序可以在此通路上发送和接收信息了。需要注意的是,在两个端点间建立连接会带来一定的时间延迟,一般来讲,延迟影响并不大,但对于实时性要求比较高的信息传送而言,就不太适合了,因此也可以考虑无连接的通信方式。102 MFC Winsock类起初,在Visual C+中用套接字编程就意味着需要调用套接字动态连接库DLL 中的相关API接口函数,即所有的功能的以函数为单位实现的。提出面向对象概念之后,为了方便程序的编写和维护,需要自己建立套接字类,利用套接字类来封装有关套接字的特点和方法。不过从Visual C+ 2.1 版本以后,系统就已经提供了两个新的类来专门管理套接字,即CAsyncSocket 及其子类CSocket。在此之后使用套接字就有了更为抽象的接口,而且类似于套接字对象的初始化和撤销等工作是我们比较容易忽略的,使用套接字对象就可以帮助我们自动完成。我们知道,Windows 应用程序是异步实现的,在同一时间内可以同时完成多项任务,在老版本的Windows操作系统环境下,如果一个应用程序陷入了循环,或者由于某种原因处于挂起状态,那么其后果可能造成整个系统都陷入循环或挂起状态。显然我们需要尽量克服这种现象发生,但是对于一个套接字发出的请求,通常是要求通过TCP/IP连接从而获得在Internet上另一个站点的信息, 信息的接收过程往往需要很长的时间完成,此时我们认为用来发送或接收信息的套接字函数就被阻塞了。对于这个问题,可以有3种解决方法:l 创建线程创建一个线程,并将可能出现阻塞的套接字函数在此线程中完成,如果函数确实阻塞了,那么只会导致它所在的线程处于等待状态,而不会影响到整个应用程序。这种方法是在线程思想得到发展之后出现的,最近才得到广泛使用。l 轮询套接字状态在编写程序时,需要发出请求时,我们可以设置一个函数,其功能是一旦请求发送出去,就返回相应的确认信息,同时我们可以设置另一个函数,它的功能是与第一个函数相对应的,即不断地通过轮询套接字,来查看请求是否发送出去。由于我们需要通过轮询的方式查看套接字状态,因此这种方法的效率是相当低的,需要浪费大量的处理时间进行检查,而不是完成具体的工作。l 发送消息最后一种方法同样是通过设计函数实现对套接字状态的检查和管理,首先设置一个函数,其功能是请求一旦发送,就返回相应的确认信息,另外在请求结束时向Windows 提交一个窗口消息。目前,这种方法是最常用的,我们完全可以利用CAsyncSocket 类完成这一工作。例如,要向Internet上另一个站点发送一条串信息,这个过程要通过已建立连接的套接字完成,那么我们可以调用套接字的Send()函数实现, Send() 函数本身并不一定能够发送具体数据,如果发现套接字并没有准备好,或者处于等待状态,Send() 函数将立刻返回,如果套接字确实已经准备好,就会向套接字发送相关的消息,而套接字就会捕获这条消息,从而获得相就的数据,即前面所要求发送的串信息,这个过程虽然简单,但可以充分体现异步Winsock 应用程序的原理。 虽然Winsock的原理很好理解,而且利用AppWizard我们就可以要求系统为我们实现一个最基本的Winsock应用程序。但在实际应用中,使用Winsock编写网络应用程序,实现较为复杂的通信功能则是一项比较困难的工作。在具体实现Winsock应用程序前,我们先来分析CAsyncSocket和CSocket类的属性和方法。1021 CAsyncSocketCAsyncSocket 类的工作实际上就是封装了对异步Winsock 调用的处理,其定义如下:class CAsyncSocket : public CObjectDECLARE_DYNAMIC(CAsyncSocket);private:CAsyncSocket(const CAsyncSocket& rSrc); / no implementationvoid operator=(const CAsyncSocket& rSrc); / no implementation/ Constructionpublic:CAsyncSocket();BOOL Create(UINT nSocketPort = 0, int nSocketType=SOCK_STREAM,long lEvent = FD_READ | FD_WRITE | FD_OOB | FD_ACCEPT | FD_CONNECT | FD_CLOSE,LPCTSTR lpszSocketAddress = NULL);/ Attributespublic:SOCKET m_hSocket;operator SOCKET() const;BOOL Attach(SOCKET hSocket, long lEvent =FD_READ | FD_WRITE | FD_OOB | FD_ACCEPT | FD_CONNECT | FD_CLOSE);SOCKET Detach();BOOL GetPeerName(CString& rPeerAddress, UINT& rPeerPort);BOOL GetPeerName(SOCKADDR* lpSockAddr, int* lpSockAddrLen);BOOL GetSockName(CString& rSocketAddress, UINT& rSocketPort);BOOL GetSockName(SOCKADDR* lpSockAddr, int* lpSockAddrLen);BOOL SetSockOpt(int nOptionName, const void* lpOptionValue,int nOptionLen, int nLevel = SOL_SOCKET);BOOL GetSockOpt(int nOptionName, void* lpOptionValue,int* lpOptionLen, int nLevel = SOL_SOCKET);static CAsyncSocket* PASCAL FromHandle(SOCKET hSocket);static int PASCAL GetLastError();/ Operationspublic:virtual BOOL Accept(CAsyncSocket& rConnectedSocket,SOCKADDR* lpSockAddr = NULL, int* lpSockAddrLen = NULL);BOOL Bind(UINT nSocketPort, LPCTSTR lpszSocketAddress = NULL);BOOL Bind (const SOCKADDR* lpSockAddr, int nSockAddrLen);virtual void Close();BOOL Connect(LPCTSTR lpszHostAddress, UINT nHostPort);BOOL Connect(const SOCKADDR* lpSockAddr, int nSockAddrLen);BOOL IOCtl(long lCommand, DWORD* lpArgument);BOOL Listen(int nConnectionBacklog=5);virtual int Receive(void* lpBuf, int nBufLen, int nFlags = 0);int ReceiveFrom(void* lpBuf, int nBufLen,CString& rSocketAddress, UINT& rSocketPort, int nFlags = 0);int ReceiveFrom(void* lpBuf, int nBufLen,SOCKADDR* lpSockAddr, int* lpSockAddrLen, int nFlags = 0);enum receives = 0, sends = 1, both = 2 ;BOOL ShutDown(int nHow = sends);virtual int Send(const void* lpBuf, int nBufLen, int nFlags = 0);int SendTo(const void* lpBuf, int nBufLen,UINT nHostPort, LPCTSTR lpszHostAddress = NULL, int nFlags = 0);int SendTo(const void* lpBuf, int nBufLen,const SOCKADDR* lpSockAddr, int nSockAddrLen, int nFlags = 0);BOOL AsyncSelect(long lEvent =FD_READ | FD_WRITE | FD_OOB | FD_ACCEPT | FD_CONNECT | FD_CLOSE);/ Overridable callbacksprotected:virtual void OnReceive(int nErrorCode);virtual void OnSend(int nErrorCode);virtual void OnOutOfBandData(int nErrorCode);virtual void OnAccept(int nErrorCode);virtual void OnConnect(int nErrorCode);virtual void OnClose(int nErrorCode);/ Implementationpublic:virtual CAsyncSocket();static CAsyncSocket* PASCAL LookupHandle(SOCKET hSocket, BOOL bDead = FALSE);static void PASCAL AttachHandle(SOCKET hSocket, CAsyncSocket* pSocket, BOOL bDead = FALSE);static void PASCAL DetachHandle(SOCKET hSocket, BOOL bDead = FALSE);static void PASCAL KillSocket(SOCKET hSocket, CAsyncSocket* pSocket);static void PASCAL DoCallBack(WPARAM wParam, LPARAM lParam);BOOL Socket(int nSocketType=SOCK_STREAM, long lEvent =FD_READ | FD_WRITE | FD_OOB | FD_ACCEPT | FD_CONNECT | FD_CLOSE,int nProtocolType = 0, int nAddressFormat = PF_INET);#ifdef _DEBUGvirtual void AssertValid() const;virtual void Dump(CDumpContext& dc) const;#endifprotected:friend class CSocketWnd;virtual BOOL ConnectHelper(const SOCKADDR* lpSockAddr, int nSockAddrLen);virtual int ReceiveFromHelper(void* lpBuf, int nBufLen,SOCKADDR* lpSockAddr, int* lpSockAddrLen, int nFlags);virtual int SendToHelper(const void* lpBuf, int nBufLen,const SOCKADDR* lpSockAddr, int nSockAddrLen, int nFlags);CAsyncSocket类有一组非常有用的成员函数:l virtual BOOL Accept(CAsyncSocket& rConnectedSocket,SOCKADDR* lpSockAddr = NULL, int* lpSockAddrLen = NULL);Accept 函数在一个监听套接字端处理连接请求,即处理一个可能建立的连接,一旦建立连接,就写入相应的套接字地址信息。这里参数rConnectedSocket 为监听套接字,lpSockAddr 即为套接字地址,其初始值为空,一旦连接建立,则将套接字实际地址通过lpSockAddr 返回,另外lpSockAddrLen 为套接字地址的长度。l BOOL AsyncSelect(long lEvent =FD_READ | FD_WRITE | FD_OOB | FD_ACCEPT | FD_CONNECT | FD_CLOSE);AsyncSelect函数的作用是在套接字准备好之后,发出请求要求发送消息,消息的类型如表10.1所示:表10.1 消息类型类型值FD_READ 0x01FD_WRITE0x02FD_OOB0x04FD_ACCEPT 0x08FD_CONNECT 0x10FD_CLOSE0x20l BOOL Attach(SOCKET hSocket, long lEvent =FD_READ | FD_WRITE | FD_OOB | FD_ACCEPT | FD_CONNECT | FD_CLOSE);Attach函数的作用是将一个套接字句柄连接到一个CAsyncSocket 对象实例上,即建立二者的关联,这样就可以实现与另一台计算机的连接了。这里hSocket即为套接字句柄,其相关的消息类型与AsyncSelect相同。l BOOL Bind(UINT nSocketPort, LPCTSTR lpszSocketAddress = NULL);l BOOL Bind (const SOCKADDR* lpSockAddr, int nSockAddrLen);Bind函数可以建立一个地址与套接字的关联,一般我们称之为绑定。可以有两种调用形式,其一是提供套接字端口和地址,而且地址是以串作为参数类型的。这种方法比较常用。另一种是提供SOCKADDR结构类型的地址参数。l virtual void Close();Close函数就是关闭套接字。l BOOL Connect(LPCTSTR lpszHostAddress, UINT nHostPort);l BOOL Connect(const SOCKADDR* lpSockAddr, int nSockAddrLen);Connect函数可以建立套接字与远程地址和端口的连接,这个函数在WinSock应用程序中必须出现。调用方式也有两种:其一是提供套接字端口和地址,而且地址是以串作为参数类型的。这种方法比较常用。另一种是提供SOCKADDR结构类型的地址参数。l BOOL Create(UINT nSocketPort = 0, int nSocketType=SOCK_STREAM,long lEvent = FD_READ | FD_WRITE | FD_OOB | FD_ACCEPT | FD_CONNECT | FD_CLOSE,LPCTSTR lpszSocketAddress = NULL);在调用CAsyncSocket 构造函数之后,需要由Create 函数实现具体的初始化工作。l SOCKET Detach();Detach函数的作用是将上一次建立关联的套接字句柄断开,并返回此套接字句柄。 l static CAsyncSocket* PASCAL FromHandle(SOCKET hSocket);FromHandle函数参数将指定一个套接字句柄,其作用是返回与此套接字关联的CAsyncSocket对象指针l static int PASCAL GetLastError();GetLastError函数将返回套接字错误代码,在实际应用中需要在操作失败后调用此函数以查找错误原因。l BOOL GetPeerName(CString& rPeerAddress, UINT& rPeerPort);l BOOL GetPeerName(SOCKADDR* lpSockAddr, int* lpSockAddrLen);指定套接字,调用GetPeerName 函数就可以找到与之对应的远程套接字的IP地址和端口号。远程套接字的IP地址和端口号可以分别由参数rPeerAddress 和rPeerPort返回,也可以由指向SOCKADDR结构的参数lpSockAddr返回。l BOOL GetSockName(CString& rSocketAddress, UINT& rSocketPort);l BOOL GetSockName(SOCKADDR* lpSockAddr, int* lpSockAddrLen);GetSockName与GetPeerName函数的作用类似,但更为简单一些,即指定套接字,则返回其IP地址和端口号。IP地址和端口号可以分别由参数rPeerAddress 和rPeerPort返回,也可以由指向SOCKADDR结构的参数lpSockAddr返回。l BOOL GetSockOpt(int nOptionName, void* lpOptionValue, int* lpOptionLen, int nLevel = SOL_SOCKET);l BOOL SetSockOpt(int nOptionName, const void* lpOptionValue, int nOptionLen, int nLevel = SOL_SOCKET);GetSockOpt函数和SetSockOpt函数的作用是对套接字状态的存取,对于SetSockOpt而言。当前设置的套接字状态由参数lpOptionValue确定。而对于GetSockOpt函数,可以由参数lpOptionValue得到套接字状态。l BOOL IOCtl(long lCommand, DWORD* lpArgument);IOCtl的作用是设置套接字的模式,一般来说,主要有两种模式,即阻塞和非阻塞。l BOOL Listen(int nConnectionBacklog=5);Listen函数的作用为监听,即构造一个套接字以查看是否需要建立连接。l virtual void OnAccept(int nErrorCode);OnAccept是一个需重载的回调函数,当一个套接字可能需要与另一端建立连接时,可以调用此函数处理相应的消息。l virtual void OnClose(int nErrorCode);OnClose是一个需重载的回调函数,当一个套接字关闭时,可以调用此函数处理相应的消息。l virtual void OnConnect(int nErrorCode);OnConnect是一个需重载的回调函数,当一个套接字成功建立连接,或者连接失败时,可以调用此函数处理相应的消息。l virtual void OnOutOfBandData(int nErrorCode);OnOutOfBandData是一个需重载的回调函数,如果一些非常急需的数据已经准备好,就会发出相应的消息,这一类消息的处理需要由OnOutOfBandData完成。l virtual void OnReceive(int nErrorCode);OnReceive是一个需重载的回调函数,当一个套接字已经准备好数据,并可以由函数Receive()接收时,可以调用此函数处理相应的消息。l virtual void OnSend(int nErrorCode);OnSend是一个需重载的回调函数,当一个套接字已经准备好数据,并可以由函数Send()发送时,可以调用此函数处理相应的消息。l virtual int Receive(void* lpBuf, int nBufLen, int nFlags = 0);Receive函数的作用是获得由远程套接字发送的数据,这里所指的套接字即与当前套接字建立连接的远程端套接字。l int ReceiveFrom(void* lpBuf, int nBufLen,CString& rSocketAddress, UINT& rSocketPort, int nFlags = 0);l int ReceiveFrom(void* lpBuf, int nBufLen,SOCKADDR* lpSockAddr, int* lpSockAddrLen, int nFlags = 0);ReceiveFrom函数将由无连接的远程套接字读取数据报信息。l virtual int Send(const void* lpBuf, int nBufLen, int nFlags = 0);Send函数的作用是向远程套接字发送数据,这里所指的套接字即与当前套接字建立连接的远程端套接字。l int SendTo(const void* lpBuf, int nBufLen,UINT nHostPort, LPCTSTR lpszHostAddress = NULL, int nFlags = 0);l int SendTo(const void* lpBuf, int nBufLen,const SOCKADDR* lpSockAddr, int nSockAddrLen, int nFlags = 0);SendTo函数与ReceiveFrom函数的作用是对应的,将向无连接的远程套接字发送数据报信息。l BOOL ShutDown(int nHow = sends);调用ShutDown函数并不影响套接字的打开状态,它不是对套接字的关闭,但可以控制是否能够继续发送或接收数据,即是否允许继续调用Send()和Receive()函数。因此可以将ShutDown函数的功能理解为对接收和发送功能的关闭,具体的控制模式有3种,如表10.2所示:表10.2 控制模式关闭模式值含义receives0关闭接收功能sends1关闭发送功能both2既关闭接收功能,又关闭发送功能套接字地址信息可以用MFC提供的sockaddr结构保存,其定义为:struct sockaddr u_short sa_family; /* address family */ char sa_data14; /* up to 14 bytes of direct address */typedef struct sockaddr SOCKADDR;在sockaddr结构中sa_family对应协议类别,sa_data为具体的地址信息,这是一个长度为14的字符数组。在使用CAsyncSocket 类时,我们需要自己完成套接字地址信息的设置,即为SOCKADDR 结构变量赋值,这项工作虽然不复杂,但我们完全可以用另一种方法来避免。即利用CAsyncSocket 类的派生类CSocket ,我们就不需要为地址的设置费心了。1022 CSocketCSocket类是由CAsyncSocket 类派生而来的,因此很多功能函数都继承于其父类。其定义如下:class CSocket : public CAsyncSocketDECLARE_DYNAMIC(CSocket);private:CSocket(const CSocket& rSrc); / no implementationvoid operator=(const CSocket& rSrc); / no implementation/ Constructionpublic:CSocket();BOOL Create(UINT nSocketPort = 0, int nSocketType=SOCK_STREAM,LPCTSTR lpszSocketAddress = NULL);/ Attributespublic:BOOL IsBlocking();static CSocket* PASCAL FromHandle(SOCKET hSocket);BOOL Attach(SOCKET hSocket);/ Operationspublic:void CancelBlockingCall();/ Overridable callbacksprotected:virtual BOOL OnMessagePending();/ Implementationpublic:int m_nTimeOut;virtual CSocket();static int PASCAL ProcessAuxQueue();virtual BOOL Accept(CAsyncSocket& rConnectedSocket,SOCKADDR* lpSockAddr = NULL, int* lpSockAddrLen = NULL);virtual void Close();virtual int Receive(void* lpBuf, int nBufLen, int nFlags = 0);virtual int Send(const void* lpBuf, int nBufLen, int nFlags = 0);int SendChunk(const void* lpBuf, int nBufLen, int nFlags);protected:friend class CSocketWnd;BOOL* m_pbBlocking;int m_nConnectError;virtual BOOL ConnectHelper(const SOCKADDR* lpSockAddr, int nSockAddrLen);virtual int ReceiveFromHelper(void* lpBuf, int nBufLen,SOCKADDR* lpSockAddr, int* lpSockAddrLen, int nFlags);virtual int SendToHelper(const void* lpBuf, int nBufLen,const SOCKADDR* lpSockAddr, int nSockAddrLen, int nFlags);static void PASCAL AuxQueueAdd(UINT message, WPARAM wParam, LPARAM lParam);virtual BOOL PumpMessages(UINT uStopFlag);#ifdef _DEBUGvirtual void AssertValid() const;virtual void Dump(CDumpContext& dc) const;#endif;这里需要特别说明一些常用的函数:l BOOL Attach(SOCKET hSocket);Attach函数将一个套接字句柄连接到一个CAsyncSocket 对象实例上,从而可以建立与另一端的连接。l BOOL Create(UINT nSocketPort = 0, int nSocketType=SOCK_STREAM, LPCTSTR lpszSocketAddress = NULL);Create函数是在CSocket对象构造之后调用的,它将完成具体的初始化工作。l static CSocket* PASCAL FromHandle(SOCKET hSocket);FromHandle函数参数首先指定一个套接字句柄,其作用是返回与此套接字关联的CSocket对象指针l BOOL IsBlocking();IsBlocking 的返回值是一个布尔值,作用为判断在当前时刻,套接字是否处于阻塞状态,即是否在等待某种事件发生。l void CancelBlockingCall();如果套接字处于阻塞状态,那么可以调用CancelBlockingCall函数将所有套接字余下的请求删除。l virtual BOOL OnMessagePending();OnMessagePending是一个需要重载的虚函数,当套接字阻塞时,OnMessagePending可以处理应用中的其它消息。103 WinSock编程实例为了进一步地理解WinSock编程方法,这里介绍一个WinSock应用程序实例。在实例中,我们并没有直接应用MFC 提供的CSocket类,这是因为考虑到对于类而言,其成员函数调用必然是完全阻塞方式的,因此只能用于人工线程中。基于这种思想,我们可以在CObject类基础上派生一个套接字类,其使用方式为阻塞方式,虽然增加了使用的条件,但可以保证其正常工作,而不会出现不加控制的使用CSocket对象而带来的冲突现象。下面将具体介绍有关的套接字类的定义,由于新创建的套接字功能主要通过调用CSocket的相关操作实现,因此在介绍过程中,读者可以更深入地理解MFC套接字类的功能。1031 套接字类CBlockingSocket首先我们需要定义此套接字类,在类中设置了一个属性变量:SOCKET m_hSocket; m_hSocket 表示套接字的句柄。另外还构造了一组方法,其功能与CSocket类是对应的。l 构造CBlockingSocket() m_hSocket = NULL; 这是一个内联函数,将套接字句柄初始化为空。l 关闭void Cleanup();Cleanup函数的作用是关闭套接字,即重置套接字句柄,具体实现为:void CBlockingSocket:Cleanup()if(m_hSocket = NULL) return;VERIFY(closesocket(m_hSocket) != SOCKET_ERROR);m_hSocket = NULL;由于我们在异常捕获即catch块中调用此函数,因此在实现时并不抛出异常,以免造成异常循环。l 创建void Create(int nType = SOCK_STREAM); 创建套接字即要求创建相应的连接,缺省类型为面向连接的流,具体实现为: void CBlockingSocket:Create(int nType)ASSERT(m_hSocket = NULL);if(m_hSocket = socket(AF_INET, nType, 0) = INVALID_SOCKET) throw new CBlockingSocketException(创建套接字);l 关闭连接void Close(); 关闭连接与关闭套接字功能相似,所不同的是在关闭连接操作中,可以发出异常,当然,如果正常关闭是不会产生异常的。void CBlockingSocket:Close()ASSERT(m_hSocket != NULL);if(closesocket(m_hSocket) = SOCKET_ERROR) throw new CBlockingSocketException(关闭连接);m_hSocket = NULL;l 绑定void Bind(LPCSOCKADDR psa); Bind函数的作用是绑定连接,若绑定失败,需要发出相应的异常。void CBlockingSocket:Bind(LPCSOCKADDR psa)ASSERT(m_hSocket != NULL);if(bind(m_hSocket, psa, sizeof(SOCKADDR) = SOCKET_ERROR) throw new CBlockingSocketException(绑定连接);l 监听void Listen();Listen函数完成监听连接的任务,我们在实现时要求最多有10个连接请求排队,这在一般的应用中是完全足够的。void CBlockingSocket:Listen()ASSERT(m_hSocket != NULL);if(listen(m_hSocket, 10) = SOCKET_ERROR) throw new CBlockingSocketException(Listen);l 建立连接void Connect(LPCSOCKADDR psa);连接的实际建立可以由Connect实现,同样地,缺省的建立方式为面向连接的流。void CBlockingSocket:Create(int nType )ASSERT(m_hSocket = NULL);if(m_hSocket = socket(AF_INET, nType, 0) = INVALID_SOCKET) throw new CBlockingSocketException(创建套接字);l 接收BOOL Accept (CBlockingSocket& s, LPSOCKADDR psa); Accept 的参数s表示当前的套接字,psa指向新连接方的地址,其作用就是接收由psa指定的连接方发出的信息。如果接收失败,但监听被取消,则不发异常。BOOL CBlockingSocket:Accept(CBlockingSocket& sConnect, LPSOCKADDR psa)ASSERT(m_hSocket != NULL);ASSERT(sConnect.m_hSocket = NULL);int nLengthAddr = sizeof(SOCKADDR);sConnect.m_hSocket = accept(m_hSocket, psa, &nLengthAddr);if(sConnect = INVALID_SOCKET) if(WSAGetLastError() != WSAEINTR) throw new CBlockingSocketException(接受连接);return FALSE;return TRUE;l 发送消息int Send(const char* pch, const int nSize, const int nSecs); Send函数的作用是将数据块按一个消息发送,参数pch即为发送的消息,nSize为消息长度,nSecs可以限制操作时间。如果客户方取消读操作,则返回值将小于nSize指定的消息长度。int CBlockingSocket:Send(const char* pch, const int nSize, const int nSecs)ASSERT(m_hSocket != NULL);FD_SET fd = 1, m_hSocket;TIMEVAL tv = nSecs, 0;if(select(0, NULL, &fd, NULL, &tv) = 0) throw new CBlockingSocketException(发送超时);int nBytesSent;if(nBytesSent = send(m_hSocket, pch, nSize, 0) = SOCKET_ERROR) throw new CBlockingSo

温馨提示

  • 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
  • 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
  • 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
  • 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
  • 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
  • 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
  • 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。

评论

0/150

提交评论