2

基本的にデータグラムを受信するための別のスレッドを生成するUDPクライアントの下に書いていますが、データグラムはメインスレッドでのみ送信されています。ここで、Linux ディストリビューション上の UDP クライアントのインスタンス化 ("./udpClient 1") 後に ctrl^D を押すと、実装がループから抜け出し (getline () 呼び出しの周り)、ソケット記述子を閉じます。

私が観察したことは、記述子で close を呼び出しても、クライアントプロセスが終了し、「netstat -an|grep udp」を実行すると関連するエントリが表示されるまで同じことが解放されないことです。ただし、受信側スレッドを生成するコードをコメントアウトまたは削除すると、close () を呼び出してソケット記述子がすぐに解放されます。

別のスレッド(基本的には、上記のソケット記述子を受信して​​ブロックする)を生成すると、ソケット参照カウントが増加しているように見えるため、 close () 呼び出しは何もしません。

ソケット記述子がプロセス間で共有されているがスレッド間では共有されていない場合、ソケット参照カウントが増加することを理解していたので、これは望ましいですか? これは、LWP としてのスレッドの Linux 実装によるものですか? もしそうなら、これは望ましい動作ですか?

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

void *receiver (void *arg);

int sockfd;

int doConnect = 0;

int
main (int argc, char *argv[])
{
    pthread_t thrid;

    struct sockaddr_in servaddr;

    struct sockaddr_in clntaddr;

    char *line;

    size_t len = 0;

    size_t read;

    if (argc < 4)
    {
        printf
            ("Usage:%s <Server Ipv4 address> <Server Port> <Connect Yes(1)/No(0)> [Client IPv4 address] [Client Port]\n",
             argv[0]);
        return -1;
    }


    /*
     * AF_INET, AF_INET6, AF_UNIX, AF_NETLINK
     * SOCK_STREAM (TCP), SOCK_DGRAM (UDP), SOCK_RAW
     */
    if ((sockfd = socket (AF_INET, SOCK_DGRAM, IPPROTO_UDP)) == -1)
    {
        printf ("Failed to create socket: %s\n", strerror (errno));
        return -1;
    }

    bzero (&clntaddr, sizeof (struct sockaddr_in));

    if (argc == 6)
    {
        printf ("doing bind......\n");
        clntaddr.sin_family = AF_INET;
        clntaddr.sin_port = htons (atoi (argv[5]));
        inet_aton (argv[4], &clntaddr.sin_addr);

        if (bind
            (sockfd, (struct sockaddr *) &clntaddr,
             sizeof (struct sockaddr_in)) == -1)
        {
            printf ("Failed to bind: %s\n", strerror (errno));
            close (sockfd);
            return -1;
        }
    }

    /*
     * fill up the server details
     */
    bzero (&servaddr, sizeof (struct sockaddr_in));

    servaddr.sin_family = AF_INET;
    servaddr.sin_port = htons (atoi (argv[2]));
    inet_aton (argv[1], &servaddr.sin_addr);

    doConnect = atoi (argv[3]);

    if (1 == doConnect)
    {
        printf ("doing connect......\n");
        if (-1 ==
            connect (sockfd, (struct sockaddr *) &servaddr,
                     sizeof (struct sockaddr_in)))
        {
            printf ("Failed to connect: %s\n", strerror (errno));
            close (sockfd);
            return -1;
        }
    }

    if (pthread_create (&thrid, NULL, receiver, NULL) < 0)
    {
        printf ("Failed to create thread: %s\n", strerror (errno));
        close (sockfd);
        return -1;
    }

    while ((read = getline (&line, &len, stdin)) != -1)
    {
        if (1 == doConnect)
        {
            if (send (sockfd, line, len, 0) < 0)
            {
                printf ("send () failed: %s\n", strerror (errno));
            }
        }
        else
        {
            if (sendto (sockfd, line, len, 0, (struct sockaddr *) &servaddr,
                        sizeof (struct sockaddr_in)) < 0)
            {
                printf ("sendto () failed: %s\n", strerror (errno));
            }
        }
    }

    if (line)
        free (line);

    close (sockfd);

    printf ("socket is closed.....\n");

    sleep (60);
}

void *
receiver (void *arg)
{
    char buff[512];

    while (1)
    {
        memset (buff, 0, sizeof (buff));

        if (1 == doConnect)
        {
            if (recv (sockfd, buff, sizeof (buff), 0) < 0)
            {
                // printf ("recvfrom () failed: %s\n", strerror (errno));
                printf ("recv () failed\n");
            }
            else
            {
                printf ("recv () returned: %s\n", buff);
            }
        }
        else
        {
            if (recvfrom (sockfd, buff, sizeof (buff), 0, NULL, NULL) < 0)
            {
                // printf ("recvfrom () failed: %s\n", strerror (errno));
                printf ("recvfrom () failed\n");
            }
            else
            {
                printf ("recvfrom () returned: %s\n", buff);
            }
        }
    }
}
4

1 に答える 1

3

ファイル記述子はプロセス グローバルであり、その参照カウントはプロセス グローバルです。私があなたのコードで読んでいることから最も可能性が高いのは、スレッドがまだrecvまたはrecvfromシステムコールが終了するまでカーネルがファイルを閉じないということです。そのファイルのすべてのシステムコールが終了するまで、ファイルを閉じることはできません (ファイル記述子スロットを解放することはできますが)。これは、非常に厄介なバグにつながります (過去にもありました)。

への呼び出しの前にスレッドを撃つcloseと、ファイル記述子が思いどおりに閉じることがわかります。

于 2013-01-08T10:45:02.037 に答える