• 沒有找到結果。

知识扩展

在文檔中 第 2 章 扫描可控计算机 (頁 24-31)

2.4.1 澄清一些概念

Socket 在英文屮是“插座”的意思,它的设计者实际上是暗指电话插座。因为在 Socket 环境下编程很像是模拟打电话,Internet 的 IP 就是电话号码,要打电话,需要电话插座,在 程序中就是向系统申请一个 Socket,以后两台机器上的程序“交谈”都是通过这个 Socket 来 进行的。对程序员来说,也可以把 Socket 看成一个文件指针,只要向指针所指的文件读写数 据,就可以实现双向通信。利用 Socket 进行通信,有两种主要的方式。第一种是面向连接的 流方式。顾名思义,在这种方式下,两个通信的应用程序之间先要建立一种连接链路,其过 程好像在打电话。一台计算机(电话)要想和另一台计算机(电话)进行数据传输(通 话),须首先获得一条链路,只有确定了这条通路之后,数据(通话)才能被正确接收和发 送,这种方式对应的是 TCP(Transport Control Protocol)。第二种叫做无连接的数据报文方 式,这时两台计算机像是把数据放在一个信封里,通过网络寄给对方,信在传送的过程中有 可能会残缺不全,而且后发出的信也有可能会先收到,它对应的是 UDP(User Datagram Protocol)。

流方式的特点是通信可靠,对数据有校验和重发的机制,通常用来做数据文件的传输,

如 FTP、Telnet 等,数据报文方式由于取消了重发校验机制,能够达到较高的通信速率,可 用于对数据可靠性要求不高的通信,如实时的语音、图像转送和广播消息等。

在 ISO 的 OSI 网络七层协议中,WinSock 主要负责控制数据的输入和输出,也就是传输 层和网络层。WinSock 屏蔽了数据链路层和物理层,它的出现给 Windows 下的网络编程带来 了巨大的变化。

套接字有同步阻塞方式和异步非阻塞方式两种使用方法。同步和异步往往都是针对一个 函数来说的,“同步”就是函数直到其要执行的功能全部完成时才返回,而“异步”则是函数 仅仅做一些简单的工作,然后马上返回,所要实现的功能留给别的线程或者消息循环去完成。

阻塞方式的套接字简单、易用,但效率低。相比之下,异步套接字使用复杂,但效率很 高。本章实例使用的是阻塞方式。

2.4.2 WinSock 编程原理

Windows Sockets API 是 Windows 下的网络应用程序接口,用于网络通信。API 函数有 1.1 版和 2.0 版,称为 WinSockl 和 WinSock2,通过前缀 WSA 区分。2.0 版有良好的向后兼 容性,任何使用 1.1 版的源代码、二进制文件和应用程序都可以不加修改地在 2.0 规范下使 用。现在基本上都使用 2.0 版本进行开发,它主要是为了适应近年来网络技术的迅猛发展,

特别是多媒体网络技术迅速发展的需要,通过制定 Windows Sockets2 规范来提供一个与协议 无关的网络传输接口。

Windows Sockets 使用套接字进行编程,套接字编程是面向客户端/服务器模型而设计的,

因此系统中需要客户端和服务器两个不同类型的进程,根据连接类型的不同,对于面向连接的 TCP 服务和无连接的 UDP 服务,服务器分别采取不同的处理操作来对客户提供服务。

第 2 章 扫描可控计算机 39

对于面向连接的 TCP 服务,服务器需要等待客户端向其提出的建立连接的申请,一旦 接收到客户端的连接请求,服务器返回一个新的套接字描述符,通过该描述符调用数据传输 函数即可与客户端进行数据的收发。

对于无连接的 UDP 服务,服务器通常是面向事务处理的,服务器和客户端在传输数据 之前不需要进行连接的申请和建立,数据传输结束后,直接关闭套接字即可完成一次通信。

WinSock 中的主要函数如下:

 WinSock 的打开——WSAStartup()

 服务器建立套接字——socket()

 服务器绑定端口——bind()

 客户端提出连接申请——connect()

 服务器端接受客户端的连接请求——accept()

 数据的传送——send()和 recv()

 关闭套接字——closesocket()

 关闭 WinSock——WSACleanup()

