3.1需求分析
进程A向进程B传递一个复杂的结构数据,至少包括两类成员:整数型,字符串型。B接收到数据后,将其中的字符串逆序后,再将数据发送给进程A。
即在进程A中创建一个结构体my_message,该结构体包含char数组words和整形数据key,要求将进程A中的结构体序列化后经管道传送给进程B,由进程B反序列化后输出,并将结构体中的words逆序通过管道返回给进程A。
输入:在进程A的终端输入结构体的信息,包括字符串和整型数据。
输出:进程B的终端显示由进程传入的结构体信息,并在进程A中显示返回的逆序字符串信息。
3.2概要设计
由于进程通信以及字符处理涉及到很多头文件的引入,故使用统一的头文件con.h进行管理,头文件中con.h包含一些变量的声明(fifo文件的命名以及字符缓冲区输入的大小),以及string.h,stdlib.h头文件,便于进行字符串的复制,转化(数字和字符数组)及连接,和必要的管道文件。
结构体my_message,以及输出结构体信息的函数print_message(struct my_message m),进程A通过以及进程B主要通过main函数完成所需功能。
对于通信过程,首先调用mkfifo函数创建相应的fifo文件,即fifo1和fifo2,用于进程A和进程B之间的双向通信。同时进行必要的错误处理(重要,用于判断程序运行到哪一步出错了),然后进程获取管道的文件句柄(设置为只有写或读权限)以完成通信和必要处理。
3.3详细设计
模块一:print_message
该模块接受结构体my_message,逐行输出结构体的字符内容和整型数据
模块二:reverse
该模块接收字符串左右指针位置,设置left和right,通过交换left和right所指向的字符实现逆序。在循环中,left递增,right递减。
模块三:进程A的main函数
:定义message结构体,以及管道缓存buf,和字符数组strk。
:调用mkfifo创建进程A向进程B通信的命名管道fifo1。
:从键盘输入结构体的信息,并调用print_message函数输出结构体信息。
:将结构体序列化至buf中,首先调用库函数sprintf将message.key转为strk,接着调用strcpy和strcat完成对结构体的序列化,内容存在buf数组中
:打开管道,进行通信,同时在收到进程B所传送的消息后,打印输出并关闭管道,结束通信。
模块四:进程B的main函数
:设置buf,以及逆序返回的数组rebackmes和用于进行字符数字转换的strk数组。
:调用mkfifo创建进程B向进程A通信的命名管道fifo2。
:通过open函数打开管道fifo1和fifo2。
:读取fifo1中的buf数据,通过for循环对buf中的数字和字符分割
其中,数字赋值给strk,字符赋值给rebackmes。
:调用strcpy和atoi库函数完成对进程B中的结构体的赋值(反序列化)。
:通过reverse函数对rebackemes字符数组逆序并返回给进程A。最后关闭管道,结束通信。
3.4调试分析
分析一:mkfifo创建fifo失败
当运行一次程序之后再次运行程序会提示创建fifo失败,需注意,第一次运行程序时已经创建了fifo文件,而使用mkfifo创建fifo文件时,需确保该文件不存在,否则会出现冲突而创建失败。若需二次运行时需要删除初次运行时产生的fifo文件,才可以第二次运行。
同时注意mkfifo需要在main函数前执行,而不能放在scanf后,否则程序会卡死。
分析二:结构体是否应在进程B中实例化
进程A和进程B都需要创实例化my_message结构体,为了使程序更健壮,若在进程B中不实例化该结构体,只对数据进行处理,而用户并不知情,可能会输出相反的顺序,造成程序输出错误,不能为了简化编写,而省略进程B的实例化过程。
分析三:linux系统向windows文件系统复制文件失败
Linux系统可以访问Windows系统的文件,而Windows系统无法访问Linux文件,移动con.h这个文件时,在Linux系统中显示文件移动失败。需注意con是windows系统的保留字,需将代码命名改为con1.h。
a.c
#include "con.h"
struct my_message{
char words[maxsize];
int key;
};
void print_message(struct my_message m)
{
printf("该结构数据的字符部分为:%s\n", m.words);
printf("该结构数据的整型部分为:%d\n", m.key);
}
int main()
{
struct my_message message;//自定义结构数据
char buf[maxsize];//用于命名管道的通信缓存
char strk[maxsize];//用于存储被转化为字符的整型数据
//mkfifo需要放在所有程序之前 不然会出错
int re1 = mkfifo(a2b, S_IFIFO | 0666);
if (re1 == -1){
printf("mkfifo a2b error\n");
return 1;
}
printf("这里是进程A,请输入发送给进程B的结构数据(数据由字符和整数构成):\n");
scanf ("%s %d", message.words, &message.key);
print_message(message);
//将结构体序列化至buf中,具体操作为
// 将数字转为字符串itoa不能用
//将message.words复制到buf里
// 将字符和数字连接
sprintf(strk, "%d", message.key);
strcpy(buf, message.words);
strcat(buf,strk);
// 进程A使用fifo1,向里面写数据
// 进程A通过打开fifo2,读里面的数据 并进行错误处理
int fda = open(a2b, O_WRONLY);
int fdb = open(b2a, O_RDONLY);
if ((fda < 0) | (fdb < 0))
{
printf("open file error");
return 1;
}
//向管道一写数据
//为接收数据,清空buf
//读取管道二的数据
write(fda,buf,strlen(buf)+1);
memset(buf, '\0', sizeof(buf));
read(fdb,buf,sizeof(buf));
printf("进程B返回的数据为:%s\n", buf);
close(fda);
close(fdb);
return 0;
}
b.c
#include "con.h"
struct my_message{
char words[maxsize];
int key;
};
//反转字符串函数,为原地翻转
void reverse(char* left, char* right)
{
while (left<right)
{
char tmp = 0;
tmp = *left;
*left = *right;
*right = tmp;
left++;
right--;
}
}
void print_message(struct my_message m)
{
printf("这里是进程B,反序列化后所得结构数据的信息如下:\n");
printf("该结构数据的字符部分为:%s\n", m.words);
printf("该结构数据的整型部分为:%d\n", m.key);
}
int main()
{
struct my_message message;
char buf[maxsize];
char rebackmes[maxsize];
char strk[maxsize];
//进程B创建fifo2
int re1 = mkfifo(b2a, S_IFIFO | 0666);
if (re1== -1){
printf("mkfifo b2a error\n");
return 1;
}
//进程B使用fifo1,读里面的数据
//进程B通过打开fifo2,向里面写数据 看这个顺序 需要先打开 A 后打开B
int fda = open(a2b, O_RDONLY);
int fdb = open(b2a, O_WRONLY);
if ((fda < 0) | (fdb < 0))
{
printf("打开文件错");
return 1;
}
int re2 = read(fda,buf,sizeof(buf));
if (re2 <= 0)
printf("读文件出错\n");
//反序列化buf中的信息
//提取buf中的信息
for (int i = 0, j = 0; i < strlen(buf); i++)
{
if (buf[i] >= '0' && buf[i] <= '9')
{
strk[j] = buf[i];
j++;
}
else
rebackmes[i] = buf[i];
}
strcpy(message.words, rebackmes);//将rebackmes复制到message.words里
message.key = atoi(strk);//将字符数组转化为整数
print_message(message);//输出B所接受到的结构数据的信息
reverse(rebackmes, rebackmes + strlen(rebackmes) - 1);
int re3 = write(fdb,rebackmes,strlen(rebackmes)+1);
if (re3 <= 0)
printf("写文件出错\n");
close(fda);
close(fdb);
return 0;
}
con1.h
#ifndef CON1_H
#define CON1_H
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
#include <string.h>
#define a2b "./fifo1"
#define b2a "./fifo2"
#define maxsize 10000
#endif