WWW INFOMATION
 tips top >> C/C++言語でCGI

C/C++言語でCGI

C/C++でCGIを作るメリットは、何より速度とサーバへの負荷軽減です。他の言語とは比較にならないくらい高速で動作し、また、マシンへの負荷も軽いです。
サーバの処理能力は限界に近いけど、買い換えるお金もない・・なんて場合、PHP、Perl、javaなどのサーバー側処理をC/C++に変更するだけで、劇的に変化します。

以下、C/C++言語でCGIを利用するためのトピックスです。
( 環境:windows + Apache2 + CGI )

サーバを設定する CGIを使う
フォームデータを受け取る ファイル操作
最後に

サーバを設定する

Windows+APACHE 2.0.48でCGIを使用する設定を以下に示します。

@httpd.confの設定を修正する。

・モジュールの追加(デフォルトで追加されている。)
LoadModule cgi_module modules/mod_cgi.so
・CGIディレクトリの設定
ScriptAlias /cgi-bin/ "CGI設置ディレクトリを指定"
・CGI利用可能の設定を変更(option の設定項目をExecCGIに)
<Directory "CGI設置ディレクトリを指定">
        AllowOverride None
        Options ExecCGI
        Order allow,deny
        Allow from all
   </Directory>

Aサーバの再起動

APACHEを再起動します。

CGIを使う

