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



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

这是我的服务器代码(请注意,当我删除注释行/**/时,它会从所有客户端读取,它运行得很好,但仅限-READING)。如何强制服务器也向所有客户端写入?(这个概念是客户端先写,然后发送给服务器客户端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: %sn", 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: %sn", buffer);
/*if(startsWith(buffer, "#"))
{
// remove # from message from client
string login = removePrefix(buffer);
string fifoname = "./fifo";
fifoname += login;
printf("REGISTERED: %sn", 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: %sn", myfifo);
printf("Client reads from: %sn", 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: %sn",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调用中的错误。这很可能解释了你所问的问题。

我不清楚"消息"应该是什么。我知道它应该以#开头,但服务器怎么知道它的结尾呢?它总是4096字节吗?

但你有三个主要问题:

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

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

  3. 您的代码无法处理部分写入。如果任何客户端读取速度不够快,它当前将挂起(由于上面的问题1),但一旦修复,它将损坏数据。

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

你有两种写作策略可供选择:

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

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

最新更新