尝试通过 FIFO 传递 c 中的结构

问题描述 投票:0回答:1

我试图通过 c 中的 FIFO 传递一个结构,但似乎无法将该结构传递给客户端。任何帮助将不胜感激。

创建结构并将其写入管道。不确定我是否做对了。在客户端中打开 FIFO,但无法在该端打印任何内容。 更新

这是我更新的代码以及我正在尝试做的事情。我向辅助函数添加了一个 switch 语句,并添加了一个 case 将数字增加了三倍。

我将如何让程序循环,直到我决定最终退出并终止它?我是否可以将整个

if(mkfifo("ClientFifo", 0777) == -1)
语句放入 while 循环中,然后使用新的 case 在 switch 语句中退出或终止,或者我将如何继续这样做。

我见过服务器客户端程序的其他示例,但它们似乎都只是继续循环并且不退出或终止。

再次感谢

带有 switch 语句的 Server.c

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <errno.h>
#include <fcntl.h>
#include <time.h>

struct Client {
    int clientNum;
    int systemCall;
    int num;
    int result;
}client;

struct Client get_student_data()
{
    struct Client s;
    s.clientNum = getpid();
    printf("Client Number:%d", s.clientNum);
    printf("\n");
    printf("Enter System Call number:");
    scanf("%d", &s.systemCall);
    printf("Enter Number:");
    scanf("%d", &s.num);
    printf("system call:%d\n", s.systemCall);

    switch (s.systemCall)
    {
    case 1:
        printf("Youre in case 1\n");
        s.result = s.num * s.num; 
        break;
    case 2:
        printf("Youre in case 2\n");
        s.result = s.num * s.num * s.num;
        break;
    default:
        printf("INVALID CHOICE\n");
        s.result = 0;
        break;
    }
    
    return s;
}

int main(int argc, char* argv[]) {//main
   
   //makes fifo  
   if(mkfifo("ClientFifo", 0777) == -1)
   {
        //error message in case fifo isnt created
        if(errno != EEXIST){
        printf("Could not create fifo file\n");
        return 1;
        }//end error checking
 
    //area to do work
    printf("\n");
    struct Client c1 = get_student_data();
    printf("\nClient number: %d\n",c1.clientNum);
    printf("System call number: %d\n",c1.systemCall);
    printf("Paramet number: %d\n",c1.num);
    printf("Result number: %d\n",c1.result);
    
    
    //opens and closes FIFO
    printf("Opening...\n");
    int fd = open("ClientFifo", O_WRONLY);
    printf("Opened\n");

    if(write(fd, &c1, sizeof(c1)) == -1){
    return 2;
    }
    //}//ends write
    //Closes fd
    printf("Written..\n");
    close(fd);
    
    return 0;
}//ends if fifo   
    
}//ends main

客户端.c

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <errno.h>
#include <fcntl.h>
#include <time.h>

struct Client {
    int clientNum;
    int systemCall;
    int num;
    int result;
}client;

int main(int argc, char* argv[]) {

   if(mkfifo("ClientFifo", 0777)== -1)
   {
    if(errno != EEXIST){
    printf("Could not create fifo file\n");
    return 1;
    }
    printf("Opened\n");
    int fd = open("ClientFifo", O_RDONLY);
    if (fd == -1) {
        return 1;
    }
    
         printf("Reading\n");
          read(fd, &client, sizeof(client)); 
          printf("Client number:  %d\n", client.clientNum);
          printf("System call number:  %d\n", client.systemCall);
          printf("Parameter number %d\n", client.num);
          printf("Result %d\n", client.result);
      
    close(fd);
    
    return 0;

}//end if   
    
}//end main
c struct fifo
1个回答
0
投票

下面是使用

FIFO
连接器在客户端和服务器程序之间传递数据的示例。

此处的示例是异步的 --- 非阻塞 --- 查看您的代码与此代码之间的差异可能会有所帮助。我不会说它是好的或更好的,甚至是正确的。但它是从另一个一直在这里运行的程序中删除的。

