当前位置:七道奇文章资讯编程技术Java编程
日期:2011-03-22 16:16:00  来源:本站整理

用C++写的CGI程序[Java编程]

赞助商链接



  本文“用C++写的CGI程序[Java编程]”是由七道奇为您精心收集,来源于网络转载,文章版权归文章作者所有,本站不对其观点以及内容做任何评价,请读者自行判断,以下是其具体内容:
经过前面的学习,大家应当可以按按例子用ANSI C为自己的服务器写出CGI程序.之所以选用ANSI C,是因为它几近到处可见,是最风行的C语言尺度.当然,目前的C++也非常风行了,分外是采取GNU C++编译器(g++)情势的那一些(注释④).可从网上很多地方免费下载g++,并且可选用几近全部平台的版本(普通与Linux那样的操作系统配套供应,且已预先安装好).正如大家行将看到的那样,从CGI程序可得到面向对象程序计划的很多好处.

④:GNU的全称是“Gnu's Not Unix”.这最早是由“安闲软件基金会”(FSF)负责开辟的一个项目,努力于用一个免费的版本代替原有的Unix操作系统.目前的Linux仿佛正在做前人没有做到的事情.但GNU工具在Linux的开辟中扮演了至关重要的角色.事实上,Linux的整套软件包附带了数目非常多的GNU组件.

为避免第一次就提出过量的新概念,这个程序并未打算成为一个“纯”C++程序;有些代码是用普通C写成的——固然还可选用C++的一些替用情势.但这并非个突出的问题,因为该程序用C++制作最大的好处就是可以成立类.在解析CGI信息的时刻,由于我们最关心的是字段的“名称/值”对,所以要用一个类(Pair)来代表单个名称/值对;另一个类(CGI_vector)则将CGI字串自动解析到它会包容的Pair对象里(作为一个vector),这样便可在有空的时刻把每个Pair(对)都取出来.
这个程序同时也非常风趣,因为它演示了C++与Java相比的很多优缺陷.大家会看到一些类似的东西;比方class关键字.拜候掌握利用的是完好相同的关键字public和private,但用法却有所差别.它们掌握的是一个块,而非单个办法或字段(也就是说,假如指定private:,后续的每个定义都具有private属性,直到我们再指定public:为止).别的在成立一个类的时刻,全部定义都自动默许为private.
在这儿利用C++的一个缘由是要操纵C++“尺度模板库”(STL)供应的便利.至少,STL包含了一个vector类.这是一个C++模板,可在编译期间举行配置,令其只包容一种特定范例的对象(这里是Pair对象).和Java的Vector差别,假如我们试图将除Pair对象之外的任何东西置入vector,C++的vector模板城市造成一个编译期错误;而Java的Vector可以照单全收.并且从vector里取出什么东西的时刻,它会自动成为一个Pair对象,毋需举行造型处理.所以查抄在编译期举行,这使程序显得更为“结实”.此外,程序的运行速度也可以加快,因为没有必要举行运行期间的造型.vector也会过载operator[],所以可以操纵非常便利的语法来提取Pair对象.vector模板将在CGI_vector成立时利用;在当时,大家便可以领会到如此简短的一个定义竟然储藏有那么宏大的能量.
若提到缺陷,就一定不要忘掉Pair在下列代码中定义时的复杂程度.与我们在Java代码中看到的相比,Pair的办法定义要多得多.这是由于C++的程序员必须提早知道若何用副本构建器掌握复制历程,并且要用过载的operator=完成赋值.正如第12章注释的那样,我们有时也要在Java中考虑一样的事情.但在C++中,几近一刻都不能放松对这些问题的关注.
这个项目首先成立一个可以反复利用的部份,由C++头文件中的Pair和CGI_vector构成.从技术角度看,确切不该把这些东西都塞到一个头文件里.但就目前的例子来说,这样做不会造成任何方面的侵害,并且更具有Java气势,所以大家阅读理解代码时要显得轻松一些:

//: CGITools.h
// Automatically extracts and decodes data
// from CGI GETs and POSTs. Tested with GNU C++ 
// (available for most server machines).
#include <string.h>
#include <vector> // STL vector
using namespace std;

