こないだ作った簡易Webサーバーをマルチタスク化するよ。(3)

シリーズ3作目れす。
クライアント毎にプロセスを作成
クライアント毎にスレッドを作成
という内容に続いて

制限付きマルチタスク。

やってみたら新しいことをするのではなくて、起動するプロセスの数を制限するというものでした。サーバーをさわってるとプロセス数の制限とかは割とよくやることなので、なんだと思ってしまうかもしれにゃ。

プログラムも本書のEchoサーバーとまったく同じもので動きました。

てな感じで終わり!!ではつまらないので、ちょっとだけ内容を変えてみて起動するプロセス数を制限して、1個のプロセスでスレッドを立ち上げる型のプログラムにしようと思います。と言ってもほとんど一緒なんだがね。

お、それってApache?と思った方は早合点です。注:たけけんも含む

本書のプログラムは
プロセス =>ソケット
プロセス =>ソケット
です。

今回のちょっと書き換えたものは
親プロセス(exit(0))
子プロセス (Daemon化)
=>スレッド=>ソケット
=>スレッド=>ソケット
子プロセス (Daemon化)
=>スレッド=>ソケット
=>スレッド=>ソケット
です。

Apache(Woker)は
親プロセス(Daemon)
=>子プロセス
=>スレッド=>ソケット
=>スレッド=>ソケット
=>子プロセス
=>スレッド=>ソケット
=>スレッド=>ソケット
で、さらにスレッドの数も制限したりできたはずだね。

WebサーバといってもGETしかできないし、CGIも使えないないし、どんな脆弱性があるかもわかりませんし、だれもやらないとは思いますけど絶対に公開サーバーとしては使わないでね。

こんな脆弱性があった的な話はウェルカムです。

あと、日記に載せるところを小さくしたかったので宣言あたりは全部ヘッダーにしました。
詳しくは前回の日記を参照してください。

そーす  TCPWebServer_type_ForkThread.c

#include "TCPWebServer_type_Thread_2.h"

void ThreadMainroutin(int servSock);
void *ThreadMain(void *arg);
int AcceptTCPConnection(int servSock);
int CreateTCPServerSocket(unsigned short port);

void GetHtml( FILE *sock_fp, HEAD_DATA *data )
{
        FILE *fp;
        char *buf;
        struct stat st;

        if(stat(data->path + 1, &st) < 0){
                fprintf(stderr, "404error\n");
                exit(1);
        }

    if((buf = (char *)calloc(st.st_size, sizeof(char))) == NULL){
          fprintf(stderr, "calloc\n");
          exit(1);
    }

        fprintf(sock_fp, "%s 200 OK\r\n", data->version);
        fprintf(sock_fp, "Server:sample\r\n");
        fprintf(sock_fp, "Content-Type:text/html\r\n");
        fprintf(sock_fp, "Content-Length:%d\r\n", (int)st.st_size);
        fprintf(sock_fp, "\r\n");

        if((fp = fopen(data->path + 1, "r")) == NULL){
                fprintf(stderr, "fopen\n");
                exit(1);
        }

    fread(buf, 1, st.st_size, fp);
    fwrite(buf, 1, st.st_size, sock_fp);

        free(buf);
        fclose(fp);

}

void HandleTCPClient(int clntSocket)
{
        FILE *sock_fp;
        char *data[64], buf[2048];
        HEAD_DATA headData;

        if((sock_fp = fdopen(clntSocket, "r+")) == NULL){
                fprintf(stderr, "fdpoen\n");
                exit(1);
        }

        unsigned int len;
        unsigned int i, k;

        for(i = 0; i < 64; i++){

                fgets(buf, sizeof(buf), sock_fp),  (void)CutCrLf(buf);

                if((len = strlen(buf)) == 0) break;

                if((data[i] = (char *)calloc(len + 1, sizeof(char))) == NULL){
                        fprintf(stderr, "calloc\n");
                        exit(1);
                }
                strcpy(data[i], buf);
        }
        k = i;

#ifdef DEBUG
                printf("%s\n", data[0]);
#endif

        len =  CutChar( data[0]      , headData.method,     ' '  ), len++;
        len += CutChar( data[0] + len, headData.path,       ' '  ), len++;
        len += CutChar( data[0] + len, headData.version,    '\0' ), len++;

#ifdef DEBUG
                printf("%s %s %s\n", headData.method, headData.path, headData.version);
#endif

        if(strstr("headData.version" , "1.0") != NULL || strstr("headData.version" , "1.1") != NULL){
                fprintf(stderr, "request error\n");
                exit(1);
        }

        GetHtml(sock_fp, &headData);
                fflush(sock_fp);
        for(i=0; i < k; i++){
                printf("%s", data[i]);
                free(data[i]);
        }
}

