【C】ソケットAPIを用いたデータロガープログラムを書いてみる【socket】

こんにちは。
TCP/IP でのソケットを用いたデータ通信の仕組みに関して改めて勉強してみたので、自分自身の理解のために簡単なデータロガープログラムを書いてみました。
※ いつも通りエラーハンドリングは 無視 省力です。

client.c

まずはクライアント側ですね。
socket() でソケットを作成した後に、connect() を使えばコネクションが張れるようです。
後は生成したソケットを使って、send() でサーバ側にデータを送るだけです。
送信するデータは fgets() で標準入力から取得します。

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

int main(void){
    int sockfd;
    char mes[64];
    struct sockaddr_in server;

    server.sin_family = AF_INET;
    server.sin_port = htons(8888);
    server.sin_addr.s_addr = inet_addr("127.0.0.1");

    sockfd = socket(AF_INET, SOCK_STREAM, 0);

    connect(sockfd, (struct sockaddr *)&server, sizeof(server));

    printf("INPUT:");
    fgets(mes, sizeof(mes), stdin);

    send(sockfd, mes, 64, 0);

    close(sockfd);

    return 0;
}

接続先サーバの情報は sockaddr_in 構造体を使って socket に紐づけます。
※ sockaddr にキャストする必要アリです。
例では localhost の 8888 ポートに接続しています。

server.c

次はサーバ側ですね。
サーバは bind() を使って ソケットに IPアドレス/ポート番号を紐づけ、listen() で割り当てたポート番号に接続を作成できることをシステムに伝えます。
※ 例では最大5つの接続を受け持ちます。( 後段の処理が完了するまで最大5つまでの接続要求がキューに入ります )
実際のデータの送受信にはこのソケットは用いず、accept() を使ってデータ送受信用のソケットを生成して処理を委任します。
後は recv() を使ってデータを受信し、ファイルにデータを書き込み データ送受信用のソケットをクローズします。
※ 36行目には到達しないです。

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

int main(void){
    int sock, sockfd, sock_size;
    char mes[64];
    struct sockaddr_in server;
    struct sockaddr_in client;
    FILE *file;

    server.sin_family = AF_INET;
    server.sin_port = htons(8888);
    server.sin_addr.s_addr = INADDR_ANY;

    sockfd = socket(AF_INET, SOCK_STREAM, 0);

    bind(sockfd, (struct sockaddr *)&server, sizeof(server));

    listen(sockfd, 5);

    while(1){
        sock_size = sizeof(client);
        sock = accept(sockfd, (struct sockaddr *)&client, &sock_size);

        recv(sock, mes, 64, 0);
        file = fopen("loger.txt", "a");
        fprintf(file, mes);
        fclose(file);
        
        close(sock);
    }
    close(sockfd);
    return 0;
}

クライアントと同じく、受け持つIPアドレス/ポート番号を sockaddr_in 構造体を使って socket に紐づけます。
こちらも sockaddr にキャストする必要アリです。
※ INADDR_ANY は全ての接続元( 0.0.0.0 )を表しています。

まとめると下記の形になりますね。

1.socket() を実行して TCPソケットを作成する。
2.bind() を実行してソケットにポート番号(IPアドレスも)を割り当てる。
3.listen() を実行し割り当てたポート番号へ接続を作成できることをシステムに伝える。
4.以下繰り返し
・接続要求を受け取るたびに、accept() を呼び出して新規ソケットを取得
・作成したソケットを介してクライアント(接続要求元)とやり取り
・close() でクライアントとの接続をクローズ

【C言語】単純なTCPサーバをdemontoolsでデーモン化する【demontools】

C 言語で単純な TCP サーバを作って demontools でデーモン化してみます。
※ demontools を使う機会があったので備忘録を兼ねています。

TCPサーバ

#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>

int main(){
        int sockfd, sock_size, sock;
        struct sockaddr_in server;
        struct sockaddr_in client;

        /* ソケット作成 */
        sockfd = socket(AF_INET, SOCK_STREAM, 0);

        server.sin_family = AF_INET;
        server.sin_port = htons(8888);
        server.sin_addr.s_addr = INADDR_ANY;

        /* ソケットにポート番号割り当て */
        bind(sockfd, (struct sockaddr *)&server, sizeof(server));

        /* 割り当てたポート番号へ接続が作成できることをシステムに伝える */
        listen(sockfd, 5);

        /* 接続要求を受ける度にソケットを新しく取得 */
        while(1){
                sock_size = sizeof(client);
                sock = accept(sockfd, (struct sockaddr *)&client, &sock_size);
                write(sock, "TEST\n", 5);
                /* クライアントとの接続をクローズ */
                close(sock);
        }
        close(sockfd);
        return 0;
}

エラー処理は省いています。
適当なディレクトリにコンパイルしてください。
# ディレクトリ移動
cd /usr/local/bin
vi main.c
===================
*上記コード
===================
# コンパイル
gcc main.c -o simple-server

demontools

demontools の設定です。

# demontoolsの導入
mkdir -p /package
chmod 1755 /package
cd /package
wget http://cr.yp.to/daemontools/daemontools-0.76.tar.gz
gunzip daemontools-0.76.tar
tar -xpf daemontools-0.76.tar
rm -f daemontools-0.76.tar
cd admin/daemontools-0.76
cd src/

# ヘッダファイル修正
vi error.h
=======================
extern int errno;

↓

#include <errno.h>
=======================

# インストール
cd ..
package/install

# centos7 では使用しないのでコメントアウト
vi /etc/inittab
=======================
#SV:123456:respawn:/command/svscanboot
=======================

# サービスファイル作成
vi /etc/systemd/system/demontools.service
======================================================
[Unit]
Description=run demontools during boot
After=network.target

[Service]
User=root
Group=root
Type=simple
RemainAfterExit=yes
ExecStart=/command/svscanboot
Restart=always
LimitNOFILE=20480

[Install]
WantedBy=multi-user.target
======================================================


# プロセスのデーモン化
mkdir /service/simple-server/
vi /service/simple-server/run
======================================================
#!/bin/sh

exec /usr/local/bin/simple-server
======================================================

# ログ出力設定
mkdir /service/simple-server/log/
mkdir /var/log/simple-server
vi /service/simple-server/log/run
==============================================
#!/bin/sh

exec /usr/local/bin/multilog t /var/log/simple-server
==============================================

# 権限変更
chmod 755 /service/simple-server/run
chmod 755 /service/simple-server/log/run

# demontools 起動/自動起動設定
systemctl start demontools
systemctl enable demontools