1

I am writing some code to create a client - server connection. The client sends strings to the server and the server is supposed to parse and print the string depending on what was sent.

Client code:

// Client side C/C++ program to demonstrate Socket programming
#include <stdio.h>
#include <sys/socket.h>
#include <stdlib.h>
#include <netinet/in.h>
#include <string.h>
#define PORT 8080

int main(int argc, char const *argv[]) {
    struct sockaddr_in address;
    int sock = 0, valread;
    struct sockaddr_in serv_addr;
    size_t len = 0;
    char *buffer = NULL;
    if ((sock = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
        printf("\n Socket creation error \n");
        return -1;
    }

    memset(&serv_addr, '0', sizeof(serv_addr));

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

    // Convert IPv4 and IPv6 addresses from text to binary form
    if (inet_pton(AF_INET, "127.0.0.1", &serv_addr.sin_addr) <= 0) {
        printf("\nInvalid address/ Address not supported \n");
        return -1;
    }

    if (connect(sock, (struct sockaddr *) &serv_addr, sizeof(serv_addr)) < 0) {
        printf("\nConnection Failed \n");
        return -1;
    }

    while(1) {
        getline(&buffer, &len, stdin);
        printf("Line sent: %s", buffer);
        send(sock, buffer, strlen(buffer), 0);
    }
}

Server code:

/*Required Headers*/

#include <sys/types.h>
#include <sys/socket.h>
#include <netdb.h>
#include <stdio.h>
#include<string.h>

int main()
{

    char str[100];
    int listen_fd, comm_fd;

    struct sockaddr_in servaddr;

    listen_fd = socket(AF_INET, SOCK_STREAM, 0);

    bzero( &servaddr, sizeof(servaddr));

    servaddr.sin_family = AF_INET;
    servaddr.sin_addr.s_addr = htons(INADDR_ANY);
    servaddr.sin_port = htons(8080);

    bind(listen_fd, (struct sockaddr *) &servaddr, sizeof(servaddr));

    listen(listen_fd, 10);

    comm_fd = accept(listen_fd, (struct sockaddr*) NULL, NULL);

    while(1)
    {
        bzero( str, 100);

        read(comm_fd,str,100);

        printf("Received: %s",str);
        if(strncmp(str, "NEW", 3) == 0)
        {
            printf("Client - NEW");
        }
        else if(strncmp(str, "FILE", 4) == 0)
        {
            printf("Client - FILE");
        }
        else if(strncmp(str, "NAME", 4) == 0)
        {
            strtok(str, ": ");
            printf("NAME: %s", str);
        }
        else if(strncmp(str, "DESCRIPTION", 11) == 0)
        {
            strtok(str, ": ");
            printf("DESCRIP: %s", str);
        }
        else if(strncmp(str, "PRINTER", 7) == 0)
        {
            strtok(str, ": ");
            printf("PRINTER: %s", str);
        }

    }
}

What I am expecting to happen is that when I send "NEW" from the client to the server, the server prints "Client - NEW". Then when I send "FILE" the server prints "CLIENT - FILE". What happens is there is a delay of one send. So when I send NEW, the server prints "Received: NEW", but it doesn't go into the if check to print "CLIENT - NEW". Then when I send "FILE", the server prints "Client - NEWRecieved: FILE".

What could I be doing wrong here?

Citut
  • 847
  • 2
  • 10
  • 25
  • 1
    Probably line buffering issues, add a `\n` at the end of each printf format string. – Ctx Mar 31 '19 at 22:42
  • Possible duplicate of [What is the correct way of reading from a TCP socket in C/C++?](https://stackoverflow.com/questions/666601/what-is-the-correct-way-of-reading-from-a-tcp-socket-in-c-c) – tevemadar Mar 31 '19 at 22:45
  • @tevemadar What exactly in that question/answer(s) should help the OP here? – Ctx Mar 31 '19 at 22:48
  • @Ctx people mis-use `read`, they ignore its return value and assume it will read a "message", what they fed into the other end in a single step. In your answer you went around the problem by treating the thing as a line-based protocol, but the test string "FILE" forecasts that OP will try to extend this into a file-transfer protocol in the next step. So yes, you helped writing OP a chat application, just I do not think they are writing a chat application. And at the end OP still does not know how to use `read`. – tevemadar Apr 01 '19 at 08:48
  • @tevemadar Just concentrate on the issues the OP raises and give him the chance, to solve other issues for himself. – Ctx Apr 01 '19 at 09:46

1 Answers1

1

So when I send NEW, the server prints "Received: NEW"

Yes, that's here:

printf("Received: %s",str);

but to be specific, you send "NEW\n", and the server prints "Received: NEW\n". This newline is relevant, because it triggers the output on line buffered terminals

but it doesn't go into the if check to print "CLIENT - NEW".

Yes, it executes the conditional:

printf("Client - NEW");

but the output is buffered until the next newline gets printed.

Then when I send "FILE", the server prints "Client - NEWRecieved: FILE".

Now after you send "FILE", this gets executed:

printf("Received: %s",str);

with str having "FILE\n" as content. This flushes the output buffer, which currently contains "Client - NEWRecieved: FILE\n". Furthermore

printf("Client - FILE");

gets executed (but not output, only buffered) and the process iterates.

So the fix is, to add a newline "\n" at the end of each printf format string (or use puts in the simple cases).

printf("Received: %s\n",str); // Although "str" will usually contain a newline, but better play it safe

puts("Client - NEW");

Furthermore, if you have a line-based protocol, you should switch to buffered input for your socket:

comm_fd = accept(listen_fd, (struct sockaddr*) NULL, NULL);
FILE *sock = fdopen(comm_fd, "r+");

and then read the input line-by-line

while (1) {
    fgets(str, sizeof(str), sock);
Ctx
  • 18,090
  • 24
  • 36
  • 51
  • But the *real* problem is, of course(as always) , assuming message boundaries. – wildplasser Mar 31 '19 at 23:04
  • 1
    @wildplasser That's not the issue he raises here at the moment, but I addressed it anyway. Maybe it helps him in the future. The current issue is clearly the line buffering. – Ctx Mar 31 '19 at 23:10
  • And there is also the`bzero()`thing. Which is defensive. And also *very* wrong. – wildplasser Mar 31 '19 at 23:29
  • @Ctx Your solution fixed my buffering issues, thanks!! – Citut Apr 01 '19 at 01:11
  • @wildplasser I saw the bzero in multiple code snippets, what's wrong with it? – Citut Apr 01 '19 at 01:11
  • 1
    It's unnecessary, and dangerous, cargo-cult cycle wasting. Correctly handling the result returned by read() will tell you where to insert a NUL terminator, under-reading by 99 will ensure that there is always space for a NUL and prevent the UB of writing out-of-bounds if 100 chars are read. The 'multiple code snippets' are all bad:) – Martin James Apr 01 '19 at 07:07
  • @wildplasser That was automatically addressed with the "message boundaries" thing before, wasn't it? The `bzero()` is now obsolete. Btw. I don't consider it mandatory to fix each and every issue with some code snippet, I usually concentrate on the question and maybe comment on other things as I see it fit. – Ctx Apr 01 '19 at 07:12
  • @Ctx thanks for your help. I have one more question, will `fgets(str, sizeof(str), sock);` always end with `\n\0`? Or will it only have the trailing null character? – Citut Apr 01 '19 at 20:06
  • @Citut It will always have the zero byte, but it might not end with \n if the size limit kicks in first. – Ctx Apr 01 '19 at 22:35