read() / write() 如何与 FIFO 配合使用? - client/server 程序中的问题

How read() / write() works with FIFO? - Issue in a client/server program

我有一个 client/server 程序。

客户端循环执行以下操作。

服务器循环执行以下操作。

  1. 对于某些迭代,这工作正常,在大约 10 次迭代后,在客户端和服务器上只能看到 string2 被读取。 为什么会这样? 此外,如果在第二次 write() 之后调用 read(),则在 while() 循环中的客户端程序中一切正常。

  2. 在客户端中,从第二次迭代开始,read() 应该 return 管道中的全部数据,因为管道没有消息边界。但它只读取服务器中 1 次 write() 调用写入的数据。

下面是我的代码供参考。

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, '[=12=]', sizeof(str));

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

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

下面是输出: 服务器输出: 服务器开启 bufsize=8192。 收到:str1 发回... 收到:str2 发回... 收到:str1 发回... 收到:str2 发回... 收到:str1 发回... 收到:str2 发回... 收到:str1 发回... 收到:str2 发回... 收到:str1 发回... 收到:str2 发回... 收到:str1 发回... 收到:str2 发回... 收到:str1 发回... 收到:str2 发回... 收到:str1 发回... 收到:str2 发回... 收到:str1 发回... 收到:str2 发回... 收到:str1 发回... 收到:str2 发回... 收到:str2 发回... 收到:str2 发回... 收到:str2 发回... 收到:str2 发回... 收到:str2 发回... 收到:str2 发回... 收到:str2

以下是客户端的输出: ...从服务器收到:str1 ...从服务器收到:str2 ...从服务器收到:str1 ...从服务器收到:str2 ...从服务器收到:str1 ...从服务器收到:str2 ...从服务器收到:str1 ...从服务器收到:str2 ...从服务器收到:str1 ...从服务器收到:str2 ...从服务器收到:str1 ...从服务器收到:str2 ...从服务器收到:str1 ...从服务器收到:str2 ...从服务器收到:str1 ...从服务器收到:str2 ...从服务器收到:str1 ...从服务器收到:str2 ...从服务器收到:str1 ...从服务器收到:str2 ...从服务器收到:str2 ...从服务器收到:str2 ...从服务器收到:str2 ...从服务器收到:str2 ...从服务器收到:str2 ...从服务器收到:str2 ...从服务器收到:str2 ...从服务器收到:str2 ...从服务器收到:str2 ...从服务器收到:str2 ...从服务器收到:str2 ...从服务器收到:str2 ...从服务器收到:str2 ...从服务器收到:str2

简短的回答是客户端和服务器接收 "str2[=20=]str1[=20=]" 但只打印到第一个 \0,即 "str2".

长的答案是在客户端,你发送的不仅仅是字符串 "str1" 和 "str2",还有终止 NUL:

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

这里的sizeofreturns字符串的长度加上终止NUL。

现在是服务器端。您最多读取 BUFSIZ 字节。因此,如果客户端足够快以发送少量字符串,即 "str2[=20=]str1[=20=]" 您将一次性读取所有字符串。要解决此问题,您必须考虑读取的内容 returns,即:

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

您不检查错误,也不使用 read(2) 中的 return 值,它表示读取了 return 的字符数。这很奇怪,因为这是不同步的服务器和客户端的来源。我已经以不会失败的方式重写了您的代码(尽管我没有检查 write(2) 的 return 值,这应该会在破坏管道时失败 --- 在杀死服务器或客户端时)这是留给你完成的,作为练习。

关注 printf(3) 调用如何处理非空终止字符串,通过指定 "%.*s" 格式的字符串长度(这是避免将最终 [= 16=] 在缓冲区中(如果您正在阅读 sizeof buf,而不是 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;
}