0

I am learning about sockets and followed the GeeksForGeeks tutorial here. As a test, I added a while loop to enclose the sending and receiving of data on both the client and server side. I then noticed that if I killed the client process, the server process also exited. And that's where I'm confused -- the client and server both have separate PIDs, so why would this happen? It seems like the client should have been the only process to have been killed.

I see on this post that the trick is putting the server accept() call inside the while loop. But still, shouldn't the server have been stuck in its own while-loop, attempting to read and send data on the no longer active socket, no matter what the client is doing? There is no condition in my while loop which would have exited upon the connection closing. I suppose my real question is what kind of IPC is going on here that would cause this behavior?

I am attaching my code for reference.

Server.c

    int server_fd, new_socket, valread;
    struct sockaddr_in address;
    int addrlen = sizeof(address);
    int opt = 1;
    char buffer[1024] = { 0 };
    char *hello = "Hello from server";

    if ((server_fd = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
        perror("socket failed");
        exit(EXIT_FAILURE);
    }

    if (setsockopt(server_fd, SOL_SOCKET, SO_REUSEADDR | SO_REUSEPORT, &opt, sizeof(opt)) < 0) {
        perror("setsockopt");
        exit(EXIT_FAILURE);
    }

    address.sin_family = AF_INET;
    address.sin_addr.s_addr = INADDR_ANY;
    address.sin_port = htons(PORT);

    if (bind(server_fd, (struct sockaddr*)&address, sizeof(address)) < 0) {
        perror("bind failed");
        exit(EXIT_FAILURE);
    }


    if (listen(server_fd, 3) < 0) {
            perror("listen");
            exit(EXIT_FAILURE);
    }

    if ((new_socket = accept(server_fd, (struct sockaddr*)&address, (socklen_t*)&addrlen)) < 0) {
            perror("accept");
            exit(EXIT_FAILURE);
    }
    while (1) {
        valread = read(new_socket, buffer, sizeof(buffer));
        printf("%s\n", buffer);

        
        send(new_socket, hello, strlen(hello), 0);

    }


    close(new_socket);
    shutdown(server_fd, SHUT_RDWR);

    return 0;
}

Client.c

    int client_fd, status, valread;
    struct sockaddr_in serv_addr;
    char *hello = "Hello from client";
    char buffer[1024] = { 0 };

    if ((client_fd = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
        perror("socket failed");
        exit(EXIT_FAILURE);
    }

    serv_addr.sin_family = AF_INET;
    serv_addr.sin_port = htons(PORT);

    if (inet_pton(AF_INET, "127.0.0.1", &serv_addr.sin_addr) <= 0) {
        perror("Address not supported");
        exit(EXIT_FAILURE);
    }

    if ((status = connect(client_fd, (struct sockaddr*)&serv_addr, sizeof(serv_addr))) < 0) {
        perror("Connection failed");
    }
    
    while (1) {
        send(client_fd, hello, strlen(hello), 0);
        printf("Hello message sent\n");
        valread = read(client_fd, buffer, sizeof(buffer));
        printf("%s\n", buffer);
    }
    

    close(client_fd);

    return 0;

I tried to output the pids of the client server process to validate they have separate process IDs. I also looked up about shared resources between clients and servers, but found nothing that would indicate the processes themselves would be interdependent.

  • Unfortunately your programs are incomplete. I cannot compile and run them to check for errors. Please [edit] your question and show a [mre]. – Bodo May 10 '23 at 18:23
  • Apart from the problem mentioned in your answer, the code has more problems. With `send(client_fd, hello, strlen(hello), 0);` you send the string without the terminating `'\0'`, so on the receiving side you don't have a valid C string and using it with `printf` is undefined behavior. The return values of both `send` and `recv` tell you how many bytes were sent or received, which can be less than the size specified as a function argument. In this case you might have to repeat the call with updated buffer position and size to transfer the remaining part. – Bodo May 10 '23 at 18:36
  • What do you expect the other side to do when one side closes the connection or gets terminated (which implicitly closes all files including sockets)? – Bodo May 10 '23 at 18:38
  • In your server, you just do: `if ((new_socket = accept(...)` So, when the subsequent `while` loop completes, the program terminates. You want _two_ nested loops (e.g.) `while (1) { new_socket = accept(...); while (1) { rlen = read(new_socket,...); if (rlen <= 0) break; send(new_socket,...); } close(new_socket); }` – Craig Estey May 10 '23 at 18:55

2 Answers2

0

Just found the answer, here. It seems when the client is closed with Ctrl+C, the server will try to call send() on a closed connection, which will issue a SIGPIPE to the server process, which kills it. The option MSG_NOSIGNAL can be used as a flag on the server send call which will prevent this behavior.

0

As OP has already found the problem, I'll present two ways to ignore SIGPIPE:

signal (SIGPIPE, SIG_IGN);

// OR:

send (fd, buf, len, MSG_NOSIGNAL);
Harith
  • 4,663
  • 1
  • 5
  • 20