Can't read more than one word from a named pipe using poll

I simulate having two authors and one reader based on this.

So I am creating two pipes and I am writing one actual line for each channel and one line that notifies the reader that it is done with that author.

However, it will only read the first line, and sometimes the final line of the second pipe.

What am I missing?

reader.c

int main() {
    int w_no = 2;
    int fd[w_no];
    char * myfifo[w_no];
    fill_names(myfifo, w_no);
    print_names(myfifo, w_no);
    struct pollfd fdtab[w_no];
    int done[w_no];

    /* create the FIFO (named pipe) */
    int i;
    for (i = 0; i < w_no; ++i) {
        //fd[i] = open(myfifo[i], O_RDONLY);
        while( (fd[i] = open(myfifo[i], O_RDONLY)) == -1); 

        fdtab[i].fd = fd[i];
        fdtab[i].events = POLLIN;
        fdtab[i].revents = 0;

        done[i] = 0;
    }

    char buffer[1024];
    ssize_t bytes;
    printf("Edw prin\n");
    while(not_all_done(done, w_no)) {
        int retpoll = poll(fdtab,  w_no, 300);
        if(retpoll != 0) {
             if (retpoll == -1) {
                 perror("poll");
                 break;
             }

             for(i = 0; i < w_no; ++i) {
                 if(fdtab[i].revents & POLLIN) {
                            printf("Edw %d %d %d %d\n", i, retpoll, fdtab[i].revents, POLLIN);
                     //read the written pipe
                     while((bytes = read(fdtab[i].fd, buffer, sizeof(buffer))) > 0)
                        printf("Read |%s| %d %d %d\n", buffer, retpoll, fdtab[i].revents, POLLIN);
                     if(!strcmp(buffer, "++"))
                        done[i] = 1;
                 }
             }
         } else if (retpoll == 0) {
                /* the poll has timed out, nothing can be read or written */
                printf("timeout from writer\n");
                break;
            }
    }

    for (i = 0; i < w_no; ++i) {
        close(fd[i]);
    }

    free_names(myfifo, w_no);
    return 0;
}

      

writer.c

int main() {
    int w_no = 2;
    int fd[w_no];
    char * myfifo[w_no];
    fill_names(myfifo, w_no);
    print_names(myfifo, w_no);

    /* create the FIFO (named pipe) */
    int i;
    int bytes;
    for (i = 0; i < w_no; ++i) {
      mkfifo(myfifo[i], 0666);
        fd[i] = open(myfifo[i], O_WRONLY);

        while( (bytes = write(fd[i], "Hi+", sizeof("Hi+"))) == 3);
        printf("wrote %d bytes, %d\n", bytes, sizeof("Hi+"));
        while( (bytes = write(fd[i], "++", sizeof("++"))) == 2);
        printf("wrote %d bytes, %d\n", bytes, sizeof("++"));
    }

    for (i = 0; i < w_no; ++i) {
        close(fd[i]);
        unlink(myfifo[i]);
    }

    free_names(myfifo, w_no);
  return 0;
}

      

Output example:

/tmp/myfifo_0
/tmp/myfifo_0
/tmp/myfifo_1
/tmp/myfifo_1
wrote 4 bytes, 4
wrote 3 bytes, 3
wrote 4 bytes, 4
Edw prin
wrote 3 bytes, 3
Edw 0 2 17 1
Read |Hi+| 2 17 1
Edw 1 2 1 1
Read |Hi+| 2 1 1
^C

      


EDIT

When rows Hi+

arrive, the value bytes

is 7.

The final line I'm trying to send is this ++

, but it won't read.


EDIT_2

char* concat(char *s1, char *s2) {
  char *result = malloc(strlen(s1) + strlen(s2) + 1);  //+1 for the null-terminator
  //in real code you would check for errors in malloc here
  strcpy(result, s1);
  strcat(result, s2);
  return result;
}

void fill_names(char* f[], int n) {
  int i = 0;
  char * buf = "/tmp/myfifo_";
  char str[15];
  for (; i < n; ++i) {
    sprintf(str, "%d", i);
    f[i] = concat(buf, str);
  }
}

      


Idea

Maybe the author closes and disconnects pipes before data is read from them? If so, what should I do to prevent this?

Putting in front of this sleep(10)

doesn't change the behavior, it just reads the first two lines, but it takes more time and then it will hang (because it waits for the lines to end).


EDIT_3

I also have main.c that does read and write.

+3


source to share


2 answers


There is a problem in your string entry:

    while( (bytes = write(fd[i], "Hi+", sizeof("Hi+"))) == 3);
    printf("wrote %d bytes, %d\n", bytes, sizeof("Hi+"));
    while( (bytes = write(fd[i], "++", sizeof("++"))) == 2);
    printf("wrote %d bytes, %d\n", bytes, sizeof("++"));

      

Here you are sending 7 bytes: H i + \0 + + \0

because sizeof()

litteral strings include a null terminator.

By the way, it while((bytes=write(...))==3)

will loop until 3 bytes are written. This does not happen because of the null terminator you write. But it's better to delete the attached one while

.

Since a pipe is a stream, nothing guarantees that you will receive bytes in two different reads. In fact, all your explanations and logs show that you are getting all 7 bytes at the same time.

However, you are printing the content with printf("Read |%s| %d %d %d\n"...)

: the result of chaining a string containing "\ 0" is undefined. In your case, the printed line is truncated, so only "Hi +" is printed, but "\ 0 ++" remains hidden in the buffer.

By the way, it while((bytes = read(...)) > 0)

can cycle and print several times. This in itself is not a problem. It's just that if the writer sends data on time, continuous reading can temporarily block the reading of other pipes. Typically, in a polishing program, you prefer to read a little from each finished pipe.

Your check for the final string

    if(!strcmp(buffer, "++"))
          done[i] = 1;

      

in most cases it will fail. You are not sure if one write on one side will result in a read on the other. Thus, your "++" string will not necessarily be at the beginning of the buffer. It could be anywhere in the buffer, so you need to look for it. It can even be split between two consecutive readings.



By the way, it may be that it read()

detects only partial data (eg: "i +") without a trailing zero. If you try to print your buffer, assuming there is a valid string in it, you run the risk of a buffer overflow.

Recommendation:

If your named pipes are for handling textual data, I would suggest adding '\n'

at the end of each group of data you want to send and write the lines in the pipe without the trailing zero:

bytes = write(fd[i], "Hi+\n", sizeof("Hi+\n")-1);

      

Then, when you read, you can manipulate the buffer like a string: always add a trailing 0:

bytes = read(fdtab[i].fd, buffer, sizeof(buffer)-1);  // leave a byte for terminator
if (bytes>0) {
    buffer[bytes]=0; // end of string.  
    // process the string in the buffer
}
else if (bytes==0) {
    done[i]=1; 
} 

      

Finally, there are three possibilities to define your final command, assuming you sent it as "++ \ n":

if (strncmp(buffer,"++\n",3)==0  /* it at the beginning of the buffer */ 
      || strstr(buffer, "\n++\n") )    /* it in the middle but not a subpart and preceded by a packet separator */
       done[i]=1;   

      

But you should also check the gaps between the two readings. It's more subtle, but I'm sure you found a way :-)

+1


source


Try also to print the number of bytes you read into your reader's program. I am assuming you have read a few bytes than you think, but when you print a string, you only get bytes up to the first zero-terminated one.



Was it your intention to send trailing zeros to the named pipe? Maybe it would be better to send something else as a new line?

0


source







All Articles