代码改变世界

结对编程项目——C语言实现WordCount Web化

2018-10-17 22:40  星河流放  阅读(423)  评论(0编辑  收藏  举报

结对编程项目

代码地址

201631062219,201631011410

gitee项目地址:https://gitee.com/xxlznb/pair_programming

作业地址:https://edu.cnblogs.com/campus/xnsy/2018Systemanalysisanddesign/homework/2188

团队PSP

PSP2.1 PSP阶段 预估耗时(分钟) 实际耗时(分钟)
Planning 计划 30 40
-Estimate -估计这个任务需要多少时间 20 25
Development 开发 600 740
-Analysis -需求分析 120 80
-Design Spec -生产设计文档 0 0
-Design Review -设计复审 0 0
-Coding Standard -代码规范 10 10
-Design 具体设计 100 240
-Coding -具体编码 360 480
-Code Review -代码复审 30 60
-Test -测试 90 30
Reporting 报告 60 60
-Test Report -测试报告 0 0
- Size Measurement -计算工作量 0 0
-Postmortem & Process Improvement Plan 事后总结,提出过程改进计划 30 30
合计 810 985

项目介绍

项目的主要语言为C语言,C语言作为一种平台依赖性较小的语言,同时也是性能非常优秀的语言,同时也是为了玩,所以我们使用了C语言作为开发语言,但是C语言的图形界面并不方便使用,因此我们换了一个思路,我们将这个项目web化了。

同时,web后端也使用C作为开发语言。服务器架构为Apache+CGIC库

CGI——common gateway interface,通用网关接口,我们将客户端提交的http请求通过Apache服务器拆解后发送至CGI程序,由CGI程序处理后输出相应,在此项目中,我们需要解决多文件上传的问题,我们使用CGIC库协助我们开发,CGIC库是一个极度简单的库(https://boutell.com/cgic/)这里为他的主页。

![2018-10-17 21:48:13屏幕截图](/home/xxl/Pictures/2018-10-17 21:48:13屏幕截图.png)

以上为处理逻辑,我们为了保证原始执行程序的独立性,我们只对起做基础包装,CGIC库由于不信任临时文件,因此在API中隐藏了临时文件的信息,无法直接获取临时文件,它推荐的做法是,如果需要保存文件则直接提供临时文件指针,但我们的wc是输入文件路径的,为了直接获取CGIC的临时文件路径,我们得修改库函数,在cgi.c中添加

cgiFormResultType cgiFormTempFileName(
    char *name, char *result, int resultSpace)
{
    cgiFormEntry *e;
    int resultLen = 0;
    char *s;
    e = cgiFormEntryFindFirst(name);
    if(!e){
        strcpy(result,"");
        return cgiFormNotFound;
    }
    s = e->tfileName;
    while(*s) {
        APPEND(result, *s);
        s++;
    }
    if(resultSpace) {
        result[resultLen] = '\0';
    }
    if(!strlen(e->tfileName)) {
        return cgiFormNoFileName;
    } else if (((int) strlen(e->tfileName)) > (resultSpace - 1)) {
        return cgiFormTruncated;
    } else {
        return cgiFormSuccess;
    }

}

同时在cgi.h中添加

extern cgiFormResultType cgiFormTempFileName(
    char *name, char *result, int max);

之后就可以通过这个函数直接获取临时文件的路径了,之后我们将临时文件与文件对应的checkbox中的数据进行拼接即可获得一条shell命令。

在获取到命令后,我们使用popen

popen()函数通过创建一个管道,调用fork()产生一个子进程,执行一个shell以运行命令来开启一个进程。可以通过这个管道执行标准输入输出操作。这个管道必须由pclose()函数关闭,必须由pclose()函数关闭,必须由pclose()函数关闭,而不是fclose()函数(若使用fclose则会产生僵尸进程)。pclose()函数关闭标准I/O流,等待命令执行结束,然后返回shell的终止状态。如果shell不能被执行,则pclose()返回的终止状态与shell已执行exit一样。

type参数只能是读或者写中的一种,得到的返回值(标准I/O流)也具有和type相应的只读或只写类型。如果type是"r"则文件指针连接到command的标准输出;如果type是"w"则文件指针连接到command的标准输入。command参数是一个指向以NULL结束的shell命令字符串的指针。这行命令将被传到bin/sh并使用-c标志,shell将执行这个命令。

popen()的返回值是个标准I/O流,必须由pclose来终止。前面提到这个流是单向的(只能用于读或写)。向这个流写内容相当于写入该命令的标准输入,命令的标准输出和调用popen()的进程相同;与之相反的,从流中读数据相当于读取命令的标准输出,命令的标准输入和调用popen()的进程相同。

FILE *fp = NULL; 
char c;
fp = popen(command, "r");
if(!fp)
{
    perror("popen");
    exit(EXIT_FAILURE);
} 
while(!feof(fp))
{
	c=fgetc(fp);
    if(c==-1)break;
    printf("%c",c);
}
pclose(fp);

目前已知的问题

前端丑,后端处理后的返回页面没有做,部分功能没有实现。仍待优化。

项目总结

总结:

通过本次项目,我们学到了很多一个人做项目的时候不会学到的东西,掌握了很多新知识,学了很多之前想学而又没时间学习的知识。

首先从结对的角度上来说,一个人写代码的过程是枯燥的,而两个人可以很好的从队友的反馈中调节自己的心态,能够在最艰难的时期咬牙坚持下去。在做停用词功能的时候,由于在一个过程函数中返回了栈中的指针,导致外部程序永远都无法正确地访问到真正的字符串地址,最恐怖的是他在调试的时候一切都是正常的,直到这部分内存被回收之后程序才会报错。我在这个部分卡了整整半个小时,直到后来意识到靠自己确实无法解决这个BUG之后,叫来了结对的小伙伴,我们俩一起审查代码过后找到了原因。这是我在这个项目过程中离放弃最近的一次,多亏结对编程,我最后才能成功解决这个BUG。并且两个人一起写代码过程中,积极性都普遍高于一个人写代码,做项目的这几天里,除了其他不得不完成的任务,其他的时间几乎全部投入到了代码的编写及测试的过程中。

其次从项目内容的角度上来说,我们项目的独立性非常高,从前端到处理Http请求,从Http请求到参数的获取以及文件的获取,从参数到文件的获取到参数的解析以及文件的处理,整个过程基本上都是由我们自己从底层自己搭建的。也因此我们花费了大量的时间去造轮子,导致花在项目主体上的时间不够多,因此可能一部分功能还待实现。但是我们的目标是把这个项目做大做好,不仅仅是为了应付这一次作业,而是做一个长期的打算,通过这个项目去学习我们之前没有学习过的内容,掌握我们还不熟悉的知识,不断将我们的项目完善。

从代码上来看,我们代码耦合度不高,符合面向过程设计规范,很多代码都可以复用。并且我们从开始就考虑了可拓展性,很多可能几行代码就可以实现的功能我们都尽量做到泛化,增强它的可拓展性。正如前面所提到的,我们将这个项目作为一个长期项目来考虑,避免Magic Number以及模棱两可的变量名,做到看名其意。

从优化的角度上来看,我们计划在接下来的时间实现多进程取文件,提升读取效率。权衡时间与空间复杂度,将涉及到查询的代码重构成具有更高效率的查询方式。将前端的页面重新设计并实现。

总而言之,本次项目收获很多,"Learning by doing",孔子曾经说过:“吾听吾忘,吾见吾记,吾做吾悟”,我们在以后的学习中,任将承这种学习态度,在实践中感悟,在实践中成长。