シリーズ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 うに いくら
うに、いくらって何だよ(笑)って人はすいませんが前日の日記を参照してください。