5

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.

Zagorax
  • 11,440
  • 8
  • 44
  • 56
  • But isn't that what is expected? After all the client to where the server was writing the data is no available anymore. – MeTitus Jun 27 '13 at 00:33
  • In the server, right after the while, why you didn't check the return value of recv() ??, also before using printf, you must nul terminate your newly receive string, for instance, b[rev] = 0 – yeyo Jun 27 '13 at 00:36
  • http://stackoverflow.com/questions/14782143/linux-socket-how-to-detect-disconnected-network-in-a-client-program – MeTitus Jun 27 '13 at 00:40
  • 1
    Nobody is going to 'just compile both, run both, and then kill the client'. You're expected to post the error messages, stack traces, etc here. This is not a free 2nd level help desk and you are not reporting a software bug in some vendor's code. -1 – user207421 Jun 27 '13 at 00:46
  • 1
    When a program's exit status is > 128, it means it was killed due to a signal `status-128`. If you look up the Unix signal list, you'll see that 13 is `SIGPIPE`. – Barmar Jun 27 '13 at 00:50
  • 1
    @EJP, probably nobody had compiled it, but some people have read the whole question and not just the last line. I've posted the error code I get and they found a solution without even try to run the code. Thank you for your lesson on how to ask question on Stackoverflow, but unfortunately that was not my doubt. Cheers. – Zagorax Jun 27 '13 at 07:44

2 Answers2

16

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.

jxh
  • 69,070
  • 8
  • 110
  • 193
  • There is no need to use errno, man page explicity says: ""The return value will be 0 when the peer has performed an orderly shutdown."" so, this MIGHT not even be a portable solution, testing for EPIPE. – yeyo Jun 27 '13 at 00:46
  • 1
    We check `errno` if the result is less than `0`, indicating an error. The `0` return for orderly shutdown is with `recv()`. I haven't used a system where the `EPIPE` check was wrong for `send()` (Solaris, Linux, BSD, Winsock). – jxh Jun 27 '13 at 01:01
  • Richard Stevens - Unix network programming Third Edition, Chapter 5, section 5.3 TCP Echo Server. The author suggest testing for 0 and not EPIPE, in fact I was not able to find any evidence pointing the use of EPIPE. But of course, your answer seems to be correct. – yeyo Jun 27 '13 at 01:17
  • but again I see no evidence of using EPIPE insted of checking for 0. Did you see Figure 5.14 tcpcliserv/str_cli11.c line 10, Richard is testing for 0. `if (Readline(sockfd, recvline, MAXLINE) == 0)`. – yeyo Jun 27 '13 at 01:29
  • Thank you very much. `MSG_NOSIGNAL` solved the problem. Now I can catch the error correctly. – Zagorax Jun 27 '13 at 07:47
  • @Kira: My last message may have been too terse. The code you cite is not dealing with `send()`, but for `recv()` (suggested by the fact that the API is called `Readline`, and the input buffer is called `recvline`). `EPIPE` and `SIGPIPE` are error conditions that occur for `send()`. Note that `recv()` errors would not induce `EPIPE` or `SIGPIPE`. Also, `recv()` return of `0` properly indicates that no more data is forthcoming. A `send()` return of `0` is an [edge case](http://stackoverflow.com/a/3082032/315052) but not an error. – jxh Apr 18 '14 at 00:33
  • uh-huh, I learned I lot. I realized it is better to use recv(2)/send(2) instead of read(2)/write(2) when dealing with sockets, although, whenever possible, the use of writev(2)/readv(2) is a lot better than a simple recv/send. Knowing how to handle these functions is crucial, your answer lightened my sight, despite the fact I wasn't totally agree with your answer at the time – yeyo May 02 '14 at 02:44
0

in the server side, check if the client closed the connexion:

rv = recv(newfd, b, 100, 0);
if(rv<=0) {
        if(!rv) {
             fprintf(stderr, "conn. closed.");
        } else {
              perror("recv");
        }
        exit(EXIT_FAILURE);
}
yeyo
  • 2,954
  • 2
  • 29
  • 40
  • `!rv` isn't correct. You must check for `rv == 0` to detect EOS, and -1 to detect an error. – user207421 Jun 27 '13 at 00:45
  • @EJP wait!, 0 evaluate to false, anything else evaluate to TRUE, if rv==0, then !rv will evaluate to TRUE and the fprintf function will be executed. Asterisk CodingStyle explicitly suggest using !variable instead of variable==0. man your -1 is so incorrect. – yeyo Jun 27 '13 at 00:50
  • 1
    This will not prevent the observed crash from occurring if the server is sending when the client has already been killed. – jxh Jun 27 '13 at 01:07
  • @Kira It's a trick, and a confusing one, as this discussion shows. The return value is documented as -1, 0, or a positive integer, and that is what you should test. There is no such recommendation in the coding style you cite. – user207421 Jun 27 '13 at 01:54
  • @EJP I'm so sorry for the bad citation; human mistake, I meant, Mozilla Coding Style, section: C/C++ practices. Here: https://developer.mozilla.org/en-US/docs/Developer_Guide/Coding_Style very easy to check. "...Use (x) or (!x) instead..." – yeyo Jun 27 '13 at 02:19