read()/ write()如何与FIFO一起使用? - 客户端/服务器程序中的问题

时间:2017-07-21 07:31:06

标签: c pipe mkfifo

我有一个客户端/服务器程序。

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

  • 在管道上写入string1。
  • 从另一个烟斗中读取一个字符串。
  • 在管道上写入string2。

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

  • 读取字符串。
  • 将相同的数据写入其他管道上的客户端。
  1. 对于某些迭代,这可以正常工作,大约10次迭代后,只能看到在客户端和服务器上读取string2。 为什么这样呢? 另外,在while()循环中的客户端程序中如果在第二次write()之后调用read(),一切正常。

  2. 在客户端,从第二次迭代读取()应该返回管道中的整个数据,因为管道没有消息边界。但它只读取服务器中1次write()调用所写的数据。

  3. 以下是我的参考代码。

    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, '\0', sizeof(str));
    
      //write second string
      write(client_to_server, buf2, sizeof(buf2));
     }
    
     close(client_to_server);
     close(server_to_client);
     return 0;
    }
    

    以下是输出: 服务器输出: 服务器ON 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

2 个答案:

答案 0 :(得分:0)

简短的回答是客户端和服务器接收&#34; str2 \ 0str1 \ 0&#34;但打印到第一个\ 0,即&#34; str2&#34;。

客户端的长答案不仅仅是字符串&#34; str1&#34;和&#34; str2&#34;但终止NUL:

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

sizeof返回字符串的长度加上终止NUL。

现在是服务器端。你读到了BUFSIZ字节。因此,如果客户端足够快,可以发送少量字符串,即&#34; str2 \ 0str1 \ 0&#34;你将一次阅读所有字符串。要解决这个问题,你必须考虑读取的内容,即:

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

答案 1 :(得分:0)

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

通过指定printf(3)格式的字符串长度,关注"%.*s"调用处理非空终止字符串的方式(这是避免放置最终{{1}的技巧在缓冲区中(如果您正在阅读\0,则应该检查,而不是sizeof buf

server.c

sizeof buf - 1

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