这些函数的具体定义信息前面已经详细介绍了,这里就不再赘述。

2.4.3 MFC 网络编程

MFC 中有两个主要的网络编程类:CAsyncSocket 类和 CSocket 类。CAsyncSocket 类用 面向对象的方法封装了 WinSock,CSocket 类是 CAsyncSocket 类的子类。这两个类是 Visual C++环境中网络编程经常使用的两个类。

1.CAsyncSocket 类介绍

CAsyncSocket 类对 Windows Sockets API 函数进行了封装,它是从 CObject 类派生出来 的。该类在非常低的级别上封装 Windows Sockets API。CAsyncSocket 适合那些对网络通信 细节很了解,但希望利用回调的便利通知网络事件的程序员使用。如果想利用 Windows Sockets 方便地处理 MFC 应用程序中的多个网络协议,而又不想放弃灵活性,可以使用 CAsyncSocket,但是程序员必须自己处理阻塞,字节序的差异和 Unicode 多字节字符集

(MBCS)的转换。CAsyncSocket 类的重要函数如下:

 构造套接字对象——Create()

 接受套接字的连接请求——Accept()

 将本机地址绑定到套接字——Bind()

 建立连接——Connect()

 监听连接请求——Listen()

 发送数据——Send()和 SendTo()

 接受数据——Receive()和 ReceiveFrom()

 禁止发送接收数据——ShutDown()

 关闭套接字——Close()

这些函数中大部分在前面程序代码详解里已经详细介绍过,这里不再赘述。没有介绍的 函数如下:

 将本机地址绑定到套接字——Bind()

40 Visual C++网络编程技术

该函数将本机地址绑定到套接宇,在 Connect 或者 Listen 之前可以被调用。在能接受连 接请求前,监听的服务器套接字必须选择一个端口号并调用 Bind 将本地名分配给未命名的 Windows 套接字,使其知道自己的地址和端口号,如果函数调用成功则返回非 0 值,否则返 0 值。其函数原型如下:

BOOL Bind(UINT nSocketPort, LPCTSTR lpszSocketAddress = NULL);

参数 nSocketPort:要绑定的套接字端口号。

参数 lpszSocketAddress:要绑定的套接字网络地址。

BOOL Bind (const SOCKADDR* IpSockAddr, int nSockAddrLen);

参数 lpSockAddr:指向 SOCKADDR 结构的指针。

参数 nSockAddrLen:IpSockAddr 指针中地址的字节长度。

 发送数据——SendTo()

该函数与 Send()类似,也是通过数据报或者数据流套接字发送数据,如果函数成功调 用,则返回发送的字节总数,否则返回 SOCKET_ERROR。其函数原型如下:

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);

参数 lpBuf:要发送的数据缓冲区地址。

参数 nBufLen:lpBuf 缓冲区的字节长度。

参数 nHostPort:套接字应用程序的端口号。

参数 lpszHostAddress:被连接的套接字的网络地址。

参数 lpSockAddr:SOCKADDR 结构的指针。

参数 nSockAddrLen:指针所指向结构中的地址的字节长度的指针。

参数 nFlags:函数的调用标志,其取值为 MSG_DONTROUTE 和 MSG_OOB 的组合,

默认值为 0。

 接收数据——ReceiveFrom()

该 函 数 从 面 向 连 接 流 或 者 无 连 接 的 数 据 报 套 接 字 接 收 数 据 , 并 将 源 地 址 存 放 在 SOCKADDR 结构或者 rSockeLAddress 中。如果函数调用成功,则返回所读入的字节数,如果 连接被关闭,则返回 0,如果函数调用失败,则返回 SOCKET_ERROR。其函数原型如下:

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);

参数 lpBuf:接收数据的缓冲区。

参数 nBufLen:缓冲区的字节节长度。

参数 rSocketAddress:一个点分隔的字符串 IP 地址的 CString 对象的引用。

参数 rSocketPort:端口号的 UINT 类型的引用。

