简体   繁体   中英

Named pipes, select bad file descriptor C++

I just want to write a simple multiclient server with named pipes. I have a problem with server-side. When I had only reading, everything was great. Now, I added a possibility for server to send a message back to client - and now its not working at all ...

Heres my server code (notice, that when I remove commented lines /* */, it reads from all the clients, it works just fine but - READING ONLY) . How to force server to write to all the clients too? (The concept is that client first write sends to server clients fifoname. Server creates and opens such fifo and can write to it but the writing part still doesnt want to work ... :/)

int main (int argc, char *argv[])
{
        int fd_in, fd_out, j, result;
        char buffer[4096];
        const char *myfifo = "./fifo";
        mkfifo(myfifo, 0666 );

        if ((fd_in = open(myfifo, O_RDONLY | O_NONBLOCK)) < 0)
                perror(myfifo);

        printf("Server reads from: %s\n", myfifo);


        for (;;) {

                fd_set fds_r;
                fd_set master;
                FD_ZERO (&fds_r);
                FD_ZERO (&master);
                FD_SET (fd_in, &fds_r);
                master = fds_r;

                if ((result = select(fd_in + 1, &fds_r, NULL, NULL, NULL)) < 0)
                        perror ("select()");

                if (! FD_ISSET(fd_in, &fds_r))
                        continue;
                result = read(fd_in, buffer, 4096);
                printf("FROM CLIENT: %s\n", buffer);

                /*if(startsWith(buffer, "#"))
                {
                    // remove # from message from client
                    string login = removePrefix(buffer);
                    string fifoname = "./fifo";
                    fifoname += login;
                    printf("REGISTERED: %s\n", fifoname.c_str());
                    mkfifo(fifoname.c_str(), 0666);
                    if ((fd_out = open(fifoname.c_str(), O_WRONLY)) < 0)
                            perror(fifoname.c_str());
                    FD_SET (fd_out, &master);

                }
               /* for(j = 0; j <= fd_in; j++) {
                            // send to everyone!
                            if (FD_ISSET(j, &master)) {
                                // except the listener and ourselves
                                if (j != fd_out) {
                                    if (write(j, buffer, sizeof(buffer)) == -1) {
                                        perror("write");
                                    }
                                }
                            }
                        }*/


                memset(buffer, 0, sizeof(buffer));
        }

        fprintf (stderr, "Got EOF!\n");
        close (fd_in);
        return 0;
}

client.cpp

    #include <stdio.h>
#include <fcntl.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h>
#include <string>
#include <string.h>
using namespace std;

int main(int argc, char **argv)
{
   int client_to_server;
   const char *myfifo = "./fifo";

   int server_to_client;
   string f = "./fifo";
   string u(argv[1]);
   f += u;
   const char *myfifo2 = f.c_str();

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

   // register message #username
   string reg = "#";
   reg += u;

   printf("Client writes to: %s\n", myfifo);
    printf("Client reads from: %s\n", myfifo2);

   // first write to server, he can now make a fifo called fifo+username
   if(write(client_to_server, reg.c_str(), strlen(reg.c_str())) == -1)
        perror("write:");

   while(1)
    {

   printf("Input message to serwer: ");
   scanf("%s", str);

   if(write(client_to_server, str, sizeof(str))==-1)

   perror("Write:"); //Very crude error check

   if(read(server_to_client,str,sizeof(str))==-1)

   perror("Read:"); // Very crude error check

   printf("...received from the server: %s\n",str);
    }
   close(client_to_server);
   close(server_to_client);

   /* remove the FIFO */

   return 0;
}

My output:

server-side:

$ ./SERVER 
Server reads from: ./fifo
FROM CLIENT: #username
FROM CLIENT: hello
FROM CLIENT: 
FROM CLIENT: ?
FROM CLIENT: 
FROM CLIENT: huh
FROM CLIENT: 

client-side:

$ ./CLIENT username
Client writes to: ./fifo
Client reads from: ./fifousername
Input message to serwer: hello
Read:: Bad file descriptor
...received from the server: hello
Input message to serwer: ?
Read:: Bad file descriptor
...received from the server: ?
Input message to serwer: huh
Read:: Bad file descriptor
...received from the server: huh
Input message to serwer: 

First, check for errors on the open call. That's most likely what explains the issue you asked about.

It's not clear to me what a "message" is supposed to be. I see it's supposed to start with a # , but how is the server supposed to know where it ends? Is it always 4,096 bytes?

But you have three major problems:

  1. Your code is not supposed to block anywhere, yet it doesn't set the descriptors non-blocking.

  2. Your code expects 4,096 byte messages. But if it reads fewer bytes, it doesn't wait until it has read the rest but instead mangles the message. (Or whatever message it expects, it clearly can exceed the number of bytes a pipe can handle atomically. You need to find the message boundaries somehow.)

  3. Your code has no way to handle a partial write. If any client isn't reading fast enough, it will currently hang (due to issue 1 above), but once you fix that, it will mangle the data.

You need per-connection application receive buffers. When you read data from a connection, read it into an application buffer. If you got the whole message, great, process the message. Otherwise, wait until you've read the rest. If you're not using fixed-length messages, you have to handle the case where you read the end of one message and the beginning of another.

You have two choices of write strategies:

  1. Use a per-connection, application write buffer. When you send data to a connection, if all the bytes don't send immediately, save the leftover in an application buffer. Start selecting on that descriptor for write as well as read. If you get a write hit, try to write from the application buffer.

  2. Use only the kernel buffer. If you get a partial write, the kernel buffer is full. Disconnect the client because it can't keep up.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM