シンプルなエコーサーバー
複数のクライアントに同時対応します。
「シンプルなエコーサーバー」はCoddyKit上の無料C Academyレッスンです。 これはレッスン4/4です。 下記で完全なレッスンを無料で読むことができます。その後、ブラウザ内の組み込みコードエディタと24時間対応のAIチューターでハンズオン演習できます。 これはC Academy学習パスの一部であり、ウェブとCoddyKitアプリ全体で進捗が同期されます。 C Academyコースには全4レッスンが含まれています。
作成するもの
エコーサーバーはTCP接続を受け付け、クライアントが送信したバイト列をそのまま送り返します。ネットワークプログラミングにおける「Hello World」のようなものです。
epollを使って構築するため、1つのスレッドで多数のクライアントを同時に処理できます。構成要素は、接続待ちソケット、epollループ、そしてクライアントごとの読み取り後の書き込み処理です。
接続待ちソケットの作成
まずsocket()でIPv4 TCPエンドポイントを作成します。指定する3つの値は、AF_INET、SOCK_STREAM、プロトコル0です。
これによりファイルディスクリプタが返されます。これをbindし、ノンブロッキングに設定して、最終的にepollへ登録します。すべてのシステムコールで-1を確認してください。
int listen_fd = socket(AF_INET, SOCK_STREAM, 0);
if (listen_fd == -1) { perror("socket"); exit(1); }アドレスの再利用
bindする前に、setsockopt()でSO_REUSEADDRを設定します。これにより、TIME_WAIT状態が終わるのを待たずに、サーバーを再起動して同じポートに再bindできます。
これを設定しないと、すぐに再起動した際にEADDRINUSEで失敗することがよくあり、開発中に困惑する原因になります。
int yes = 1;
setsockopt(listen_fd, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof yes);ポートへのbind
struct sockaddr_inにアドレスファミリー、ポート、アドレスを設定します。ポートにはhtons()を使い、すべてのインターフェースで待ち受けるにはINADDR_ANYを使用します。
次にbind()を呼び出して、ソケットをそのアドレスに関連付けます。htonsはホストバイト順をネットワークバイト順に変換します。これは必須です。
struct sockaddr_in addr = {0};
addr.sin_family = AF_INET;
addr.sin_addr.s_addr = htonl(INADDR_ANY);
addr.sin_port = htons(8080);
bind(listen_fd, (struct sockaddr*)&addr, sizeof addr);listenとノンブロッキング設定
listen()を呼び出してソケットをパッシブにし、保留中の接続に対するバックログを設定します。SOMAXCONNを指定すると、システムの最大値を要求します。
次にソケットをノンブロッキングに切り替え、accept()がイベントループを停止させないようにします。これでepollに登録する準備が整います。
listen(listen_fd, SOMAXCONN);
set_nonblocking(listen_fd);接続待ちソケットの登録
epollインスタンスを作成し、接続待ちソケットをEPOLLINで追加します。ここで読み取り可能イベントが発生した場合、新しいクライアントの接続を受け付けられることを意味します。
data.fdにfdを格納し、ループ内で接続待ちソケットを識別できるようにします。
int epfd = epoll_create1(0);
struct epoll_event ev = { .events = EPOLLIN, .data.fd = listen_fd };
epoll_ctl(epfd, EPOLL_CTL_ADD, listen_fd, &ev);クライアントの接続受付
接続待ちソケットが読み取り可能になったら、accept()がEAGAINを返すまでループします。新しいクライアントソケットごとにノンブロッキングを設定し、EPOLLINで登録します。
これ以降、クライアントがエコー可能なデータを送信するたびに、epollが通知してくれます。
int c = accept(listen_fd, NULL, NULL);
if (c != -1) {
set_nonblocking(c);
struct epoll_event cev = { .events = EPOLLIN, .data.fd = c };
epoll_ctl(epfd, EPOLL_CTL_ADD, c, &cev);
}エコー処理のロジック
準備完了したクライアントから、recv()でバッファに読み込みます。バイト列を受け取ったら、同じバイト列をsend()ですぐに送り返します。
この単純な読み取り後の書き込みがエコー処理です。実運用では未送信データをバッファリングしますが、ここでは小さな応答が送信バッファに収まるものとします。
char buf[4096];
ssize_t r = recv(fd, buf, sizeof buf, 0);
if (r > 0) {
send(fd, buf, r, 0);
}切断の検出
recv()の戻り値が0の場合、クライアントが正常に接続を終了したことを意味します。EAGAIN以外の負の戻り値は実際のエラーです。
どちらの場合も、ディスクリプタをepollから削除し、close()してリソースを解放するとともに、それ以上イベントが発生しないようにします。
if (r == 0 || (r < 0 && errno != EAGAIN)) {
epoll_ctl(epfd, EPOLL_CTL_DEL, fd, NULL);
close(fd);
}完全なループ
各要素を組み合わせます。イベントを待ち、ディスクリプタが接続待ちソケットかクライアントかで分岐し、それぞれに応じて処理します。
このコンパクトなループだけで、完全に動作する並行エコーサーバーになります。1つのスレッドで多数のクライアントを処理し、接続ごとのスレッドは必要ありません。
for (;;) {
int n = epoll_wait(epfd, events, MAX_EVENTS, -1);
for (int i = 0; i < n; i++) {
if (events[i].data.fd == listen_fd) accept_clients();
else echo_or_close(events[i].data.fd);
}
}テストと次のステップ
Linuxでコンパイルし、nc localhost 8080で接続してください。入力した内容がそのまま返ってきます。複数のターミナルを開いて、並行処理を確認しましょう。
堅牢性を高めるには、部分的な送信をバッファリングし、EPOLLOUTを処理し、EPOLLETに切り替えて最後まで読み出し、ハーフクローズにはEPOLLRDHUPを処理します。
確認テスト
エコーサーバーの処理の流れを理解できているか確認しましょう。
復習
epollを使ったエコーサーバーを構築しました。ソケットを作成し、SO_REUSEADDRを設定して、bind、listenを行い、ノンブロッキングにします。リスナーを登録し、ループ内でクライアントを受け付け、recv()/send()でエコーを返します。
0が返された場合は切断として扱い、適切にクローズします。この基本構成をもとに、高性能なシングルスレッドのネットワークサーバーへ発展させられます。
よくある質問
「シンプルなエコーサーバー」レッスンは無料ですか?
はい。「シンプルなエコーサーバー」の完全なテキストはこのウェブで無料で読めます。インタラクティブに演習し(組み込みコードエディタと24時間対応のAIチューター)、C Academyコースの残りをアンロックするには、CoddyKit PROにアップグレードしてください。 C Academyコースには全4レッスンが含まれています。
「シンプルなエコーサーバー」で何を学びますか?
複数のクライアントに同時対応します。 ブラウザで直接実行するハンズオンコードでC Academyを演習し、24時間対応のAIチューターがレッスンを進める中での質問に答えます。
C Academyを始めるのに経験は必要ですか?
事前経験は必要ありません。CoddyKitのC Academyは初級者から上級者向けに構成されているため、ここから始めるか最初から始めて、自分のペースで進むことができます。 これはレッスン4/4です。
「シンプルなエコーサーバー」レッスンにはどのくらい時間がかかりますか?
ほとんどのCoddyKitレッスンは約5~10分かかります。各レッスンはコンパクトでインタラクティブなので、着実に進歩し、ウェブとアプリ全体で正確に前回の場所から再開できます。
このC Academyレッスンでコードを書いて実行できますか?
はい。すべてのC Academyレッスンに組み込みコードエディタが含まれているため、ブラウザでリアルコードを書いて実行し、即座のAIフィードバックを取得できます。ローカル設定は不要です。
このコースのすべてのレッスン
- ブロッキングI/OとノンブロッキングI/O
- epollをセットアップする
- イベントループ
- シンプルなエコーサーバー