我将如何让程序循环,直到我决定最终退出并终止它?我是否可以将整个 if(mkfifo("ClientFifo", 0777) == -1) 语句放入 while 循环中,然后使用新的 case 在 switch 语句中退出或终止,或者我将如何继续这样做。

嗯,磁盘上的管道只是一个参考,在您启动围绕它的程序之前就可以在那里。并将留在那里。这对于您如何开始或结束

FIFO
没有任何影响。
FIFO
周围没有单个程序,除非您将相同的程序放在管道的两侧:总是有两个连接。

FIFO
的一侧终止时,另一侧也应该结束,因为管道只是一个连接器,并且——作为网络连接——它仅在连接存在时才处于活动状态。

关于示例

代码将位于头文件、lib 文件、服务器和客户端代码中。

数据


typedef struct
{
    char id[16];
    char name[16];
    char creation_log[64];
    char payload[928 + 9 * 1024];

} Packet;  // 10K area

这是使用的数据包,无用的

10K
区域。一个简单的帮助器工厂函数创建供使用的编号记录,
get_data()
。另一个助手
show_data()
显示数据并在客户端和服务器上使用。这是输出的示例:

Packet = {
    id: "1000",
    name: "process 004389",
    23/10/10 01:30:16 process 004389 id is 1000
}

payload
被忽略。
id
1000
开始编号。
name
获取服务器程序的
pid
creation_log
是时间戳,如图所示。它相当于你的
Client

所以标题

stuff.h

stuff.h

#define _GNU_SOURCE
#include <errno.h>
#include <fcntl.h>
#include <poll.h>
#include <signal.h>
#include <stdlib.h>
#include <string.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <time.h>
#include <unistd.h>

typedef struct
{
    char id[16];
    char name[16];
    char creation_log[64];
    char payload[928 + 9 * 1024];

} Packet;  // 10K area

int     check_fifo(const char*);
Packet* get_data(const char* payload);
int     show_data(Packet* pack);

check_fifo()
获取名称并返回 0(如果该名称的
FIFO
可用)。如果不在磁盘中,则会创建它。默认名称为
test_pipe
,但可以在命令行中输入,就像在
C
中一样。

O_NONBLOCK
FIFO

的服务器端

如果读卡器已连接,则

open
调用会立即返回;如果没有读卡器,则返回
-1
ENXIO
。 举个例子,调用
fnctl()
来获取和设置
FIFO
大小:

然后这个

monitor
struct
被传递到
poll
并按照预期使用这样的名称,监视管道另一侧的读取器是否存在或超时。这就是全部了。

    struct pollfd monitor = {
        .fd = out, .events = POLLERR, .revents = 0};

这是电话:

    int ch = poll(&monitor, 1, 5000);

这是服务器结束的一种方式:当阅读器关闭时,服务器获得

POLLERR
。其他方式是通过信号,通过
SIGINT
,按 Control-C 或将信号发送到进程。
pid
位于发送给客户端的日志中,因此甚至可以使用
kill
从那里发送。
5000
是 5 秒超时。

代码
server.c

#define _GNU_SOURCE
#include "stuff.h"

void game_over(int S);

