サーバの仕組みとスレッド
通常、Webサーバなどは親プロセスがポートをListenし、接続があったら、子プロセスを生成し、その後の(クライアントへのデータ送信等の)処理は、子プロセスに任せるというフローをとる。
そうすることで、親プロセスはクライアントへデータ送信が終わるのを待つ必要がなくなり、どんどん接続を受けることが可能になる。
接続数が多くなると、当然、子プロセスが多くなり、サーバへの過負荷となる。
そこで、子プロセスではなく、スレッドでクライアントの処理を行うと負荷低減につながる((スレッドの生成は子プロセスの生成より軽量))
echoサーバのPOSIXスレッド化
[C言語]Socket間通信 echoサーバを作るで作成したechoサーバにptheadを組み込み、複数クライアントに対して同時処理ができるように改良する。POSIXスレッドについて知りたければ、まずは[C言語]POSIXスレッドプログラミングを参照。
課題
- 1回の通信が終わっても、Listenし続けるように変更する
- threadで返答を返すよう変更する
コード
#include <sys/types.h>
#include <sys/socket.h> /* socket(), bind(), listen(), accept(), recv() */
#include <netinet/in.h> /* htons() */
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h> /* exit() */
#include <pthread.h> /* pthread_create(), pthread_detach() */
#define PORT 8823 /* Listenするポート */
#define MAXDATA 1024 /* 受信バッファサイズ */
void reply(void *);
int main(void)
{
struct sockaddr_in saddr;
struct sockaddr_in caddr;
int listen_fd;
int conn_fd;
int len = sizeof(struct sockaddr_in);
pthread_t worker;
/* ソケットの生成 */
if ((listen_fd = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
perror("socket");
exit(EXIT_FAILURE);
}
/*
* saddrの中身を0にしておかないと、bind()でエラーが起こることがある
*/
bzero((char *)&saddr, sizeof(saddr));
/* ソケットにアドレスとポートを結びつける */
saddr.sin_family = PF_INET;
saddr.sin_addr.s_addr = INADDR_ANY;
saddr.sin_port = htons(PORT);
if (bind(listen_fd, (struct sockaddr *)&saddr, len) < 0) {
perror("bind");
exit(EXIT_FAILURE);
}
/* ポートをListenする */
if (listen(listen_fd, SOMAXCONN) < 0) {
perror("listen");
exit(EXIT_FAILURE);
}
printf("Start Listening Port : %d...\n", PORT);
while (1) {
/* 接続要求を受け付ける */
if ((conn_fd = accept(listen_fd, (struct sockaddr *)&caddr, &len)) < 0) {
perror("accept");
exit(EXIT_FAILURE);
}
/* スレッドの生成 */
if (pthread_create( &worker, NULL, (void *)reply, (void *)conn_fd) != 0) {
perror("pthread_create");
exit(EXIT_FAILURE);
}
pthread_detach(worker);
}
/* Listeningソケットを閉じる */
close(listen_fd);
}
void reply(void *fd) {
int conn_fd = (int)fd;
int rsize;
char buf[MAXDATA]; /* 受信バッファ */
/* 送信されたデータの読み出し */
do {
rsize = recv(conn_fd, buf, MAXDATA, 0);
if (rsize == 0) { /* クラアイントが接続を切ったとき */
break;
} else if (rsize == -1) {
perror("recv");
exit(EXIT_FAILURE);
} else {
write(conn_fd, buf, rsize);
}
} while (1);
if ( close(conn_fd) < 0) {
perror("close");
exit(EXIT_FAILURE);
}
printf("Connection closed.\n");
}
ポイント
- スレッドにする部分を関数にする
- 生成したスレッドをdetach状態にする
- while(1)ループでListenし続ける
参考文献
![]() マスタリングTCP/IP 入門編 |
![]() マスタリングTCP/IP 応用編 |
![]() マルチコアCPUのための並列プログラミング |
参考URL
Pthreadによる複数クライアントに対するサービスの同時提供
http://www.coins.tsukuba.ac.jp/~yas/coins/syspro-2004/2004-05-10/echo-server-pthread.html
4.2 どうしてソケットがクローズしてくれないのでしょうか?
http://www.kt.rim.or.jp/~ksk/sock-faq/unix-socket-faq-ja-4.html#ss4.2
構築環境
FreeBSD 4.10-RELEASE


