c++ - 命名管道,选择错误的文件描述符 C++

标签 c++ client-server named-pipes echo-server

我只想编写一个简单的带有命名管道的多客户端服务器。我在服务器端有问题。当我只有读书的时候,一切都很棒。现在,我添加了服务器将消息发送回客户端的可能性 - 现在它根本不起作用......

这是我的服务器代码(注意,当我删除注释行/* */时,它会从所有客户端读取,它工作正常,但 - 只读)。如何强制服务器也写入所有客户端? (这个概念是客户端首先写入发送给服务器客户端 fifoname。服务器创建并打开这样的 fifo 并且可以写入它但是写入部分仍然不想工作......:/)

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;
}

客户端.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;
}

我的输出:

服务器端:

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

客户端:

$ ./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: 

最佳答案

首先,检查 open 调用是否有错误。这很可能解释了您所问的问题。

我不清楚“消息”应该是什么。我看到它应该以 # 开头,但是服务器应该如何知道它在哪里结束?它总是 4,096 字节吗?

但是你有三个主要问题:

  1. 您的代码不应在任何地方阻塞,但它不会将描述符设置为非阻塞。

  2. 您的代码需要 4,096 字节的消息。但是,如果它读取的字节较少,它不会等到读取完其余字节,而是会破坏消息。 (或者它期望的任何消息,它显然可以超过管道可以原子处理的字节数。您需要以某种方式找到消息边界。)

  3. 您的代码无法处理部分写入。如果任何客户端读取速度不够快,它目前会挂起(由于上面的问题 1),但一旦您解决了这个问题,它就会破坏数据。

您需要每个连接应用程序接收缓冲区。当您从连接读取数据时,将其读入应用程序缓冲区。如果您收到了完整的消息,很好,请处理消息。否则,请等到您阅读完其余内容。如果您不使用固定长度的消息,则必须处理读取一条消息的结尾和另一条消息的开头的情况。

您有两种写入策略选择:

  1. 使用每个连接的应用程序写入缓冲区。当您向连接发送数据时,如果所有字节都没有立即发送,请将剩余字节保存在应用程序缓冲区中。开始选择该描述符进行写入和读取。如果您获得写入命中,请尝试从应用程序缓冲区写入。

  2. 仅使用内核缓冲区。如果您获得部分写入,则内核缓冲区已满。断开客户端,因为它跟不上。

关于c++ - 命名管道,选择错误的文件描述符 C++,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/13840911/

相关文章:

c++ - 我可以执行获取我的 `std::future` 并等待它吗?

java - 如何构建分布式 Java 应用程序?

c - 使用套接字下载(获取)文件时观察不稳定的行为

go - 在 Go 中读取命名管道时 100% CPU 使用率

c++ - OpenGL 实例化 VBO - 每个顶点、每个实例的属性

c++ - 调用SDL_CreateTextureFromSurface时SDL崩溃

c++ - 如何避免在 C++11 服务器程序中为多个客户端使用多线程

Java MySQL - 命名管道连接在关闭时抛出警告

c - mkfifo 文件权限未正确执行

C++ std::stringstream operator<< 重载