参数 lpSockAddr:返回源地址的 SOCKADDR 结构指针。

参数 lpSockAddrLen:lpSockAddr 的字节长度。

参数 nFlags:用来表示函数的实现,值为 MSG_OOB 和 MSG_PEEK 项的组合,通常默

第 2 章 扫描可控计算机 41

认为 0。

 禁止发送接收数据——ShutDown()

该函数用于禁止发送或者接收数据,如果调用成功返回非 0 值,否则返回 0 值。其函数 原型如下:

BOOL ShutDown (int nHow=sends) ;

参数 nHow:不允许的操作,可以取值为 SD_RECENE、SD_SEND 或 SD_BOTH,这三 个值的含义按照字面理解即可。

2.CSocket 类介绍

CSocket 类是 CAsyncSocket 类的派生类,它继承了 Windows Sockets API 封装函数。实 现了比 CAsyncSocket 类对 Windows Sockets 更高层的抽象。它与类 CSocketFile 和 CArchive 共同合作完成对发送数据、接收数据的管理,CSocket 类提供了对于同步操作 CArchive 对象 非常重要的阻塞功能,使程序员在管理数据的发送和接收的工作变得简单。

CSocket 类提供了阻塞的访问方式,这对于 CArchive 类的同步操作是必需的,其成员函 数如 Receive()、Send()、ReceiveFrom()、SendToO 和 Accept()不会像 WinSock 中的函数一样 返回错误,这些函数会等待直到操作完成。

CSocket 类的几个重要成员函数:

 创建套接字并将其同一个对象绑定起来——Create()

 确定一个阻塞是否正在执行——IsBlocking() 其函数原型如下:

BOOL IsBlocking();

如果套接字是阻塞状态返回非 0 值,否则返回 0 值。

 返回一个指向 CSocket 对象的指针——FromHandle()

给定一个 Socket 句柄,返回一个指向 CSocket 的指针,如果没有 CSocket 对象绑定到这 个句柄上,则该函数返回 NULL,并且不创建临时的对象。其函数原型如下:

static CSocket* PASCAL FromHandle( SOCKET hSocket );

参数 hSocket 为套接字句柄。

 把一个 Socket 句柄绑定到一个 CSocket 对象上——Attach()

该函数把一个 Socket 句柄绑定到一个 CSocket 对象上,SOCKET 句柄被保存到 CSocket 对象的成员变量 m_hSocket 中。如果调用成功返回非 0 值,否则返回 0 值。其函数原型如下:

BOOL Attach(SOCKET hSocket) ; 参数 hSocket 为一个套接字的句柄。

 取消一个正在进行中的阻塞调用——CancelBlockingCall()

调用该函数,原始的阻塞调用会立即终止并返回 WSAEINTR 错误。如果是阻塞的 Connect()操作,Windows 套接字可以实现立即终止这个阻塞的调用,但是不可能立即释放这 个套接字的资源,必须等到连接完成或者超时后资源才会被释放。其函数原型如下:

void CancelBlockingCall();

 过滤和响应 Windows 特定的消息——OnMessagePending()

该函数是一个重载函数,用来过滤和响应 Windows 特定的消息,是一个高级的可重载 函数。如果 Windows 消息成功处理了则返回非 0 值,否则返回 0 值。其函数原型如下:

virtual BOOL OnMessagePending() ;

42 Visual C++网络编程技术

2.4.4 WinInet 编程技术

一个 Internet 客户端程序的目的是通过 Internet 协议如 HTTP、FTP 等来存取网络数据源

(服务器)的信息。客户端程序可以访问服务器获得如天气预报、股票价格、重要新闻数 据,甚至是与服务器交换信息。Internet 客户端程序可以通过外部网络(Internet)或内部网 络(一般为 Intranet)访问服务器。

为了开发 Internet 客户端程序。MFC 类库提供了专门的 Win32 Internet 扩展接口,也就

为了开发 Internet 客户端程序。MFC 类库提供了专门的 Win32 Internet 扩展接口,也就

在文檔中 第 2 章 扫描可控计算机 (頁 24-31)

相關文件