1.WinSock API
Windows Sockets是在Windows环境下使用的一套网络编程规范,常常简称为Winsock。
WinSock主要是基于socket来开发基于TCP/IP模型中各层的服务器端与客户端程序。可使用RAW SOCKET API(原始套接字)开发低层协议的程序,当然这需要了解协议的报文格式。
1.1 Windows Sockets API
参考《WinSock编程基础》中的Windows Sockets规范。
1.2 Winsock API函数的分类
在Winsock规范中把Winsock API函数集分为与BSD Socket(用在Unix中)相兼容的基本函数、网络数据信息检索函数和Windows专用扩展函数三类。
参考《WinSock编程基础》中的套接字API概览。
2.CAsyncSocket/CSocket
2.1 MFC对Winsock API的封装(Windows Sockets: Using Class CAsyncSocket)
CAsyncSocket对象表示一个Windows Socket–一个网络通信的端点。CAsyncSocket类封装了Windows套接字API,对想使用与MFC连接的Windows套接字的程序员提供了一个面向对象的抽象化概念。它是基于暗窗口的WSAAsyncSelcet异步I/O模型。所谓“暗窗口”是指委托处理网络事件消息的窗口,即“Socket Notification Sink”。由于暗窗口只是用来在后台做消息处理,故其风格、尺寸都为零,隐而不显。
应用此类,需处理块、字节顺序(大小端)以及Unicode和多字符集(MBCS)的问题。如果想要一个更方便的处理这些问题的接口,参阅CSocket类。
与CAsyncsocket对象相比,CSocket对象代表了Windows Sockets API的更高一级的抽象化。CSocket与类CSocketFile和CArchive一起来管理对数据的发送和接收。
CAsyncSocket & CSocket
2.2 CAsyncsocket
声明一个CAsyncSocket对象后,需调用Create成员函数进行具体套接字的创建:CAsyncSocket::CreateàCAsyncSocket::SocketàCAsyncSocket::AttachHandle/CAsyncSocket::AsyncSelect。
CAsyncSocket::AttachHandle完成MFC对象CAsyncSocket与SOCKET内核对象(句柄)的附加委托。
使用CAsyncSocket的线程在创建第一个对象时,_AFX_SOCK_THREAD_STATE::m_pmapSocketHandle->IsEmpty() == TRUE,将创建一个暗窗口(CSocketWnd对象)并记录在线程套接字状态管理模块的m_hSocketWindow字段中(_AFX_SOCK_THREAD_STATE::m_hSocketWindow)。
在CAsyncSocket::AsyncSelect中,调用WSAAsyncSelect(m_hSocket, pState->m_hSocketWindow, WM_SOCKET_NOTIFY, lEvent)。
以后该线程的所有CAsyncSocket对象的网络事件均以消息WM_SOCKET_NOTIFY发送给_AFX_SOCK_THREAD_STATE::m_hSocketWindow。
暗窗口CSocketWnd接收WM_SOCKET_NOTIFY消息,消息处理函数OnSocketNotify被调用—ON_MESSAGE(WM_SOCKET_NOTIFY, OnSocketNotify)。
WM_SOCKET_NOTIFY消息的wParam参数为对应发生该事件的套接字句柄,lParam参数的高字位(一般用WSAGETSELECTERROR宏取得HIWORD)包含出错码,lParam参数的低字位(一般用WSAGETSELECTEVENT宏取得LOWORD)则标识了网络事件代码(FD_XXX)。一般先检查高位,再检查低位进行网络事件的处理。
OnSocketNotify函数回调CAsyncSocket::DoCallBack,DoCallBack调用事件处理函数,如OnRead、OnWrite等。OnRead、OnWrite在CAsyncSocket中为空,用户一般需要重载虚函数OnRead、OnWrite,调用Receive、Send进行接发处理。Receive、Send只是对recv和send的简单调用,它们都是可重载的虚函数。
2.3 CSocket
CSocket类是从CAsyncsocket派生的一个同步阻塞Socket的封装类,CSocket类的诸如Send(),Receive()在收到WSAEWOULDBLOCK“错误”时,不是如CAsyncSocket那样立即返回,而是进入PumpMessages()消息循环,PumpMessages()直到有指定事件(uStopFlag == FD_*)发生才返回,在2秒(CSocket:: m_nTimeOut) 期间,如果线程取到了任一窗口的WM_PAINT消息,则刷新相应窗口。这样异步的CAsyncSocket,到了派生类CSocket,就变成同步的了。
2.4 多线程环境下的CAsyncSocket/CSocket
CAsyncSocket/CSocket不是线程安全的,典型的在一个线程里调用了CSocket的成员函数,之后启动一个线程,在另一线程中对CSocket对象调用成员函数会失败,提示SOCKET相关的窗口不存在。其原因在于消息循环是和窗口相关的,而窗口又是线程相关的。具体涉及到SOCKET状态_AFX_SOCK_STATE和SOCKET线程模块状态_AFX_SOCK_THREAD_STATE(AFX_MODULE_THREAD_STATE)。
CSocket一般配合多线程使用,只要你想收发数据,你就可以创建一个CSocket对象,并创建一个子线程来进行收发。同步+多线程≈异步。但是大规模并发通信时,由于Windows消息泵本省的局限性,WAsyncSelect异步模型可能遭遇瓶颈。
最后,别忘了使用MFC WinSock类CAsyncSocket/CSocket的线程需要调用AfxSocketInit初始化WinSock库。一般在MFC程序的CWinApp::InitInstance()中调用AfxSocketInit。
源码参考:<AFXSOCK.H>、<AFXSOCK.INL>、<SOCKCORE.CPP>
// AFXSOCK.H
#include <winsock.h>
#pragma comment(lib, “wsock32.lib”)
/
// AFXSOCK – MFC support for Windows Sockets
// CSocketWnd — internal use only Implementation for sockets notification callbacks.
class CSocketWnd : public CWnd
// Async Socket implementation and base class for Synchronous Socket
class CAsyncSocket : public CObject
// Synchronous Socket
class CSocket : public CAsyncSocket
// Used with CSocket and CArchive for streaming objects on sockets.
class CSocketFile : public CFile
3.WinInet API
WinInet API主要是开发基于TCP/IP模型中的应用层客户端程序。
WinInet(「Windows Internet」)API是一个高阶函数集,帮助程序写作者使用三个常见的Internet协议:用于World Wide Web全球信息网的超文字传输协议(HTTP,Hypertext Transfer Protocol)、文件传输协议(FTP,File Transfer Protocol)和另一个称为Gopher的文件传输协议。
WinInet API由动态链接库wininet.dll提供支持。
// Internet Extensions for Win32
#include <wininet.h>
#pragma comment(lib, “wininet.lib”)
LoadLibrary(“C://WINDOWS//system32//wininet.dll”);
WinInet函数的语法与常用的Windows文件函数的语法类似,这使得使用这些协议就像使用本地磁盘驱动器上的文件一样容易。
MFC对WinInet API的封装(MFC Classes for Creating Internet Client Applications)
MSDN关键字:WinInet, programming/ WinInet, classes
MFC provides the following classes and global functions for writing Internet client applications. Indentation indicates a class is derived from the unindented class above it. CGopherFile and CHttpFile derive from CInternetFile, for example.
Classes:
CInternetSession
CInternetConnection
CFtpConnection
CGopherConnection
CHttpConnection
CInternetFile
CGopherFile
CHttpFile
CFileFind
CFtpFileFind
CGopherFileFind
CGopherLocator
CInternetException
Global functions:
AfxParseURL
AfxGetInternetHandleType
AfxThrowInternetException
源码参考:<AFXINET.H>、<AFXINET.INL>、<INET.CPP>
// AFXINET.H
#include <wininet.h>
#pragma comment(lib, “wininet.lib”)
// Global Functions
AfxParseURL();
AfxParseURLEx();
CHtmlView
class CHtmlView : public CFormView
CHtmlView类在文档/视图结构的应用程序中提供WebBrowser控件的功能。WebBrowser控件是客户可浏览网址以及本地文件和网络文件的窗口。WebBrowser控件支持超级链接、统一资源定位符(URL)导航器并维护一张历史列表,核心接口是IWebBrowser2,它是Internet Explorer的核心。
源码参考:<AFXHTML.H>、<AFXHTML.INL>、<VIEWHTML.CPP>
4.ISAPI
ISAPI是Internet Server Application Program Interface(服务器应用程序接口)的简写,是微软提供的一套面向Internet 服务的API接口,它能实现CGI(Common Gateway Interface,公共网关接口)能提供的全部功能,并在此基础上进行了扩展,如提供了过滤器应用程序接口。ISAPI的工作原理和CGI大体上是相同的,都是通过交互式主页取得用户输入信息,然后交服务器后台处理。
ISAPI应用的DLL不仅可以象CGI程序一样被用户请求激活,还可以被系统预先激活来监视用户输入;对于被用户激活的DLL,在处理完一个用户请求后不会马上消失,而是继续驻留在内存中等待处理别的用户输入,直到过了一段时间后一直没有用户输入。一个ISAPI的DLL,可以在被用户请求激活后长驻内存,等待用户的另一个请求,还可以在一个DLL里设置多个用户请求处理函数,此外,ISAPI的DLL应用程序和WWW服务器处于同一个进程中,效率要显著高于CGI。
每个使用DLL的请求都会赋给同一个处理器空间的线程,IIS通常只是在初始化的时候为线程分好一个线程池后,在运转周期里就一直使用这些线程。线程池技术减小了创建线程开销。
ISAPI出现在CGI之后,是一种优于CGI的动态网页开发技术,ASP是一个更好的ISAPI,而ASP.NET是更好的ASP。
ISAPI Classes
ISAPI describes an interface for Internet servers. An example of an ISAPI server is Windows NT Server running Microsoft Internet Information Server (IIS).
HTTP filters handle server requests. They can be used to handle the following types of applications:
(1)Custom authentication schemes
(2)Data compression
(3)Encryption
(4)Logging
Filter Classes
CHttpFilter
Filters selected HTTP requests sent to an ISAPI server.
CHttpFilterContext
Manages the context for an HTTP filter. This is a helper class to handle multiple, concurrent requests of a CHttpFilter object.
Server Classes
ISAPI server extensions process server requests. MFC ISAPI classes will not process requests from a Common Gateway Interface (CGI).
CHttpServer
Extends the functionality of an ISAPI server by processing client requests.
CHttpServerContext
Manages the context for an ISAPI server extension. This is a helper class to handle multiple, concurrent requests of a CHttpServer object.
Related Classes
CHtmlStream(流IO、动态内存管理)
Handles caching HTML output. Functionally similar to CMemFile.
源码参考:<AFXISAPI.H>、<AFXISAPI.INL>、<ISAPIMIX.CPP>、<ISAPI.CPP>
// AFXISAPI.H
#include <VC98//Include//HTTPEXT.H>
#include <VC98//Include//HTTPFILT.H>
/
// AFXIASPI – MFC Internet Server API support
// CHtmlStream — manages in-memory HTML
// Status codes for HTTP transactions
// Parse Map macros
参考:
《MFC深入浅出》李久进
第十四章《SOCKET类的设计和实现》
《Visual C++ 6.0网络编程技术》雷斌等
《Visual C++ 6 From The Ground Up》John Paul Mueller
第三部分《Visual C++与Internet》
《ISAPI实用技术指南》K.Clements
《用 C++ 开发 Web 商用程序》
《网络编程学习小结》