Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Server dies on `send` if client was closed with Ctrl+c

Tags:

c

sockets

send

I cannot understand why this application dies on send. Here is the code for the server:

#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");
}

Unfortunately, if the client was forcefully closed using Ctrl+C as soon as it has sent the message, server will print (as last line):

before send

And then it dies without doing anything else. I tried to check errno or whatever else (including the call to send in an if statement), but it seems that it's send itself to cause the server to die. I've tryed with a write but it's the same.

I checked it's return code with bash, and it exits with return code 141, which I was unable to understand what it means.

If you want to try, here is the client code:

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

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

int main() {
    int sfd, rv;
    char b[100];
    char ok[3];
    struct sockaddr_un sa;

    sfd = socket(AF_UNIX, SOCK_STREAM, 0);

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

    connect(sfd, (struct sockaddr*)&sa, sizeof(sa));

    while (scanf("%s", b)) {
        send(sfd, b, 100, 0);

        recv(sfd, ok, 3, 0);

        printf("%s\n", ok);
    }

    printf("END\n");
}

Just compile both, run both and then kill the client with Ctrl+C just after the message has been sent.

like image 628
Zagorax Avatar asked Dec 09 '22 14:12

Zagorax


1 Answers

When you call send() on an already closed connection, the operating system may issue SIGPIPE to your process. Usually the default handler for SIGPIPE is to kill your process.

In order to prevent this from occurring, you can either ignore the signal (e.g., with signal(SIGPIPE, SIG_IGN) or sigignore(SIGPIPE)), or you can pass the MSG_NOSIGNAL option to send():

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... */
    }
}

Now, instead of issuing SIGPIPE, the send() call will fail, and errno will be set to EPIPE.

like image 68
jxh Avatar answered Dec 11 '22 09:12

jxh