単純な例として、”テスト出力です。”と表示するCGIの例を以下に示します。
(これをコンパイルし、hoge.cgiなどとして、http://xxx/hoge.cgiといった形でアクセスすると、ブラウザで表示されます。)

/* C++ */
#include <iostream>
#include <string>
using namespace std;

void main(void)
{
  /* 出力データを文字列としてためておくための変数 */
  string strOutData;

  /* httpヘッダ */
  /* HTML出力であることを出力 */
  strOutData="Content-type: text/html; charset=Shift_JIS\n";
  /* ブラウザにキャッシュさせない。 */
  strOutData+="Pragma: no-cache\n";
  strOutData+="Cache-Control: no-cache\n\n";//最後は改行2つ

  /* 本文出力 */
  strOutData+="<html><head><title>テスト</title><head>";
  strOutData+="<body>テスト出力です。</body></html>";

  /* 標準出力に設定内容を出力 */
  out << strOutData.c_str();

}

フォームデータを受け取る

Cでのフォームデータの受け取り方です。

掲示板やブログでは、この取得内容をファイルやDBに出力し記録しておきます。

#include <stdio.h>
#include <stdlib.h>

int Datatok(char* pchOrgData,int inDatalen,
            char* pchName[],char* pchValue[]);
int DataDcd(char* pchData,int inLen);

void main(void)
{
  char *pchBuf=NULL;
  char *pchName[10],*pchValue[10];//各データの取得用
  int inLen=0;

  /*************************************************/
  /*** フォームデータの取得 ***/
  /*************************************************/

  /* フォームデータのサイズの取得 */
  if ( getenv("CONTENT_LENGTH")!=NULL )
    inLen = atoi( getenv("CONTENT_LENGTH") );

  if (inLen==0){
    //フォームデータがない場合の処理。
    //ここではサンプルなので終了
    return ;
  }

  /* データの取得 */
  pchBuf=(char *)calloc(inLen+1,sizeof(char));
  if (!pchBuf)
    //エラー処理 サンプルなので省略


  /*************************************************/
  /*** 取得したデータを分解 ***/
  /*************************************************/

  /*
  フォームから送信されたデータは項目毎に&で結ばれている。
  また、各項目の名称と値は name=valueの形で入っている。
  例)
    data1=value1&data2=value2&data3=value3
  */

  int inCnt=Datatok(pchBuf,inLen,pchName,pchValue);
  if ( inCnt==-1 )
    //エラー処理 省略


  /*************************************************/
  /*** 取得データをデコード ***/
  /*************************************************/

  /*
  ブラウザから送信されたデータはエンコード(変換)されて
  いるため、デコード(再変換)する。
  */

  for(int i = 0;i<inCnt;i++){
    DataDcd(pchName[i],strlen(pchName[i]));
    DataDcd(pchValue[i],strlen(pchValue[i]));
  }

  /* これでpchNameに項目、pchValueに値が入る */

  free((void *)pchBuf);


}
/* データの分解 */
int Datatok(char* pchOrgData,int inDatalen,
            char* pchName[],char* pchValue[])
{
    int i,inTokCnt;
    i=0;
    inTokCnt=0;
    if(pchOrgData[0]==NULL) return(-1);
    pchName[0]=pchOrgData;
    while((pchOrgData[++i]!=NULL)&&(i<inDatalen)){

      /* 項目の分解 */
            if(pchOrgData[i]=='='){
                    pchOrgData[i]=NULL;
                    pchValue[inTokCnt]=pchOrgData+i+1;

            /* データ項目で分解 */
            }else if(pchOrgData[i]=='&'){
                    pchOrgData[i]=NULL;
                    inTokCnt++;
                    pchName[inTokCnt]=pchOrgData+i+1;
            }
    }
    return inTokCnt+1;
}
/* データのデコード */
int DataDcd(char* pchData,int inLen)
{
    int i,j;
    char pchBuf,*pchTmp;
    if(len==0)
      return -1;
    pchTmp=(char*)malloc(inLen);
    for(i=0,j=0;i<inLen;i++,j++)
    {
  if(pchData[i]=='+'){pchTmp[j]=' ';continue;}
  if(pchData[i]!='%'){pchTmp[j]=pchData[i];continue;}
  if ( pchData[++i]>='A' )
    pchBuf=pchData[i]-'A'+10;
  else
    pchBuf=pchData[i]-'0';
  pchBuf*=16;
  if ( pchData[++i]>='A' )
    pchBuf+=pchData[i]-'A'+10;
        else
          pchBuf+=pchData[i]-'0';
  pchTmp[j]=pchBuf;
    }
    for(i=0;i<j;i++) pchData[i]=pchTmp[i];
    pchData[i]='\0';
    free(pchTmp);

    return 0;
}

ファイル操作

関係ありませんが、ファイルロックの仕方も載せておきます。
Windowsでは、ファイルロック用(ファイルの部分ロックですが、、)API関数が用意されていますが、ここではロックファイル を作成し、ロックファイルが存在する場合は処理を行わないようにしています。


#include <iostream>
#include <string>
using namespace std;

#include "windows.h"

/* ロックファイル用のクラスを用意 */
#include "clsLockFile.h"

void main(void)
{

  clsLockFile objRockFile;

  try{
    /*******************************************/
    /* ロックファイル作成と待機 */
    /*******************************************/
    objRockFile.makelockfile();


  /*
  ここでメイン処理を行う。
  同時処理は行っていないため、同時アクセス
  があっても常にプロセスはひとつしか起動しない。
  BBS程度なら処理が早いので
  これで十分だと思っています。
  */


  }catch(...){
    //エラー処理
  }

  /*******************************************/
  /* ロックファイルの削除 */
  /*******************************************/
  objRockFile.deletelockfile();
}


/* ロックファイル制御用クラス */


/* clsLockFile.hの内容 */

class clsLockFile
{
  bool mblFileMakeCheck;
public:
  void makelockfile(void);
  void deletelockfile(void);
};


/* clsLockFile.cppの内容 */

#include <iostram>
#include <fdtream>
using namespace std;

#include <stdio.h>
#include <stdlib.h>

#include "clsLockFile.h"

void clsLockFile::makelockfile(void){

  WIN32_FIND_DATA FindFileData;
  HANDLE hFind;

  bool blCheck=false;

  /* ちょっとだけ見つかるまで待機 */
  for(int i=0;i < 60000;i++){

    hFind=FindFirstFile(D_LOCKFILE,
      &FindFileData);
    /* ファイル存在 */
    if ( hFind != INVALID_HANDLE_VALUE ){

      FindClose(hFind);
      blCheck = true;
    /* ファイルが存在しない。 */
    }else{
      break;
    }
  }
  /* ロックファイルがあった場合、例外発生 */
  if (blCheck){
    mblFileMakeCheck=false;
    throw(string("エラー"));

  /* ロックファイルがない場合、
        ロックファイル作成 */
  }else{
    HANDLE hFile;

    hFile = CreateFile(
    D_LOCKFILE,
    GENERIC_READ | GENERIC_WRITE,
    FILE_SHARE_READ | FILE_SHARE_WRITE,
    NULL,
    CREATE_NEW,
    FILE_ATTRIBUTE_NORMAL,
    NULL);
    if (  hFile==INVALID_HANDLE_VALUE )
      throw(string("エラー"));

    CloseHandle(hFile);
    mblFileMakeCheck=true;

  }
}
void clsLockFile::deletelockfile(void){
  if(mblFileMakeCheck)
    DeleteFile(D_LOCKFILE);
}

最後に

ロジックは、他の言語と変わらないので、慣れている人なら移植もさほど難しくはないはずです。
ただし、上記、デコードの例にあるように、他の言語で標準で用意されている関数はほとんどなく、自分で作らなくてはいけません。ただ、C/C++は長い歴史があるため、世にたくさんのライブラリやサンプルがありますし、開発効率が悪い反面、作られたものは高速で動作します。

「やってみたら考えていたより簡単!」だったりもしますので、「金は無いけど時間はある!」という場合、C/C++でのCGIはお勧めです。