我试图通过 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
下面是使用
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