int main(int argc, char *argv[])
{
        int servSock;           //サーバのソケットディスクリプタ
        unsigned short echoServPort;            //サーバポート
                int i;
                pid_t processID = 0;

        if(argc != 2)   //引数の数をチェック
        {
                fprintf(stderr, "Usage: %s <Server Port>\n", argv[0]);
                exit(1);
        }

        echoServPort = atoi(argv[1]);   //ローカルポート
                servSock = CreateTCPServerSocket(echoServPort);
             /* 簡易形式プロセスは5個まで */
                for(i = 0; i < 5; i++){
                        if((processID = fork()) == 0)
                                ThreadMainroutin(servSock);
                        else if(processID < 0)
                                DieWithError("fork() failed");
                }

    exit(0);
}

void ThreadMainroutin(servSock)
{
        int clntSock;
        struct ThreadArgs *threadArgs;
        pthread_t threadID;

    while(1) //繰り返し実行
    {
                clntSock = AcceptTCPConnection(servSock);

            if ((threadArgs = (struct ThreadArgs *) malloc(sizeof(struct ThreadArgs))) == NULL)
                   DieWithError("malloc() failed");
        threadArgs->clntSock = clntSock;

                fprintf(stderr, "1:pid = %d\n", gettid());

        /* Create client thread */
                if (pthread_create(&threadID, NULL, ThreadMain, (void *) threadArgs) != 0)
                DieWithError("pthread_create() failed");
                        pthread_join(threadID, NULL);
                        close(clntSock);
            printf("with thread %ld\n", (long int) threadID);

        }
}


void *ThreadMain(void *threadArgs)
{

    int clntSock;                   /* Socket descriptor for client connection */

        fprintf(stderr, "2:tid=%d\n", gettid());

        clntSock = ((struct ThreadArgs *) threadArgs) -> clntSock;
        free(threadArgs);

        HandleTCPClient(clntSock);

        return(NULL);
}

これであとは多重化という項目を残すだけになりました。
6章、7章も終わってるんだけど、6章についてはもういいよって言われるくらい読み返しておいた方がいい内容な気がする。

つーか、だんだんプログラムが長くなってくるので、ヘッダファイルの書き方とかファイルを分けるポリシーとか、もう1回基本を復習した方がいいっしょ。

なんか分からんけど動いた。となるのが嫌でCをやってるのに先が怖いじょ ((((;´・ω・`)))

おっと、実行結果を忘れてた。

プロセスは5個立ち上がるようにしているので5個起動します。
親プロセスは終了して子プロセスがinitの子になります。
いくら=Acceptが5個ですね。あとはプロンプトが返ります。

[root@testserver_centos ~]# ./TCPWebServer_type_ForkThread 8000                                     
[root@testserver_centos ~]# うに
いくら
うに
いくら
うに
いくら
うに
いくら
うに
いくら

[root@testserver_centos ~]#</pre>
プロセスはこんな感じ。PPID=1で5個動いてます。
<pre>[root@testserver_centos ~]# ps -ef | grep TC[P]
root      6033     1  0 04:13 pts/5    00:00:00 ./TCPWebServer_type_ForkThread 8000
root      6034     1  0 04:13 pts/5    00:00:00 ./TCPWebServer_type_ForkThread 8000
root      6035     1  0 04:13 pts/5    00:00:00 ./TCPWebServer_type_ForkThread 8000
root      6036     1  0 04:13 pts/5    00:00:00 ./TCPWebServer_type_ForkThread 8000
root      6037     1  0 04:13 pts/5    00:00:00 ./TCPWebServer_type_ForkThread 8000</pre>
ブラウザからアクセスすると、上にあるPIDのいずれかからスレッドが作成されてクライアントの処理をします。
<pre>[root@testserver_centos ~]#

Handling client 192.168.24.53
1:pid = 6036
2:tid=12551
with thread 139848045307648
うに
いくら
なまこ

Handling client 192.168.24.53
1:pid = 6037
2:tid=12552
with thread 139848045307648
うに
いくら
なまこ

Handling client 192.168.24.53
1:pid = 6035
2:tid=12553
with thread 139848045307648
うに
いくら

うに、いくらって何だよ(笑)って人はすいませんが前日の日記を参照してください。

 

Related Posts


投稿者: Takeken

インターネット利用者のITリテラシーを向上したいという設定の2次元キャラです。 サーバー弄りからプログラミングまで手を付けた自称エッセイストなたけけんの物語。

コメントを残す

メールアドレスが公開されることはありません。 が付いている欄は必須項目です