互联网刚兴起时,很多项目都是用 C /Perl 语言写的一大堆 CGI,一些老程序员可谓是偿尽了编程的苦,因为那时国内的技术水平普遍比较低,如果你会 CGI 编程,就已经算是行业中人了,如果你对 CGI 编程比较熟练,则就可以称得是“专家”了,后来技术不断进步,各种国外的新技术都进入中国并不断得到普及,CGI 就逐渐沦为一种落后的技术,后来的 PHP, JSP/Servlet, ASP 逐渐占领了 WEB 编程的技术市场,这个时候如果你说再用 C 写 CGI,别人会感觉是在和古人对话。
现在主流的 WEB 开发语言一个很大的优势就是有各种相对成熟的基础库和框架,开发效率很高,而 CGI 则就逊色很多。当然,这些语言也得有执行效率相对较低的问题,毕竟它们都是脚本语言或半编译语言,需要虚拟机解释执行,象 facebook 的 WEB 前端基本都是用 PHP 写的,他们为了解决执行效率问题,在一位华人的领导下开发了可以将 PHP 代码转成 C++ 代码的工具(hiphop),从而使执行效率大大提高,这也从另一个侧面反映出技术人员还是希望他们的程序能够运行的更快些。
本文主要描述了 acl_cpp 库中有关 WEB 编程的方法类,为了使大家容易上手,其中的接口设计及命名尽量模仿 JAVA HttpServlet 等相关的类(希望 Oracle 不会告我侵权,呵呵)。如果您会用C/C++编程,同时又有使用 Java Servlet 进行 WEB 编程的经验,则该文您读起来一点不会费力,当然如果您多年从事 WEB 开发,我想理解这些类的设计及用法也不应该有什么难度。好了,下面就开始讲如何使用 acl_cpp 库中的 http/ 模块下的类进行 web 编程。
在 acl_cpp/src/http 模块下,有几个类与 WEB 编程相关:HttpServlet,HttpServletRequest, HttpServletResponse, HttpSession, http_header, http_mime, http_client。如果您掌握了这几个类的用法,则进行 WEB 编程就不会有什么问题了,下面一一介绍这几个类:
一、HttpServlet 类
构造函数及析构函数:
/**
* 构造函数
*/
HttpServlet(void);
/**
* 纯虚析构函数,即该类必须由子类进行实例化
*/
virtual ~HttpServlet(void) =0;
在构建函数中,为了支持 HttpSession 数据的存储,需要用户给出 memcached 的服务器地址(目前仅支持采用 memcached 来存储 session 数据,将来应该会扩展至可以支持 redis 等),同时用户还需要给出 session 的 cookie ID 标识符以发给浏览器。
四个虚接口,需要子类实现以应对不同的浏览器的 HTTP 请求:
/** * 当 HTTP 请求为 GET 方式时的虚函数 */ virtual bool doGet(HttpServletRequest&, HttpServletResponse&); /** * 当 HTTP 请求为 POST 方式时的虚函数 */ virtual bool doPost(HttpServletRequest&, HttpServletResponse&); /** * 当 HTTP 请求为 PUT 方式时的虚函数 */ virtual bool doPut(HttpServletRequest&, HttpServletResponse&); /** * 当 HTTP 请求为 CONNECT 方式时的虚函数 */ virtual bool doConnect(HttpServletRequest&, HttpServletResponse&); /** * 当 HTTP 请求为 PURGE 方式时的虚函数,该方法在清除 SQUID 的缓存 * 时会用到 */ virtual bool doPurge(HttpServletRequest&, HttpServletResponse&); |
用户实现的 HttpServlet 子类中可以实现以上几个虚接口的一个或者几个,以满足不同的 HTTP 请求。
下面的函数为 HttpServlet 类开始运行的函数:
/** * HttpServlet 对象开始运行,接收 HTTP 请求,并回调以下 doXXX 虚函数 * @param session {session&} 存储 session 数据的对象 * @param stream {socket_stream*} 当在 acl_master 服务器框架控制下 * 运行时,该参数必须非空;当在 apache 下以 CGI 方式运行时,该参数 * 设为 NULL;另外,该函数内部不会关闭流连接,应用应自行处理流对象 * 的关闭情况,这样可以方便与 acl_master 架构结合 * @param body_parse {bool} 针对 POST 方法,该参数指定是否需要 * 读取 HTTP 请求数据体并按 n/v 方式进行分析;当为 true 则内 * 部会读取 HTTP 请求体数据,并进行分析,当用户调用 getParameter * 时,不仅可以获得 URL 中的参数,同时可以获得 POST 数据体中 * 的参数;当该参数为 false 时则不读取数据体 * @param body_limit {int} 针对 POST 方法,当数据体为文本参数 * 类型时,此参数限制数据体的长度;当数据体为数据流或 MIME * 格式或 body_read 为 false,此参数无效 * @return {bool} 返回处理结果 */ bool doRun(session& session, socket_stream* stream = NULL, bool body_parse = true, int body_limit = 102400); /** * HttpServlet 对象开始运行,接收 HTTP 请求,并回调以下 doXXX 虚函数, * 调用本函数意味着采用 memcached 来存储 session 数据 * @param memcached_addr {const char*} memcached 服务器地址,格式:IP:PORT * @param stream {socket_stream*} 含义同上 * @param body_parse {bool} 含义同上 * @param body_limit {int} 含义同上 * @return {bool} 返回处理结果 */ bool doRun(const char* memcached_addr = "127.0.0.1:11211", socket_stream* stream = NULL, bool body_parse = true, int body_limit = 102400); |
从上面五个虚方法中,可以看到两个重要的类:HttpServletRequest 和 HttpServletResponse。这两个类分别表示 http 请求类及 http 响应类,这两个类都是由 HttpServlet 类对象创建并释放的,所以用户不必创建和销毁这两个类对象实例。下面分别介绍这两个类:
二、HttpServletRequest 类
该类主要是与浏览器的请求过程相关,您可以通过该类的方法获得浏览器的请求数据。该类的方法比较多(基本上是参照了 java HttpServlet 的功能方法及名称),所以下面仅介绍几个主要的方法:
/** * 获得 HTTP 请求中的参数值,该值已经被 URL 解码且 * 转换成本地要求的字符集;针对 GET 方法,则是获得 * URL 中 ? 后面的参数值;针对 POST 方法,则可以获得 * URL 中 ? 后面的参数值或请求体中的参数值 */ const char* getParameter(const char* name) const; /** * 获得 HTTP 客户端请求的某个 cookie 值 * @param name {const char*} cookie 名称,必须非空 * @return {const char*} cookie 值,当返回 NULL 时表示 * cookie 值不存在 */ const char* getCookieValue(const char* name) const; /** * 获得与该 HTTP 会话相关的 HttpSession 对象引用 * @return {HttpSession&} */ HttpSession& getSession(void); /** * 获得与 HTTP 客户端连接关联的输入流对象引用 * @return {istream&} */ istream& getInputStream(void) const; /** * 获得 HTTP 请求数据的数据长度 * @return {acl_int64} 返回 -1 表示可能为 GET 方法, * 或 HTTP 请求头中没有 Content-Length 字段 */ fdef WIN32 __int64 getContentLength(void) const; lse long long int getContentLength(void) const; ndif /** * 当 HTTP 请求头中的 Content-Type 为 * multipart/form-data; boundary=xxx 格式时,说明为文件上传 * 数据类型,则可以通过此函数获得 http_mime 对象 * @return {const http_mime*} 返回 NULL 则说明没有 MIME 对象, * 返回的值用户不能手工释放,因为在 HttpServletRequest 的析 * 构中会自动释放 */ http_mime* getHttpMime(void) const; /** * 获得 HTTP 请求数据的类型 * @return {http_request_t},一般对 POST 方法中的上传 * 文件应用而言,需要调用该函数获得是否是上传数据类型 */ http_request_t getRequestType(void) const; |
以上方法一般都是我们在实际对 HttpServletRequest 类方法使用过程中用得较多的。如:
getParmeter: 用来获得 http 请求参数
getCookieValue:获得浏览器的 cookie 值
getSession:获得该 HttpServlet 类对象的 session 会话
getInputStream:获得 http 连接的输入流
getContentLength:针对 HTTP POST 请求,此函数获得 HTTP 请求数据体的长度
getRequestType:针对 HTTP POST 请求,此函数返回 HTTP 请求数据体的传输方式(普通的 name=value 方式,multipart 上传文件格式以及数据流格式)