// A class to hold a single name-value pair from
// a CGI query. CGI_vector holds Pair objects and
// returns them from its operator[].
class Pair {
  char* nm;
  char* val;
public:
  Pair() { nm = val = 0; }
  Pair(char* name, char* value) {
    // Creates new memory:
    nm = decodeURLString(name);
    val = decodeURLString(value);
  }
  const char* name() const { return nm; }
  const char* value() const { return val; }
  // Test for "emptiness"
  bool empty() const {
    return (nm == 0) || (val == 0);
  }
  // Automatic type conversion for boolean test:
  operator bool() const {
    return (nm != 0) && (val != 0);
  }
  // The following constructors & destructor are
  // necessary for bookkeeping in C++.
  // Copy-constructor:
  Pair(const Pair& p) {
    if(p.nm == 0 || p.val == 0) {
      nm = val = 0;
    } else {
      // Create storage & copy rhs values:
      nm = new char[strlen(p.nm) + 1];
      strcpy(nm, p.nm);
      val = new char[strlen(p.val) + 1];
      strcpy(val, p.val);
    }
  }
  // Assignment operator:
  Pair& operator=(const Pair& p) {
    // Clean up old lvalues:
    delete nm;
    delete val;
    if(p.nm == 0 || p.val == 0) {
      nm = val = 0;
    } else {
      // Create storage & copy rhs values:
      nm = new char[strlen(p.nm) + 1];
      strcpy(nm, p.nm);
      val = new char[strlen(p.val) + 1];
      strcpy(val, p.val);
    }
    return *this;
  } 
  ~Pair() { // Destructor
    delete nm; // 0 value OK
    delete val;
  }
  // If you use this method outide this class, 
  // you're responsible for calling 'delete' on
  // the pointer that's returned:
  static char* 
  decodeURLString(const char* URLstr) {
    int len = strlen(URLstr);
    char* result = new char[len + 1];
    memset(result, len + 1, 0);
    for(int i = 0, j = 0; i <= len; i++, j++) {
      if(URLstr[i] == '+')
        result[j] = ' ';
      else if(URLstr[i] == '%') {
        result[j] =
          translateHex(URLstr[i + 1]) * 16 +
          translateHex(URLstr[i + 2]);
        i += 2; // Move past hex code
      } else // An ordinary character
        result[j] = URLstr[i];
    }
    return result;
  }
  // Translate a single hex character; used by
  // decodeURLString():
  static char translateHex(char hex) {
    if(hex >= 'A')
      return (hex & 0xdf) - 'A' + 10;
    else
      return hex - '0';
  }
};

// Parses any CGI query and turns it
// into an STL vector of Pair objects:
class CGI_vector : public vector<Pair> {
  char* qry;
  const char* start; // Save starting position
  // Prevent assignment and copy-construction:
  void operator=(CGI_vector&);
  CGI_vector(CGI_vector&);
public:
  // const fields must be initialized in the C++
  // "Constructor initializer list":
  CGI_vector(char* query) :
      start(new char[strlen(query) + 1]) {
    qry = (char*)start; // Cast to non-const
    strcpy(qry, query);
    Pair p;
    while((p = nextPair()) != 0)
      push_back(p);
  }
  // Destructor:
  ~CGI_vector() { delete start; }
private:
  // Produces name-value pairs from the query 
  // string. Returns an empty Pair when there's 
  // no more query string left:
  Pair nextPair() {
    char* name = qry;
    if(name == 0 || *name == '\0')
      return Pair(); // End, return null Pair
    char* value = strchr(name, '=');
    if(value == 0)
      return Pair(); // Error, return null Pair
    // Null-terminate name, move value to start
    // of its set of characters:
    *value = '\0';
    value++;
    // Look for end of value, marked by '&':
    qry = strchr(value, '&');
    if(qry == 0) qry = ""; // Last pair found
    else {
      *qry = '\0'; // Terminate value string
      qry++; // Move to next pair
    }
    return Pair(name, value);
  }
}; ///:~

