C言語

【C言語】第10章第7回:ネットワークチャットプログラム

本記事では、C言語を用いて基本的なネットワークチャットプログラムを作成します。ソケット通信の基本概念や、サーバーとクライアントの実装方法を学びます。

0. 記事の概要

この記事を読むメリット

  • ネットワークプログラミングの基礎理解:ソケット通信を用いた基本的なクライアントサーバーモデルを学べます。
  • 通信プログラムの実践的知識:データ送受信の仕組みや、エラー処理の重要性を理解します。
  • 応用力の向上:チャットプログラムを基に、他のネットワークアプリケーションの構築が可能です。

この記事で学べること

  • ソケット通信の基本概念
  • サーバーとクライアントの実装
  • データ送受信の方法

1. ネットワークチャットプログラムの基本設計

1.1 ソケット通信とは?

ソケット通信は、ネットワーク間でデータを送受信するための仕組みです。サーバーとクライアントの役割があり、以下の手順で通信が行われます。

  1. サーバーがソケットを作成してリッスン状態になる。
  2. クライアントがサーバーに接続要求を送る。
  3. 接続が確立されると、データの送受信が可能になる。

1.2 必要なプログラム構造

ネットワークチャットプログラムは、以下の部分で構成されます。

  • サーバー側の実装(リッスンと接続処理)
  • クライアント側の実装(サーバーへの接続処理)
  • データ送受信の処理

2. コード例と詳細解説

2.1 サーバー側コード例

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <arpa/inet.h>

#define PORT 8080
#define BUFFER_SIZE 1024

int main() {
    int server_fd, new_socket;
    struct sockaddr_in address;
    int addrlen = sizeof(address);
    char buffer[BUFFER_SIZE] = {0};

    if ((server_fd = socket(AF_INET, SOCK_STREAM, 0)) == 0) {
        perror("ソケットの作成に失敗しました");
        exit(EXIT_FAILURE);
    }

    address.sin_family = AF_INET;
    address.sin_addr.s_addr = INADDR_ANY;
    address.sin_port = htons(PORT);

    if (bind(server_fd, (struct sockaddr *)&address, sizeof(address)) < 0) {
        perror("バインドに失敗しました");
        close(server_fd);
        exit(EXIT_FAILURE);
    }

    if (listen(server_fd, 3) < 0) {
        perror("リッスンに失敗しました");
        close(server_fd);
        exit(EXIT_FAILURE);
    }

    printf("サーバーがポート%dでリッスン中...\n", PORT);

    if ((new_socket = accept(server_fd, (struct sockaddr *)&address, (socklen_t*)&addrlen)) < 0) {
        perror("接続の受け入れに失敗しました");
        close(server_fd);
        exit(EXIT_FAILURE);
    }

    while (1) {
        memset(buffer, 0, BUFFER_SIZE);
        int valread = read(new_socket, buffer, BUFFER_SIZE);
        if (valread <= 0) break;

        printf("クライアント: %s\n", buffer);
        printf("メッセージを入力してください: ");
        fgets(buffer, BUFFER_SIZE, stdin);
        send(new_socket, buffer, strlen(buffer), 0);
    }

    close(new_socket);
    close(server_fd);
    return 0;
}

2.2 クライアント側コード例

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <arpa/inet.h>

#define PORT 8080
#define BUFFER_SIZE 1024

int main() {
    int sock = 0;
    struct sockaddr_in serv_addr;
    char buffer[BUFFER_SIZE] = {0};

    if ((sock = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
        perror("ソケットの作成に失敗しました");
        exit(EXIT_FAILURE);
    }

    serv_addr.sin_family = AF_INET;
    serv_addr.sin_port = htons(PORT);

    if (inet_pton(AF_INET, "127.0.0.1", &serv_addr.sin_addr) <= 0) {
        perror("無効なアドレスです");
        exit(EXIT_FAILURE);
    }

    if (connect(sock, (struct sockaddr *)&serv_addr, sizeof(serv_addr)) < 0) {
        perror("接続に失敗しました");
        exit(EXIT_FAILURE);
    }

    while (1) {
        printf("メッセージを入力してください: ");
        fgets(buffer, BUFFER_SIZE, stdin);
        send(sock, buffer, strlen(buffer), 0);

        memset(buffer, 0, BUFFER_SIZE);
        int valread = read(sock, buffer, BUFFER_SIZE);
        if (valread <= 0) break;

        printf("サーバー: %s\n", buffer);
    }

    close(sock);
    return 0;
}
動作解説
  1. サーバー側:ソケットを作成し、指定したポートでリッスンします。接続を受け入れた後、データの送受信を処理します。
  2. クライアント側:サーバーに接続し、ユーザー入力を送信し、サーバーからの返信を受信します。
  3. データ送受信:`read`と`send`関数を使用してメッセージを交換します。

3. 練習問題

以下の課題に挑戦して、ネットワークチャットプログラムを拡張してみましょう。

  1. 複数のクライアントをサポートする機能を追加してください。
  2. メッセージにタイムスタンプを付加してください。
  3. メッセージ履歴をサーバー側でログファイルに保存してください。

4. 練習問題の解答と解説

問3の解答例

// メッセージ履歴の保存
void logMessage(const char *message) {
    FILE *logfile = fopen("chat.log", "a");
    if (logfile) {
        fprintf(logfile, "%s\n", message);
        fclose(logfile);
    }
}

この関数をサーバーのメッセージ処理部分に組み込むことで、すべてのメッセージが`chat.log`ファイルに記録されます。

5. まとめ

本記事では、C言語を用いてネットワークチャットプログラムを実装しました。さらに高度な機能を追加して、より実用的なアプリケーションに進化させてみましょう。