Download presentation
Presentation is loading. Please wait.
1
ネットワーク・プログラミング マルチタスク
2
全体の位置づけ オペレーションシステム(Linux) ソケットプログラミング プロセス カーネルとシステムコール プロセス間通信 スレッド
クライアントーサーバ 非同期I/Oとマルチタスク
3
クライアントからの要求が多い場合必要となる
1.1 マルチタスク(並行サーバ) 今まで説明したサーバの処理方法 クライアントからの要求が多い場合必要となる 反復サーバ(TCPEchoServer.c) 一度に一つのクライアントのみ処理する。 並行サーバ(TCPEchoServer-Fork.c) プロセスやスレッドを利用して、1つのクライアントの処理を並列処理するために複数のサーバを作成する。 並行サーバプロセスの作成 クライアントからの要求が到着する毎に新しいプロセスを1つ生成する。 生成された子プロセスは要求してきたクライアントの処理のみを実行する。 子プロセスは処理終了後ゾンビ状態へ。 親プロセスがwaitpid()を実行し子プロセスは完全に消滅する。 TCPEchoServer-Fork.c クライアントから接続要求がある度に子プロセスを作る以外はTCPEchoServer.cと同じ。 サーバはfork()システムコール実行後、 直にclnSockをクローズする。 子プロセスは直にservSockをクローズする。 クライアント 並行サーバ sock servSock クライアント サーバ connect(sock,…) accept(servSock,…) sock clntSock クライアント サーバ fork() sock clntSock サーバ クライアント 子 サーバ:close(clntSock) 子: close(servSock) sock サーバ クライアント clntSock サーバ
4
1.2 TCPEchoServer-Fork.c
プログラマが作成したファイル TCPEchoServer-Fork.c その1 TCPEchoServer-Fork.c その2 #include “TCPEchoServer.h” /* 新しく作ったインクルード */ #include <sys/wait.h> /* waitpid()で利用する */ int main(int argc, char *argv[]) { int servSock; /* サーバのソケット識別子 */ int clntSock; /* クライアントのソケット識別子 */ unsigned short echoServPort; /* サーバのポート番号 */ pid_t processID; /* fork()からのプロセスID */ unsigned int childProcCount = 0; /* 子プロセスの数 */ if (argc != 2) /* 引数の数が正しいかチェック */ fprintf(stderr, "Usage: %s <Server Port>\n", argv[0]); exit(1); } echoServPort = atoi(argv[1]); /* 第1引数はローカルポート番号 */ servSock = CreateTCPServerSocket(echoServPort); for (;;) /* 永遠に繰返す */ { clntSock = AcceptTCPConnection(servSock); /* 子プロセスの作成とエラー報告 */ if ((processID = fork()) < 0) DieWithError("fork() failed"); else if (processID == 0) /* 子プロセスの場合 */ close(servSock); /* 子は親プロセスのソケットをクローズ */ HandleTCPClient(clntSock); exit(0); /* 子プロセスの終了 */ } printf("with child process: %d\n", (int) processID); close(clntSock); /* 親は子のソケットをクローズ */ childProcCount++; /* 未回収の子プロセス数を1増やす */ while (childProcCount) /* 全ゾンビをクリーンアップ */ { processID = waitpid((pid_t) -1, NULL, WNOHANG); if (processID < 0) /* waitpid()のエラーを確認 */ DieWithError("waitpid() failed"); else if (processID == 0) /* ゾンビが存在しない */ break; else childProcCount--; /* 子プロセスを1つ回収 */ /* この部分には到達しない */ その3で説明 接続要求毎に 子プロセス作成 親が使用中のためソケットの リソース自体は開放されない HandleTCPClient() については第9回参照 ワイルド カード 成功すると終了した子プロセスのプロセスID サーバソケットを作成し、接続要求待ち(listen())までを行う関数 ノンブロッキング(ゾンビがいなければリターンが直に戻る)
5
1.3 AcceptTCPConnection.c
#include <stdio.h> /* printf()用 */ #include <sys/socket.h> /* accept()用 */ #include <arpa/inet.h> /* sockaddr_inとinet_ntoa()用 */ void DieWithError(char *errorMessage); /* エラー処理関数 */ int AcceptTCPConnection(int servSock) { int clntSock; /* クライアントソケット識別子 */ struct sockaddr_in echoClntAddr; /* クライアントアドレス */ unsigned int clntLen; /* クライアントのアドレス構造体の長さ */ /* 入出力パラメータのサイズを設定 */ clntLen = sizeof(echoClntAddr); /* クライアントからの接続待ち */ if ((clntSock = accept(servSock, (struct sockaddr *) &echoClntAddr, &clntLen)) < 0) DieWithError("accept() failed"); /* clntSockはクライアントに接続 */ printf("Handling client %s\n", inet_ntoa(echoClntAddr.sin_addr)); return clntSock; } cast
6
1.4 クライアント毎にスレッド作成 スレッドの利点 スレッドの欠点 TCPEchoServer-Thread.c
1.4 クライアント毎にスレッド作成 スレッドの利点 同一プロセスの中で複数のタスクを実行できるため、(プロセスの)コピー時間が不要である。 コンピュータ・リソース(メモリ、スタック、ファイル/ソケット識別子)の消費を少なく出来る。 スレッド間の情報交換が容易である。 スレッドの欠点 子プロセスの制御(kill等)はコマンドラインから可能であるが、スレッドの制御は出来ない。 TCPEchoServer-Thread.c プログラムの大部分はTCPEchoServer-Fork.cと同じ。 クライアント サーバ クライアント サーバ (接続要求) sock servSock connect(sock,…) accept(servSock,…) クライアント サーバ (接続) servSock clntSock sock pthread_create() クライアント サーバ (接続) servSock スレッド clntSock sock socketは複製されないのでclose()が不要
7
1.5 TCPEchoServer-Thread.c
#include “TCPEchoServer.h” /* TCP エコーサーバ用 */ #include <pthread.h> /* POSIXスレッド用 */ void *ThreadMain(void *arg); /* クライアントスレッドに渡す引数構造体 */ struct ThreadArgs { int clntSock; /* クライアントソケット識別子 */ }; int main(int argc, char *argv[]) int servSock; /* サーバ用ソケット識別子 */ int clntSock; /* クライアント用ソケット識別子 */ unsigned short echoServPort; /* サーバポート番号 */ pthread_t threadID; /* pthread_create()からのスレッドID */ struct ThreadArgs *threadArgs; /* 引数構造体ポインタ */ if (argc != 2) /* 引数の数が正しいかチェック */ fprintf(stderr,"Usage: %s <SERVER PORT>\n", argv[0]); exit(1); } /* 第一引数はローカルポート番号 */ echoServPort = atoi(argv[1]); servSock = CreateTCPServerSocket(echoServPort); for (;;) /* 永遠に繰返す */ { clntSock = AcceptTCPConnection(servSock); /* クライアント引数用にメモリを確保 */ if ((threadArgs = (struct ThreadArgs *) malloc(sizeof(struct ThreadArgs))) == NULL) DieWithError("malloc() failed"); threadArgs -> clntSock = clntSock; /* クライアントスレッド作成 */ if (pthread_create(&threadID, NULL, ThreadMain, (void *) threadArgs) != 0) DieWithError("pthread_create() failed"); printf("with thread %ld\n", (long int) threadID); } /* この部分には到達しない */ void *ThreadMain(void *threadArgs) int clntSock; /* クライアントのソケット識別子 */ /* 戻り時、スレッドリソースの開放 */ pthread_detach(pthread_self()); /* ソケットのファイル記述子を引数から取り出す */ clntSock = ((struct ThreadArgs *) threadArgs) -> clntSock; free(threadArgs); /* 引数に割当てられたメモリ開放 */ HandleTCPClient(clntSock); return (NULL); 動的メモリ割当(予めサイズが予測出来ない場合、要求発生時にメモリ確保) 自身のスレッドIDを得る pthread_join()しなくても終了時リソース開放 malloc()で確保したメモリ領域を解放する サーバソケットを作成し接続要求待ち(listen())までを行う関数
8
1.6 制限付マルチタスク プロセス(スレッド)数の制限 制限付マルチタスクサーバ
1.6 制限付マルチタスク サーバへのアクセス急増やDoS攻撃に備える プロセス(スレッド)数の制限 プロセス(スレッド)数の増加によるOS負荷(プロセス間スケジューリングやコンテキストスイッチ)の増加を抑える。 制限付マルチタスクサーバ 接続待ちサーバプロセスを設定された数だけ予め作る。 処理中でないプロセスがaccept()を実行し接続を確立する。 各プロセスはクライアントからの要求を処理し続ける(プロセスの生成と削除は発生ない)。 サーバ 親 子1 子2 カーネル クライアント fork() fork() exit() connect() accept() connect() 全ての子プロセスが処理中の場合accept()しない。一つの子プロセスの処理が終了した時点でaccept()する。 accept() connect() close() close() accept()
9
1.7 TCPEchoServer-ForkN.c
#include "TCPEchoServer.h" void ProcessMain(int servSock); /* 子のメインプログラム */ int main(int argc, char *argv[]) { int servSock; /* サーバ用ソケット記述子*/ unsigned short echoServPort; /* サーバのポート番号 */ pid_t processID; /* プロセスID */ unsigned int processLimit; /* 作成するプロセス数 */ unsigned int processCt; /* プロセスカウンタ */ echoServPort = atoi(argv[1]); /* 第1引数はローカルポート */ processLimit = atoi(argv[2]); /* 第2引数は子プロセス数*/ servSock = CreateTCPServerSocket(echoServPort); for (processCt=0; processCt < processLimit; processCt++) /* 子プロセスのforkとエラー報告 */ if ((processID = fork()) < 0) DieWithError("fork() failed"); else if (processID == 0) /* 子プロセスの場合 */ ProcessMain(servSock); exit(0); } void ProcessMain(int servSock) { int clntSock;/* クライアントコネクションのソケット記述子 */ for (;;) /* 永遠に繰返す */ clntSock = AcceptTCPConnection(servSock); printf("with child process: %d\n", (unsigned int) getpid()); HandleTCPClient(clntSock); } 次のconnect要求 を受け付ける エコー処理を実施する 処理中に到着するconnect要求は他のプロセスが処理するか待たせる 作成する子の数はprocessLimit 親は子を作成して終了する
10
宿題14 子1 子2 クライアント カーネル hw14.c :TCPEchoServer-ForkN.cの中のHandleTCPClient(clntSock);の直後にsleep(10);を加えたプログラム。 作成する子プロセス数を2個としてhw14を実行し、TCPEchoClientから連続3回エコー要求を出した場合、エコーの戻り時間がどのようになるか観察せよ。 psコマンドを使ってプロセス数を観測せよ。 表紙に氏名と学籍番号を書く 本文にプログラムを書く プログラムの実行結果を書く 実施した内容を説明する文章を書く レポートの締切は次の週の水曜日18:00 connect() accept() 受信データ=0 TCP close sleep(10) connect() accept() connect() 待ち行列に並んで待つ 10秒スリープ 待ち行列中の 接続要求を 子1が受付ける accept()
Similar presentations
© 2024 slidesplayer.net Inc.
All rights reserved.