如果客户端使用Ctrl+关闭,则服务器在“发送”时死亡;C

如果客户端使用Ctrl+关闭,则服务器在“发送”时死亡;C,c,sockets,send,C,Sockets,Send,我无法理解为什么此应用程序在send上死亡。以下是服务器的代码: #include <stdio.h> #include <sys/socket.h> #include <sys/types.h> #include <sys/un.h> #include <stdlib.h> #include <errno.h> #define UNIX_PATH_MAX 108 #define SPATH "./sock" in

我无法理解为什么此应用程序在
send
上死亡。以下是服务器的代码:

#include <stdio.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <sys/un.h>
#include <stdlib.h>
#include <errno.h>

#define UNIX_PATH_MAX    108
#define SPATH "./sock"

int main() {
    int sfd, rv = 100, newfd;
    char b[100];
    char ok[3] = "ok\0";
    struct sockaddr_un sa;

    sfd = socket(AF_UNIX, SOCK_STREAM, 0);

    strncpy(sa.sun_path, SPATH, UNIX_PATH_MAX);
    sa.sun_family = AF_UNIX;

    if (bind(sfd, (struct sockaddr*)&sa, sizeof(sa)) == -1) {
        perror("bind");
        exit(EXIT_FAILURE);
    }

    if (listen(sfd, SOMAXCONN) == -1) {
        perror("listen");
        exit(EXIT_FAILURE);
    }

    if ((newfd = accept(sfd, NULL, NULL)) == -1) {
        perror("accept");
        exit(EXIT_FAILURE);
    }

    while (rv != -1 && rv != 0) {
        rv = recv(newfd, b, 100, 0);

        printf("%s\n", b);

        sleep(3);

        printf("Send reply\n");
        send(newfd, ok, 3, 0);
        printf("Sent reply\n");
    }

    printf("END\n");
}
只需编译这两个命令,运行这两个命令,然后在消息发送后用Ctrl+C终止客户端。

当您在已关闭的连接上调用
send()
时,操作系统可能会向您的进程发出
SIGPIPE
。通常,
SIGPIPE
的默认处理程序是终止进程

为了防止这种情况发生,您可以忽略信号(例如,使用
signal(SIGPIPE,SIG\u IGN)
signore(SIGPIPE)
),或者将
MSG\u NOSIGNAL
选项传递到
send()

int send\u result=send(newfd,ok,3,MSG\u NOSIGNAL);
如果(发送结果>=0){
/*好的,但是检查你的数据是否都被发送了*/
如果(发送_结果<3)/*…做点什么*/;
}否则{
开关(错误号){
/* ... */
案例EPIPE:
/*正在通过关闭的连接发送*/
}
}

现在,不再发出
SIGPIPE
send()
调用将失败,
errno
将在服务器端设置为
EPIPE
,,检查客户端是否关闭了连接:

rv = recv(newfd, b, 100, 0);
if(rv<=0) {
        if(!rv) {
             fprintf(stderr, "conn. closed.");
        } else {
              perror("recv");
        }
        exit(EXIT_FAILURE);
}
rv=recv(newfd,b,100,0);

如果(rv),但这不是预期的吗?毕竟服务器写入数据的客户端不再可用。在服务器中,就在这段时间之后,为什么不检查recv()的返回值?同样在使用printf之前,必须nul终止新接收的字符串,例如,b[rev]=0没有人会“同时编译、同时运行,然后杀死客户端”。您需要在此处发布错误消息、堆栈跟踪等。这不是免费的二级帮助台,您也不会在某些供应商的代码中报告软件错误。-1当程序的退出状态>128时,意味着它是由于信号
status-128而被杀死的
。如果您查找Unix信号列表,您将看到13是
SIGPIPE
@EJP,可能没有人编译过它,但有些人已经阅读了整个问题,而不仅仅是最后一行。我已经发布了我得到的错误代码,他们甚至没有尝试运行代码就找到了解决方案。感谢您提供了关于如何在堆栈上提问的课程溢出,但不幸的是这不是我的疑问。干杯。
!rv
不正确。您必须检查
rv==0
以检测EOS,检查-1以检测错误。@EJP wait!,0求值为false,其他任何求值为TRUE,如果rv==0,则!rv将求值为TRUE,并将执行fprintf函数。星号CodingStyle explici我建议使用!variable而不是variable==0。你的-1太不正确了。如果服务器在客户端已被终止的情况下发送,这将无法防止观察到的崩溃发生。@Kira这是一个技巧,也是一个令人困惑的技巧,正如本文讨论所示。返回值记录为-1、0或正整数,这就是问题所在您应该进行测试。您引用的编码风格中没有这样的建议。@EJP我为错误的引用感到抱歉;我的意思是,人为错误,Mozilla编码风格,部分:C/C++实践。这里:非常容易检查。“…使用(x)或(!x)代替…”没有必要使用错误,手册页明确地说:“当对等方执行有序关机时,返回值将为0。”“因此,这甚至可能不是一个便携式解决方案,测试EPIPE。如果结果小于
0
,我们将检查
errno
,指示错误。
0
有序关机返回值为
recv()
。我没有使用过
EPIPE
检查
send()
错误的系统(Solaris、Linux、BSD、Winsock).Richard Stevens-Unix网络编程第三版,第5章,第5.3节TCP Echo Server。作者建议测试0而不是EPIPE,事实上我找不到任何指向EPIPE使用的证据。但当然,你的答案似乎是正确的。但是,我再次看到没有证据表明使用EPIPE而不是检查0。你看到图了吗ure 5.14 tcpcliserv/str_cli11.c第10行,Richard正在测试0。
if(Readline(sockfd,recvline,MAXLINE)==0)
。非常感谢。
MSG_NOSIGNAL
解决了问题。现在我可以正确捕捉错误。
int send_result = send(newfd, ok, 3, MSG_NOSIGNAL);
if (send_result >= 0) {
    /* okay, but check if all your data got sent! */
    if (send_result < 3) /* ...do something */;
} else {
    switch (errno) {
    /* ... */
    case EPIPE:
        /* sending on a closed connection... */
    }
}
rv = recv(newfd, b, 100, 0);
if(rv<=0) {
        if(!rv) {
             fprintf(stderr, "conn. closed.");
        } else {
              perror("recv");
        }
        exit(EXIT_FAILURE);
}