【问题标题】:writing to a close socket didn't raise a SIGPIPE as expected写入关闭的套接字没有按预期引发 SIGPIPE
【发布时间】:2014-05-28 12:42:36
【问题描述】:

我已经阅读了如何防止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() 返回写入的字节数,而我期望 SIGPIPE 信号,因为我关闭了客户端的套接字,第二个write() 确实会生成SIGPIPE 信号。
但是当我在另一台 linux 机器或 Windows 机器上运行客户端时(使用 Winsock 实现相同的客户端),我没有捕捉到任何 SIGPIPE 信号,第二个 write() 仍然返回 buffer 的大小.谁能告诉我这是怎么回事?

【问题讨论】:

  • 严格来说,在信号处理程序中调用printf() 是不安全的。有一个安全函数列表,printf() 不在列表中。我怀疑这是主要问题,但请记住未来。您没有设置结构的 sa_mask 成员;由于您没有在信号处理程序中使用额外信息,因此不应使用 SA_SIGINFO。
  • @JonathanLeffler 感谢您的提示。我使用sigaction 只是为了测试是否生成了SIGPIPE 信号,所以不要注意到printf 不在我们可以安全地从信号处理程序调用的函数列表中。
  • Windows 上没有信号管
  • @zoska 服务器始终运行在 linux 上,只有客户端可以运行在 Windows 上。

标签: c sockets network-programming sigpipe


【解决方案1】:

它不会在第一次写入时发生,原因有两个:

  1. 本地主机不知道对等方已关闭套接字以进行读取。已收到 FIN,但这可能只是因为对等方已关闭输出。只有 RST 会告诉它,而且它最早不会在下一次 I/O 中使用该工具。
  2. 缓冲。

注意,您通过调用 perror(), 破坏了 errno 的值,因此之后对其进行测试无效。

【讨论】:

  • 我使用 tcpdump 捕获客户端和服务器之间的数据包,RST 确实在第一个 write 之后发送到服务器。那我就想知道为什么服务端和客户端在不同机器上运行时没有SIGPIPE信号?
  • 你的意思是在同一个测试中有一个 RST 但没有 SIGPIPE?
  • 是的。当 C&S 在不同的机器上运行时,没有 SIGPIPERST
  • 所以 RST 一定是在 write()-s 返回之后。缓冲可以解释这一点。
【解决方案2】:

只需在 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);

【讨论】:

    猜你喜欢
    • 2011-08-05
    • 1970-01-01
    • 2012-02-08
    • 1970-01-01
    • 2017-12-25
    • 2021-06-12
    • 2016-06-19
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多