在#include语句后,可看到有一行是:
using namespace std;
C++中的“命名空间”(Namespace)办理了由Java的package负责的一个问题:将库名躲藏起来.std命名空间引用的是尺度C++库,而vector就在这个库中,所以这一行是必须的.
Pair类表面看非常简单,只是包容了两个(private)字符指针罢了——一个用于名字,另一个用于值.默许构建器将这两个指针简单地设为零.这是由于在C++中,对象的内存不会自动置零.第二个构建器调用办法decodeURLString(),在新分配的堆内存中生成一个解码过后的字串.这个内存区域必须由对象负责管理及排除,这与“破坏器”中见到的相同.name()和value()办法为相关的字段产生只读指针.操纵empty()办法,我们查询Pair对象它的某个字段能否为空;返回的后果是一个bool——C++内建的基本布尔数据范例.operator bool()利用的是C++“运算符过载”的一种特别情势.它答应我们掌握自动范例转换.假若有一个名为p的Pair对象,并且在一个本来但愿是布尔后果的表达式中利用,比方if(p){//...,那么编译器能辨别出它有一个Pair,并且需求的是个布尔值,所以自动调用operator bool(),举行必要的转换.
接下来的三个办法属于通例编码,在C++中成立类时必须用到它们.按照C++类采取的所谓“经典情势”,我们必须定义必要的“原始”构建器,以及一个副本构建器和赋值运算符——operator=(以及破坏器,用于排除内存).之所以要作这样的定义,是由于编译器会“默默”地调用它们.在对象传入、传出一个函数的时刻,需求调用副本构建器;而在分配对象时,需求调用赋值运算符.只有真正掌握了副本构建器和赋值运算符的工作原理,才能在C++里写出真正“结实”的类,但这需求需求一个对比艰苦的历程(注释⑤).

⑤:我的《Thinking in C++》(Prentice-Hall,1995)用了一整章的地方来谈论这个主题.若需更多的帮忙,请务必看看那一章.

只要将一个对象按值传入或传出函数,就会自动调用副本构建器Pair(const Pair&).也就是说,关于预备为其制作一个完好副本的那个对象,我们不预备在函数框架中传送它的地址.这并非Java供应的一个选项,由于我们只能传送句柄,所以在Java里没有所谓的副本构建器(假如想制作一个本地副本,可以“克隆”那个对象——利用clone(),拜见第12章).近似地,假如在Java里分配一个句柄,它会简单地复制.但C++中的赋值意味着整个对象城市复制.在副本构建器中,我们成立新的存储空间,并复制原始数据.但关于赋值运算符,我们必须在分配新存储空间之前释放老存储空间.我们要见到的大概是C++类最复杂的一种情形,但那恰是Java的支持者们论证Java比C++简单得多的有力证据.在Java中,我们可以安闲传送句柄,善后工作则由垃圾汇集器负责,所以可以轻松很多.
但事情并没有完.Pair类为nm和val利用的是char*,最复杂的情形主如果环绕指针展开的.假如用较时髦的C++ string类来替换char*,事情就要变得简单得多(当然,并非全部编译器都供应了对string的支持).那么,Pair的第一部份看起来就象下面这样:

class Pair {
  string nm;
  string val;
public:
  Pair() { }
  Pair(char* name, char* value) {
    nm = decodeURLString(name);
    val = decodeURLString(value);
  }
  const char* name() const { return nm.c_str(); }
  const char* value() const { 
    return val.c_str(); 
  }
  // Test for "emptiness"
  bool empty() const {
    return (nm.length() == 0) 
      || (val.length() == 0);
  }
  // Automatic type conversion for boolean test:
  operator bool() const {
    return (nm.length() != 0) 
      && (val.length() != 0);
  }

(此外,对这个类decodeURLString()会返回一个string,而不是一个char*).我们不一定义副本构建器、operator=大概破坏器,因为编译器已帮我们做了,并且做得非常好.但即便有些事情是自动举行的,C++程序员也必须理解副本构建以及赋值的细节.
Pair类剩下的部份由两个办法构成:decodeURLString()以及一个“帮忙器”办法translateHex()——将由decodeURLString()利用.注意translateHex()并不能防备用户的恶意输入,比方“%1H”.分配好充足的存储空间后(必须由破坏器释放),decodeURLString()就会此中遍历,将全部“+”都换成一个空格;将全部十六进制代码(以一个“%”打头)换成对应的字符.
CGI_vector用于解析和包容整个CGI GET号令.它是从STL vector里担当的,后者例示为包容Pair.C++中的担当是用一个冒号表示,在Java中则要用extends.此外,担当默许为private属性,所以几近必定需求用到public关键字,就象这样做的那样.大家也会发现CGI_vector有一个副本构建器以及一个operator=,但它们都声明成private.这样做是为了避免编译器同步两个函数(假如不自己声明它们,二者就会同步).但这同时也禁止了客户程序员按值大概通过赋值传送一个CGI_vector.
CGI_vector的工作是获得QUERY_STRING,并把它解析成“名称/值”对,这需求在Pair的帮忙下完成.它首先将字串复制到本地分配的内存,并用常数指针start跟踪起始地址(稍后会在破坏器顶用于释放内存).随后,它用自己的nextPair()办法将字串解析成原始的“名称/值”对,各个对之间用一个“=”和“&”标记脱离.这些对由nextPair()传送给Pair构建器,所以nextPair()返回的是一个Pair对象.随后用push_back()将该对象加入vector.nextPair()遍历完好个QUERY_STRING后,会返回一个零值.
目前基本工具已定义好,它们可以简单地在一个CGI程序中利用,就象下面这样:

//: Listmgr2.cpp
// CGI version of Listmgr.c in C++, which 
// extracts its input via the GET submission 
// from the associated applet. Also works as
// an ordinary CGI program with HTML forms.
#include <stdio.h>
#include "CGITools.h"
const char* dataFile = "list2.txt";
const char* notify = "Bruce@EckelObjects.com";
#undef DEBUG

// Similar code as before, except that it looks
// for the email name inside of '<>':
int inList(FILE* list, const char* emailName) {
  const int BSIZE = 255;
  char lbuf[BSIZE];
  char emname[BSIZE];
  // Put the email name in '<>' so there's no
  // possibility of a match within another name:
  sprintf(emname, "<%s>", emailName);
  // Go to the beginning of the list:
  fseek(list, 0, SEEK_SET);
  // Read each line in the list:
  while(fgets(lbuf, BSIZE, list)) {
    // Strip off the newline: 
    char * newline = strchr(lbuf, '\n');
    if(newline != 0) 
      *newline = '\0';
    if(strstr(lbuf, emname) != 0)
      return 1;
  }
  return 0;
}

void main() {
  // You MUST print this out, otherwise the 
  // server will not send the response:
  printf("Content-type: text/plain\n\n");
  FILE* list = fopen(dataFile, "a+t");
  if(list == 0) {
    printf("error: could not open database. ");
    printf("Notify %s", notify);
    return;
  }
  // For a CGI "GET," the server puts the data
  // in the environment variable QUERY_STRING:
  CGI_vector query(getenv("QUERY_STRING"));
  #if defined(DEBUG)
  // Test: dump all names and values
  for(int i = 0; i < query.size(); i++) {
    printf("query[%d].name() = [%s], ", 
      i, query[i].name());
    printf("query[%d].value() = [%s]\n", 
      i, query[i].value());
  }
  #endif(DEBUG)
  Pair name = query[0];
  Pair email = query[1];
  if(name.empty() || email.empty()) {
    printf("error: null name or email");
    return;
  } 
  if(inList(list, email.value())) {
    printf("Already in list: %s", email.value());
    return;
  }
  // It's not in the list, add it:
  fseek(list, 0, SEEK_END);
  fprintf(list, "%s <%s>;\n", 
    name.value(), email.value());
  fflush(list);
  fclose(list);
  printf("%s <%s> added to list\n", 
    name.value(), email.value());
} ///:~

alreadyInList()函数与前一个版本几近是完好相同的,只是它假定全部电子信件地址都在一个“<>”内.
在利用GET办法时(通过在FORM指导号令的METHOD标志内部设置,但这在这里由数据发送的方法掌握),Web服务器会汇集位于“?”背面的全部信息,并把它们置入环境变量QUERY_STRING(查询字串)里.所认为了读取那些信息,必须得到QUERY_STRING的值,这是用尺度的C库函数getnv()完成的.在main()中,注意对QUERY_STRING的解析有多么简单:只需把它传送给用于CGI_vector对象的构建器(名为query),剩下的全部工作城市自动举行.从这时开始,我们便可以从query中取出名称和值,把它们当作数组对待(这是由于operator[]在vector里已经过载了).在调试代码中,大家可看到这一切是若何运作的;调试代码封装在预处理器指导号令#if defined(DEBUG)和#endif(DEBUG)之间.
目前,我们急迫需求掌握一些与CGI有关的东西.CGI程序用两个方法之一传送它们的输入:在GET履行期间通过QUERY_STRING传送(目前用的这种方法),大概在POST期间通过尺度输入.但CGI程序通过尺度输动身送自己的输出,这普通是用C程序的printf()号令实现的.那么这个输出到那边去了呢?它回到了Web服务器,由服务器决意该若何处理它.服务器作出决意的根据是content-type(内容范例)头数据.这意味着假定content-type头不是它看到的第一件东西,就不知道该若何处理收到的数据.因此,我们无论若何也要使全部CGI程序都从content-type头开始输出.
在目前这种情形下,我们但愿服务器将全部信息都直接反馈回客户程序(亦即我们的程序片,它们正在等候给自己的答复).信息应当原封不动,所以content-type设为text/plain(纯文本).一旦服务器看到这个头,就会将全部字串都直接发回给客户.所以每个字串(三个用于出错条件,一个用于成功的加入)城市返回程序片.
我们用相同的代码增添电子信件名称(用户的姓名).但在CGI脚本的情形下,并不存在无限循环——程序只是简单地呼应,然后就中止.每次有一个CGI恳求到达时,程序城市启动,对那个恳求作出反映,然后自行关闭.所以CPU不大概陷入空等候的尴尬地步,只有启动程序和翻开文件时才存在性能上的隐患.Web服务器对CGI恳求举行掌握时,它的开销会将这种隐患减轻到最低程度.
这种计划的另一个好处是由于Pair和CGI_vector都得到了定义,大大都工作都帮我们自动完成了,所以只需改正main()便可轻松成立自己的CGI程序.固然小服务程序(Servlet)终究会变得越来越风行,但为了成立快速的CGI程序,C++仍旧显得非常便利.
  以上是“用C++写的CGI程序[Java编程]”的内容,如果你对以上该文章内容感兴趣,你可以看看七道奇为您推荐以下文章:
  • 利用cmd号令行窗口操作SqlServer的办法
  • CSS代码实例:用CSS代码写出的各种形状图形
  • Ubuntu利用cvt号令生成xorg.conf下的屏幕辨别率和刷
  • 在Firefox中利用Chrome自动更新的Flash
  • ubuntu 10.04下禁用ctrl+alt+del
  • 利用crontab实现以秒履行
  • Ubuntu利用crontab按时任务
  • Ubuntu系统中启用Chrome的Web App利用
  • 电脑换了双核CPU性能不但没提高用CPU-Z检测出来竟然是单核
  • 不用C++代码写MFC基于对话框利用程序
  • 进程spoolsv.exe占用CPU利用率100%的办理办法
  • 在MySQL数据库中利用C履行SQL语句
  • 本文地址: 与您的QQ/BBS好友分享!
    • 好的评价 如果您觉得此文章好,就请您
        0%(0)
    • 差的评价 如果您觉得此文章差,就请您
        0%(0)

    文章评论评论内容只代表网友观点,与本站立场无关!

       评论摘要(共 0 条,得分 0 分,平均 0 分) 查看完整评论
    Copyright © 2020-2022 www.xiamiku.com. All Rights Reserved .