How does read () / write () work with FIFOs? - Problem in client-server program

I have a client / server program.

The client does the following in a loop.

  • writes the string string1 to the pipe.
  • read a line from another channel.
  • writes the string string2 to the pipe.

In a loop, the server performs the following operations.

  • reads a line.
  • writes the same data to the client on another channel.
  • For some iterations this works fine, after about 10 iterations you can see that only line2 is read on both the client and the server. Why is this so? Also, in the client program in the while () loop, if read () is called after 2nd write (), everything works fine.

  • In the client, from the second iteration, read () should return all data in the pipe because the pipe has no message boundaries. But it just reads only the data written by call 1 write () on the server.

Below is my code for reference.

server.c

#include <fcntl.h>
#include <stdio.h>
#include <sys/stat.h>
#include <unistd.h>
#include <string.h>

int main()
{
 client_to_server;
char *myfifo = "/tmp/client_to_server_fifo";
int server_to_client;
char *myfifo2 = "/tmp/server_to_client_fifo";

char buf[BUFSIZ];

 /* create the FIFO (named pipe) */
 mkfifo(myfifo, 0666);
 mkfifo(myfifo2, 0666);

 /* open, read, and display the message from the FIFO */
 client_to_server = open(myfifo, O_RDONLY);
 server_to_client = open(myfifo2, O_WRONLY);

 printf("Server ON bufsize=%d.\n", BUFSIZ);

 while (1)
 {
   read(client_to_server, buf, BUFSIZ);
  if (strcmp("exit",buf)==0)
  {
     printf("Server OFF.\n");
     break;
  }
  else if (strcmp("",buf)!=0)
  {
     printf("Received: %s\n", buf);
     printf("Sending back...\n");
     write(server_to_client,buf,BUFSIZ);
  }

  /* clean buf from any data */
  memset(buf, 0, sizeof(buf));
 }

 close(client_to_server);
 close(server_to_client);
 unlink(myfifo);
 unlink(myfifo2);
 return 0;
}

      

client.c

#include <stdio.h>
#include <fcntl.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h>
#include<string.h>

int main()
{
   int client_to_server;
   char *myfifo = "/tmp/client_to_server_fifo";
   int server_to_client;
   char *myfifo2 = "/tmp/server_to_client_fifo";

   char str[BUFSIZ];

  /* write str to the FIFO */
  client_to_server = open(myfifo, O_WRONLY);
  server_to_client = open(myfifo2, O_RDONLY);

  char buf1[30] = "str1";
  char buf2[30] = "str2";

 while(1){

   //write first string
   write(client_to_server, buf1, sizeof(buf1));
   read(server_to_client,str,sizeof(str));
   perror("Read:"); // Very crude error check
   printf("...received from the server: %s\n",str);

   memset(str, '\0', sizeof(str));

  //write second string
  write(client_to_server, buf2, sizeof(buf2));
 }

 close(client_to_server);
 close(server_to_client);
 return 0;
}

      

Below is the result: Server output: Server ON bufsize = 8192. Received: str1 Send back ... Received: str2 Send back ... Received: str1 Send back ... Received: str2 Send back ... Received: str1 Send back ... Received: str2 Send back ... Received: str1 Send back ... Received: str2 Send back ... Received: str1 Send back ... Received: str2 Send back ... Received: str1 Send back .. Received: str2 Send back ... Received: str1 Send back ... Received: str2 Send back ... Received: str1 Send back ... Received: str2 Send back ... Received: str1 Send back ... Received : str2 Send back ... Received: str1 Send back ... Received: str2 Send back ... Received: str2 Send back ... Received: str2 Send back ... Received:str2 Send back ... Received: str2 Send back ... Received: str2 Send back ... Received: str2 Send back ... Received: str2

Below is the result from the client: ... received from the server: str1 ... received from the server: str2 ... received from the server: str1 ... received from the server: str2 ... received from the server: str1 ... received from server: str2 ... received from server: str1 ... received from server: str2 ... received from server: str1 ... received from server: str2 ... received from server: str1 ... received from server : str2 ... received from server: str1 ... received from server: str2 ... received from server: str1 ... received from server: str2 ... received from server: str1 ... received from server: str2 ... received from server: str1 ... received from server: str2 ... received from server: str2 ... received from server: str2 ... received from server: str2 ... received from server: str2 .. .received from server: str2 ... received from server: str2 ...received from server: str2 ... received from server: str2 ... received from server: str2 ... received from server: str2 ... received from server: str2 ... received from server: str2 ... received from server: str2 ... received from server: str2

+3


source to share


2 answers


The short answer is that the client and server both receive "str2 \ 0str1 \ 0", but only print up to the first \ 0, i.e. "str2".

Long answer on the client side that you are sending not only the "str1" and "str2" lines, but also the terminating NUL:

write(client_to_server, buf1, sizeof(buf1));

      



Here sizeof returns the length of the string plus the terminating NUL.

Now the server side. You are reading the BUFSIZ bytes. Therefore, if the client is fast enough to send multiple lines, that is, "str2 \ 0str1 \ 0", you will read all lines in one go. To fix this, you have to take into account what is reading, for example:

ssize_t ret = read(client_to_server, buf, BUFSIZ);
printf("Received: size %zd\n", ret); // print the size
fwrite(buf, 1, ret, stdout); // print the buffer with NULs
write(server_to_client,buf,ret); // write back just the data you actually received

      

