2022面向对象程序设计(福州大学)寒假作业2
这个作业属于哪个课程 | 2022面向对象程序设计 (福州大学) |
---|---|
这个作业要求在哪里 | 2022面向对象程序设计(福州大学)寒假作业2 |
这个作业的目标 | 路由器根据相应规则对数据包进行操作,现给出规则集及输入数据包,输出相应数据包最佳匹配的规则 |
作业正文 | 见下文 |
其他参考文献 | IP地址分类及CIDR划分方法 VS2015中使用fstream类时 “error C2280尝试使用已删除的函数”的处理方法 |
Github仓库请点击这里 C和C++的程序完整源代码均已上传到GitHub仓库
完成本次作业需要学习的内容
- 如何从文件输入数据以及将数据输出到文件
- 不同源文件之间如何关联到同一个程序中
- 对于IP地址块CIDR表示法的理解
学习过程
从文件读取数据和将数据输出到文件(Visual Studio 2022编译器,c文件):
/*从文件读取数据和将数据输出到文件*/
#include <stdio.h>
#include <stdlib.h>
int main()
{
FILE* fp, * out;
char z[9854] = {};
char input[2022] = {};//存储数据来源文件的完整路径
char output[2022] = {};//存储数据输出文件的完整路径
int i;
printf("请输入数据来源文件的完整路径:\n");
gets(input);
printf("请输入数据输出文件的完整路径:\n");
gets(output);
fp = fopen(input, "r");//“r”表示以只读模式打开文件
out = fopen(output, "w+");//“w+”表示打开可读写文件,若文件存在则文件长度清为零,即该文件内容会消失。若文件不存在则建立该文件。
while ((fscanf(fp, "%s", z)) != EOF)//从文件中按行读取相应类型数据,EOF表示读取到文件末尾后结束循环
{
fprintf(out, "%s\n", z);//将相应类型数据输出到文件
}
fclose(fp);//关闭文件指针对应文件
fclose(out);
system("pause");
return 0;
}
这段程序完全使用C语言,利用C语言中的fopen、fclose、fscanf、fgets、fprintf等文件函数实现。
完成C语言代码后,我也尝试使用C++的fstream中对于文件的一些操作,并取得成功,其核心部分如下(Visual Studio 2022编译器,cpp文件):
#include <iostream>
#include <fstream>
char rule_packet[9854] = {};//存储规则集文件的完整路径
char packet[9854] = {};//存储数据集文件的完整路径
char output[9855] = {};//存储数据匹配结果文件的完整路径
cout << "请输入规则集文件的完整路径(可含空格,不含引号):" << endl;
gets_s(rule_packet);
cout << "请输入数据集文件的完整路径(可含空格,不含引号):" << endl;
gets_s(packet);
cout << "请输入数据匹配结果文件的完整路径,若文件不存在,则会自动新建文件(可含空格,不含引号):" << endl;
gets_s(output);
ifstream fp_rule(rule_packet, ios::in | ios::_Nocreate);//in表示从文件输入,_Nocreate表示若文件不存在,则不新建文件
fp_rule >> a >> b >> c;
ifstream fp_packet(packet, ios::in | ios::_Nocreate);
ofstream fp_out(output, ios::out);//out表示输出到文件,且文件原本的内容将会被清空
fp_out << a << b << c;
关于IP地址的CIDR表示法,我主要参考这篇CSDN博客文章学习的:IP地址分类及CIDR划分方法
C++中类的基本使用:
class rule
{
public:
int in(ifstream& fp_rule);//fstream的拷贝(赋值)构造函数是已删除函数,“&”表示是将函数的形参设置为引用类型,防止报错
class rule* create_chain_table(ifstream& fp_rule);//通过成员函数实现对私有部分变量的调用,成员函数必须在公有部分才能被调用
void visit_chain_table(rule* p);
void visit_processed_chain_table(rule* p, ifstream& fp_packet, ofstream& fp_out);
private:
char ip0bin[37], ip1bin[37], z0[3], z1[3];
unsigned int ip0min, ip0max, ip1min, ip1max;
int ip01,ip02,ip03,ip04,ip0wei;
int ip11,ip12,ip13,ip14,ip1wei;
int d01, d02, d11, d12, x0, x1;
char x;
rule* next;
};
void rule::visit_processed_chain_table(rule* p, ifstream& fp_packet, ofstream& fp_out)
{
unsigned int ip0, ip1, d0, d1, y, count, flag;
rule* pt = p;//存放第一个结点的指针
while (fp_packet >> ip0 >> ip1 >> d0 >> d1 >> y)
{
for (p = pt, count = 0, flag = 0; p->next; p = p->next)
{
if (rule_match(ip0, ip1, d0, d1, y, p->ip0min, p->ip0max, p->ip1min, p->ip1max, p->d01, p->d02, p->d11, p->d12, p->x0, p->x1))
{
fp_out << count << endl;
flag = 1;
break;
}
count++;//计数器,表示当前为第几条规则【规则从0开始编号】
}
if (!flag)fp_out << "-1\n";
}
}
int main()
{
rule* head, * p;
p->visit_processed_chain_table(p, fp_packet, fp_out);
return 0;
}
这里仅展示类的使用的部分代码,完整代码请移步GitHub仓库。
程序设计思路
将规则集的CIDR点分十进制IP地址转换为32个二进制数字存储在字符数组中,采用从右往左四个数字分别取余逆序填入字符数组,每8位二进制数为一组操作,不足8位的前面补0,具体代码如下:
void IP_Switch_From_Dotted_DEC_To_BIN(int ip1, int ip2, int ip3, int ip4, char ip_bin[])
{
char* p;
for (p = ip_bin + 32; ip4; ip4 /= 2, p--)
*p = ip4 % 2 + '0';
if (p != ip_bin + 24)
for (; p > ip_bin + 24; p--)
*p = '0';
for (; ip3; ip3 /= 2, p--)
*p = ip3 % 2 + '0';
if (p != ip_bin + 16)
for (; p > ip_bin + 16; p--)
*p = '0';
for (; ip2; ip2 /= 2, p--)
*p = ip2 % 2 + '0';
if (p != ip_bin + 8)
for (; p > ip_bin + 8; p--)
*p = '0';
for (; ip1; ip1 /= 2, p--)
*p = ip1 % 2 + '0';
if (p != ip_bin)
for (; p > ip_bin; p--)
*p = '0';
}
之后利用位运算将32为二进制数字转换为一个十进制数,代码如下:
void IP_Switch_From_BIN_To_DEC(unsigned int *p,char x[])
{
int i, m;
*p = 0;
for (i = 1, m = 31; i <= 32; i++, m--)
*p += (x[i] - '0') * (1 << m);//利用位运算将二进制IP地址转换为十进制
}
然后根据IP地址的CIDR表示法中“/”之后的数字确定网络前缀位数,进而确实主机号位数,将主机号分别置0和置1可分别得到IP地址块的最小地址和最大地址。
再根据协议号表示及匹配规则确定可以匹配的协议号范围。
最后与数据包匹配,找到最佳匹配规则。
【注:完整的C++和C语言代码均已上传GitHub,此处不再展示】
测试程序
开始程序还存在严重bug无法输出正确结果,有时候实在找不到错误会使用逐语句调试模式,实时观察各个变量的值。
待程序严重bug修复、可以输出正确结果之后,对于程序的使用方式进行了部分完善,并进行调试。
C语言编写的程序调试过程:
C++编写的程序和简易文件比较程序调试过程:
计算程序匹配及输出过程的所用时间:
遇到的困难
- 对于C++特色的类(class)的使用非常不熟练。解决办法:参考C++程序设计书本上的例子,自己先模仿书本例子编写一段程序调试,并尝试使用成员函数访问类中的私有部分
- 链表以及文件流使用过程中遇到各种奇奇怪怪的问题。
- 使用C++的类编写程序时,尝试将定义为ifstream的fp_rule和定义为ofstream的fp_out的变量作为参数传递给类成员函数是出现“尝试调用已删除函数”的报错。解决办法:参考CSDN这篇文章VS2015中使用fstream类时 “error C2280尝试使用已删除的函数”的处理方法,将形参改为引用类型(加上“&”)即可
class rule* rule::create_chain_table(ifstream& fp_rule)
一些缺陷
- 没有算法优化,纯粹暴力算法,程序匹配时间过长
- 使用C++的类及类的链表时过于繁琐冗杂
- 未能成功将C++编写的代码中类的相关模块独立成文件
- 未能分析程序的时间复杂度
- 未能安装题目要求实现利用命令行执行并输入文件,因此还是跟以前一样写了个交互式界面
本人能力有限,代码肯定还存在各种尚未发现的问题,欢迎大家批评指正。