int main(int argc, char* argv[])
{
    int                out          = -1;
    size_t             res          = 0;
    const unsigned int interval     = 2;
    const char*        default_name = "test_pipe";
    char               f_name[150]  = {0};

    signal(SIGINT, game_over);
    if (argc > 1)
        strcpy(f_name, argv[1]);
    else
        strcpy(f_name, default_name);

    res = check_fifo(f_name);
    if (res != 0) return -1;

    while (1)
    {
        out = open(f_name, O_WRONLY | O_NONBLOCK);
        if (out < 0)
        {
            if (errno == ENXIO)
            {
                fprintf(stderr, "    ENXIO: No readers for \"%s\"\n", f_name);
                fprintf(stderr, "    Waiting...\n");
                sleep(interval);
                continue;
            }
            fprintf(
                stderr, "    Error opening \"%s\": %s\n", f_name,
                strerror(errno));
            return -3;
        };
        fprintf(stderr, "    \"%s\" is open for writing\n", f_name);
        break;
    }
    // now a server is online and there is at least one
    // reader
    res = fcntl(out, F_GETPIPE_SZ);
    fprintf(stderr, "    original FIFO size is = %lu\n", res);
    // now change size to 10 Packets
    res = fcntl(out, F_SETPIPE_SZ, sizeof(Packet));
    if (res == 0)
    {
        fprintf(stderr, "    Error setting size of FIFO\n");
        close(out);
        return -3;
    }
    res = fcntl(out, F_GETPIPE_SZ);
    fprintf(
        stderr,
        "\
        FIFO size is now %lu\n\
        size of packet is %lu\n",
        res, sizeof(Packet));

    struct pollfd monitor = {.fd = out, .events = POLLERR, .revents = 0};
    while (1)
    {
        Packet* pack = get_data(NULL);
        res          = write(out, pack, sizeof(Packet));
        if (res == sizeof(Packet))
        {
            show_data(pack);
            free(pack);
        }
        else
        {
            free(pack);
            fprintf(stderr, "\t%lu bytes recorded at FIFO\n", res);
            if (res < 0)
            {
                fprintf(
                    stderr, "    Error writing to \"%s\": %s\n", f_name,
                    strerror(errno));
            }
            break;
        }
        // wait for the reader to consume the data
        int ch = poll(&monitor, 1, 5000);
        if (ch == 0) continue;  // timeout
        // readers are gone
        fprintf(stderr, "    reader is gone. Aborting\n");
        break;
    }
    close(out);
    return 0;
}  // main

void game_over(int S)
{
    fprintf(
        stderr,
        "\n\tSIGINT received: program interrupted. "
        "Aborting.\n");
    exit(-3);
}

这只是一个玩具。我们不应该在信号处理程序中调用

fprintf()

客户端:
client.c

这更简单:

由于

open
总是成功,客户端只需使用
poll()
并等待。如果
poll()
得到
POLLIN
,则有东西可读。如果它得到
POLLHUP
那么服务器就死了。等待服务器上线时可能会出现超时。

#define _GNU_SOURCE
#include "stuff.h"

int main(int argc, char* argv[])
{
    const char*        default_name = "test_pipe";
    char               f_name[150]  = {0};
    if (argc > 1)
        strcpy(f_name, argv[1]);
    else
        strcpy(f_name, default_name);

    int res = check_fifo(f_name);
    if (res != 0) return -1;

    int in = open(f_name, O_RDONLY | O_NONBLOCK);
    if (in < 0)
    {
        fprintf(
            stderr, "    Error opening \"%s\": %s\n", f_name, strerror(errno));
        return -3;
    };
    fprintf(stderr, "    \"%s\" is open for reading\n", f_name);

    struct pollfd monitor = {.fd = in, .events = POLLIN, .revents = 0};
    Packet        pack    = {0};
    while (1)
    {
        int ch = poll(&monitor, 1, 8 * 1000);
        if (ch == 0)
        {
            fprintf(stderr, "    timeout: no data from server...\n");
            break;  // timeout
        }

        if (monitor.revents & POLLIN)
        {  // normal
            res = read(in, &pack, sizeof(pack));
            if (res == sizeof(Packet))
            {
                show_data(&pack);
                continue;
            }
            else
            {
                fprintf(stderr, "    fragmented: %d bytes read\n", res);
                break;
            }
        }

        if (monitor.revents & POLLHUP)
        {
            fprintf(stderr, "\tPOLLHUP: the server is gone\n");
            break;
        }
        fprintf(stderr, "\trevents: %d\n", monitor.revents);
        break;

    };  // while()
    close(in);
    return 0;
}  // main

stuff.c
:辅助函数

#include "stuff.h"

#include <stdio.h>