0


source


You are not checking for errors, and you are not using a return value from read(2)

that tells you how many characters are returned. This is weird as it is the source of an unsynchronized server and client. I rewrote your code so that it doesn't work (although I don't check the return value write(2)

, which should fail when pipes are broken --- when killing the server or client). It remains as complete as exercise for you.

Focus on how null terminated strings are handled by the call printf(3)

by specifying the length of the formatted string "%.*s"

(this is a trick to avoid putting the final \0

in a buffer (which should be checked if you are reading before sizeof buf

instead of sizeof buf - 1

.

server.c



#include <errno.h>
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/stat.h>
#include <unistd.h>

#define F(fmt) __FILE__":%d:%s: " fmt, __LINE__, __func__

/* file scope to share with at_exit() call */
static const char myfifo[] = "/tmp/client_to_server_fifo";
static const char myfifo2[] = "/tmp/server_to_client_fifo";

void rm_fifos(void)
{
    printf(F("deleting fifo \"%s\"\n"), myfifo);
    unlink(myfifo);
    printf(F("deleting fifo2 \"%s\"\n"), myfifo2);
    unlink(myfifo2);
}

int main()
{
    int client_to_server;
    int server_to_client;
    int res;

    char buf[BUFSIZ];

    /* create the FIFO (named pipe) */
    printf(F("creating fifo \"%s\"\n"), myfifo);
    res = mkfifo(myfifo, 0666 | O_EXCL); /* O_EXCL guarantees that no second instance is created while this is running */
    if (res < 0) {
            fprintf(stderr,
                    F("%s: %s(errno=%d)\n"),
                    myfifo, strerror(errno), errno);
            exit(EXIT_FAILURE);
    }
    printf(F("creating fifo \"%s\"\n"), myfifo);
    res = mkfifo(myfifo2, 0666 | O_EXCL);
    if (res < 0) {
            fprintf(stderr,
                    F("%s: %s(errno=%d)\n"),
                    myfifo2, strerror(errno), errno);
            unlink(myfifo); /* we successfuly created it */
            exit(EXIT_FAILURE);
    }

    atexit(rm_fifos);

    /* open, read, and display the message from the FIFO */
    client_to_server = open(myfifo, O_RDONLY);
    if (client_to_server < 0) {
            fprintf(stderr,
                    F("%s: open: %s(errno=%d)\n"),
                    myfifo, strerror(errno), errno);
            exit(EXIT_FAILURE);
    }

    server_to_client = open(myfifo2, O_WRONLY);
    if (server_to_client < 0) {
            fprintf(stderr,
                    F("%s: open: %s(errno=%d)\n"),
                    myfifo2, strerror(errno), errno);
            exit(EXIT_FAILURE);
    }

    printf(F("Server ON bufsize=%d.\n"), BUFSIZ);

    while (1) {
            ssize_t n = read(client_to_server, buf, BUFSIZ);
            if (n < 0) {
                    fprintf(stderr,
                            F("%s: read: %s(errno = %d)\n"),
                            myfifo, strerror(errno), errno);
                    exit(EXIT_FAILURE);
            }
            if (n == 0) break;  /* EOF on input */

            printf("Received: [%.*s]\n", n, buf);
            printf("Sending back...\n");
            write(server_to_client, buf, n); /* very important to write just the n read chars, and not more */

            /* no need to clean buf from any data */
    }

    close(client_to_server);
    close(server_to_client);

    /* no need to unlink, as this was prepared in the atexit() library call */
    return 0;
}

      

client.c

#include <errno.h>
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h>

#define F(fmt) __FILE__ ":%d:%s: " fmt, __LINE__, __func__

int main()
{
    int client_to_server;
    char *myfifo = "/tmp/client_to_server_fifo";
    int server_to_client;
    char *myfifo2 = "/tmp/server_to_client_fifo";

    char str[BUFSIZ];

    /* write str to the FIFO */
    client_to_server = open(myfifo, O_WRONLY);
    if (client_to_server < 0) {
            fprintf(stderr,
                    F("%s: %s(errno = %d)\n"),
                    myfifo, strerror(errno), errno);
            exit(EXIT_FAILURE);
    }
    server_to_client = open(myfifo2, O_RDONLY);
    if (server_to_client < 0) {
            fprintf(stderr,
                    F("%s: %s(errno = %d)\n"),
                    myfifo2, strerror(errno), errno);
            exit(EXIT_FAILURE);
    }

    char buf1[30] = "str1";
    char buf1_length = strlen(buf1);
    char buf2[30] = "str2";
    char buf2_length = strlen(buf2);

    while(1) {

            ssize_t n;
            //write first string
            write(client_to_server, buf1, buf1_length);
            n = read(server_to_client, str, sizeof(str));
            if (n < 0) {
                    fprintf(stderr,
                            F("%s: %s(errno = %d)\n"),
                            myfifo2, strerror(errno), errno);
                    exit(EXIT_FAILURE);
            }
            if (n == 0) break;

            printf("...received from the server: %.*s\n", n, str);

            /* NO NEED TO CLEAN THE BUFFER */

            //write second string
            write(client_to_server, buf2, buf2_length);
    }

    close(client_to_server);
    close(server_to_client);

    return 0;
}

      

0


source







All Articles