ソケットペアの反対側の端が閉じたときにどのように通知を受け取りますか?

ソケットペアの反対側の端が閉じたときにどのように通知を受け取りますか?

Unixドメインソケット(IPCソケットとも呼ばれます)を介して子プロセス(サーバー)と通信する親プロセス(クライアント)があります。

ソケットはsocketpair()データグラムタイプを使用して生成されます。

私はposix_spawn()子プロセスを開始し、親 - 子close()プロセスで不要なソケットペアの終わりを開始するために使用します。

子供たちと一緒に使用poll()してrecv()

これはすべて良いです。

これで、クライアントがソケットの終わりを閉じるか、クライアントが終了したときに子(サーバー)に通知を渡したいと思います。

POLLHUP、またはPOLLERRイベントを受け取ることを望みましたclose()が、何も得られませんでした。

開いている2つのプロセスをリストするUnixドメインソケットを使用すると、lsof -Uソケットのもう一方の端はnoneクライアントがシャットダウンした後であることがわかります。

重要な場合、これはmacOSにあります。

私は何を見逃していますか?クライアントがUnixドメインソケットを閉じたときにどのように通知されますか?

ベストアンサー1

あなたの説明に基づいてサンプルアプリを設定しましたが、いくつかの違いがあります。まず、SOCK_STREAM代わりに使用しましたSOCK_DGRAM。これは、POLLHUPソケットが接続指向のSOCK_DGRAMソケットではなくなることを意味します。使用する理由がありますかSOCK_DGRAM

この例と説明する例の2番目の小さな違いは、fork()私がposix_spawn()fork()

#include <poll.h>
#include <stdio.h>
#include <sys/socket.h>
#include <unistd.h>

int main(void)
{
    int sockets[2] = {};

    if (socketpair(AF_UNIX, SOCK_STREAM, 0, sockets) < 0) {
        perror("socketpair");
        return 1;
    }

    const pid_t pid = fork();
    if (pid < 0) {
        perror("fork");
        return 2;
    }

    if (pid > 0) {
        /* parent */
        close(sockets[0]);
        sockets[0] = 0;

        for (int i = 0; i < 3; ++i) {
            write(sockets[1], "hello", sizeof("hello"));
            sleep(2);
        }

        /* sockets[1] will get closed on parent termination */
        return 0;
    }

    /* child */
    close(sockets[1]);
    sockets[1] = 0;

    struct pollfd fds = {
        .fd = sockets[0],
        .events = POLLIN | POLLHUP,
    };

    while (poll(&fds, 1, 10 * 1000) > 0) {
        if (fds.revents & POLLHUP) {
            printf("--- Received hangup\n");
            break;
        }
        if (fds.revents & POLLERR) {
            printf("!!! Received error\n");
            break;
        }
        if (fds.revents & POLLIN) {
            char buffer[32] = {};

            if (recv(sockets[0], buffer, sizeof buffer, 0) < 0) {
                perror("recv");
                return 3;
            }

            printf("--> Received message '%s'\n", buffer);
        }
    }

    /* sockets[0] will get closed on child termination */
    return 0;
}

このプログラムでは、親は子にメッセージを書き込みます。子プロセスはこれらのメッセージを受け取り、処理します(ここでは標準出力として印刷します)。親プロセスは3つのメッセージの送信後に終了します。

このプログラムを実行すると、あなたが探していると思われる動作が表示されます。

$ ./a.out
--> Received message 'hello'
--> Received message 'hello'
--> Received message 'hello'
--- Received hangup

必ず使用する必要がある場合は、.SOCK_DGRAMへの呼び出しはpoll()最終的にタイムアウトします(例ではタイムアウトは10秒でした)。イベントのクライアントプロセスを終了できます。

おすすめ記事