1

を防ぐ方法についてはすでに読んだのでSIGPIPE、それをテストする小さなプログラムを書きます。これがコードです。

server.c

#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <fcntl.h>
#include <errno.h>
#include <signal.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>

void hdl(int sig_num, siginfo_t *sig_info, void *context)
{
    printf("got you, SIGPIPE!\n");
}

int main()
{
    int sfd, cfd;
    struct sockaddr_in saddr, caddr;


    struct sigaction act;

    memset (&act, '\0', sizeof(act));
    act.sa_sigaction = hdl;
    act.sa_flags = SA_SIGINFO;

    if (sigaction(SIGPIPE, &act, NULL) < 0) {
        return 1;
    }

    sfd= socket(AF_INET, SOCK_STREAM, 0);
    saddr.sin_family=AF_INET;
    saddr.sin_addr.s_addr=inet_addr("192.168.22.91");
    saddr.sin_port=htons(12345);

    if(bind(sfd, (struct sockaddr *)&saddr, sizeof(saddr)) )
    {
        printf("bind error\n");
        return -1;
    }
    if(listen(sfd, 1))
    {
        printf("error\n");
        return -1;
    }

    char buf[1024] = {0};
    while(1) {
            printf("Server listening...\n");
            cfd=accept(sfd, (struct sockaddr *)NULL, NULL);

            fcntl(cfd, F_SETFL, O_NONBLOCK);
            int size = read(cfd, buf, 1024);

            if(size == -1)
                printf("read error\n");

            sleep(2); // sleep for a while to make sure the client closed the socket

            int ret;
            if((ret = write(cfd, buf, strlen(buf)))<0)
            {
                if(errno == EPIPE)
                    fprintf(stderr, "SIGPIPE");
            }

            ret = write(cfd, buf, strlen(buf)); // write again.
            printf("write return %d\n", ret);
    }
    close(sfd);
}

client.c

#include <stdio.h>
#include <string.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <stdlib.h>
#include <assert.h>


int main()
{
    int ret, fd;
    struct sockaddr_in sa_dst;
    char buffer[] = "hello, world";
    char rcv_buf[128] = {0};

    fd = socket(AF_INET, SOCK_STREAM, 0);

    memset(&sa_dst, 0, sizeof(struct sockaddr_in));
    sa_dst.sin_family = AF_INET;
    sa_dst.sin_port = htons(12345);
    sa_dst.sin_addr.s_addr = inet_addr("192.168.22.91"); 

    ret = connect(fd, (struct sockaddr *)&sa_dst, sizeof(struct sockaddr));
    if(ret != -1)
    {
        send(fd, buffer, strlen(buffer), 0);
        close(fd);
    }
    return 0;
}

サーバーとクライアントを同じ Linux マシンで実行すると、サーバー側で、クライアント側でソケットを閉じたためにシグナルwrite()を期待している間に最初に書き込まれたバイト数が返され、2 番目にシグナルが生成されます。 しかし、別の Linux マシンまたは Windows マシン (Winsock で同じクライアントを実装) でクライアントを実行したとき、シグナルをキャッチせず、2 番目はまだ. 誰かが私に何が起こっているのか教えてもらえますか?SIGPIPEwrite()SIGPIPE
SIGPIPEwrite()buffer

4

2 に答える 2

1

次の 2 つの理由から、最初の書き込みでは発生しません。

  1. localhost は、ピアが読み取りのためにソケットを閉じたことを知りません。FIN を受信しましたが、ピアが出力のためにシャットダウンしたことが原因である可能性があります。それを伝えるのは RST だけであり、次の I/O までにはそれを取得しません。
  2. バッファリング。

NB をerrno呼び出して値を破損しているperror(),ため、後でテストすることはできません。

于 2014-05-28T22:19:35.857 に答える
0

SERVERでこれを変更するだけで機能します

        fcntl(cfd, F_SETFL, O_NONBLOCK);
        int size = read(cfd, buf, 1024);

        if(size == -1)
            printf("read error\n");

        sleep(2); // sleep for a while to make sure the client closed the socket

        int ret;
           ret = write(cfd, buf, strlen(buf));
         sleep(2);
        ret = write(cfd, buf, strlen(buf)); // write again.
        printf("write return %d\n", ret);
于 2015-02-03T13:37:04.050 に答える