int check_fifo(const char* f_name)
{
    fprintf(stderr, "    FIFO is \"%s\"\n", f_name);
    int res = mkfifo(f_name, 0777);
    if (res == 0) return 0;
    if (errno == EEXIST)
    {
        fprintf(stderr, "    \"%s\": already exists\n", f_name);
        return 0;
    }
    fprintf(stderr, "    Error opening \"%s\": %s\n", f_name, strerror(errno));
    return -1;
}

Packet* get_data(const char* payload)
{
    static int   usn  = 999;
    unsigned int pid  = getpid();
    Packet*      pack = malloc(sizeof(Packet));
    if (pack == NULL) return NULL;
    // id counts up from usn
    sprintf(pack->id, "%d", ++usn);
    // name is "Process nnNNnn"
    sprintf(pack->name, "process %06d", pid);
    // creation_log is
    //"yy:mm:dd hh:mm:ss from server running as process
    // 123456"
    char       timestr[20];
    time_t     tm;
    struct tm* temp;
    tm   = time(NULL);
    temp = localtime(&tm);
    if (temp == NULL)
    {
        free(pack);
        return (NULL);
    }
    int res = strftime(timestr, sizeof(timestr), "%y/%m/%d %H:%M:%S", temp);
    if (res == 0)
    {
        fprintf(stderr, "strftime returned 0");
        free(pack);
        return NULL;
    }
    sprintf(
        pack->creation_log, "%s %s id is %s", timestr, pack->name, pack->id);
    return pack;
};

int show_data(Packet* pack)
{
    if (pack == NULL) return -1;
    fprintf(
        stderr,
        "\
        Packet = {\n\
            id: \"%s\",\n\
            name: \"%s\",\n\
            %s\n\
        }\n\n",
        pack->id, pack->name, pack->creation_log);
    return 0;
};

输出

服务器在终端上启动,没有读取器,由 control-C 取消

[SO@Fedora38 fifo]$ ./server new_pipe
    FIFO is "new_pipe"
    "new_pipe": already exists
    ENXIO: No readers for "new_pipe"
    Waiting...
    ENXIO: No readers for "new_pipe"
    Waiting...
^C
        SIGINT received: program interrupted. Aborting.

客户端在没有服务器的情况下启动

[SO@Fedora38 fifo]$ ./client new_pipe
    FIFO is "new_pipe"
    "new_pipe": already exists
    "new_pipe" is open for reading

服务器启动,阅读器在线,开始发送数据

[SO@Fedora38 fifo]$ ./server new_pipe
    FIFO is "new_pipe"
    "new_pipe": already exists
    "new_pipe" is open for writing
    original FIFO size is = 65536
        FIFO size is now 16384
        size of packet is 10240
        Packet = {
            id: "1000",
            name: "process 005903",
            23/10/10 02:43:19 process 005903 id is 1000
        }

以及客户端相应的输出:

[SO@Fedora38 fifo]$ ./client new_pipe
    FIFO is "new_pipe"
    "new_pipe": already exists
    "new_pipe" is open for reading
        Packet = {
            id: "1000",
            name: "process 006074",
            23/10/10 02:45:46 process 006074 id is 1000
        }

        Packet = {
            id: "1001",
            name: "process 006074",
            23/10/10 02:45:51 process 006074 id is 1001
        }

        POLLHUP: the server is gone

当服务器端结束

poll()
时,客户端得到
POLLHUP

当客户端结束

poll()
时,服务器端得到
POLLERR
,服务器也结束。

[SO@Fedora38 fifo]$ ./server new_pipe
    FIFO is "new_pipe"
    "new_pipe": already exists
    "new_pipe" is open for writing
    original FIFO size is = 65536
        FIFO size is now 16384
        size of packet is 10240
        Packet = {
            id: "1000",
            name: "process 006343",
            23/10/10 02:48:34 process 006343 id is 1000
        }

    reader is gone. Aborting
© www.soinside.com 2019 - 2024. All rights reserved.