admin 管理员组

文章数量: 887031


2023年12月23日发(作者:shell脚本判断用户参数是数字)

一、 引言(简要说明设计题目的目的、意义、内容、主要任务等)

1.1设计目的

本次系统软件课程设计的主要目的有两个:一方面是分析设计linux源代码,另一方面是进行系统级的程序设计。

1.2 题目与要求

我计划编写的题目是:⑴小学算术运算测试程序。制作一个可供小学数学运算的程序:10以内加减法,能根据输入题数出题,判断题是否正确,最后计算分数。并用make工程管理器编译,编写makefile文件。⑵简单聊天程序。在linux下用C语言编写一个简单的网络聊天程序。实现网络传输功能。

1.3内容及主要任务

⑴小学算术运算测试程序:要求完成 10 以内的加减运算。 能根据用户 输入的想要练习的题目数自行出题。用户输入答案后可以判断正误,并能给出算对及算错的题目数,最后计算出分数。同时,对于用户算错的题目会发出报警声,提示用户注意,同时给出正确结果。做完一次测试后用户可决定是否继续进行下一次测试。

⑵简单聊天程序:本课题是建立聊天通信模型,设计一个聊天室软件, 包括服务器端和客户端, 主要功能为:Ⅰ、 服务器端功能:①初始化 socket创建服务器端。②维护一个链表,保存所有用户的 IP 地址、端口信息。③接受用户传送来的聊天信息,然后向链表中的所用用户转发。④接受用户传送来的连接判断命令,并向用户发出响应命令。Ⅱ、 客户端功能: 客户端界面上的两个文本框,一个用于显示接受的聊天信息,一个用来接受用户输入的聊天信息。当按下“发送”按钮时将信息发送给服务器。

1.4题目设计意义

⑴通过本课题的毕业设计,熟悉了关于linux下C语言的系统软件程序设计,可以比较深入的了解和掌握 WINSOCK 控件基本属性、方法和事件,理解 网络聊天通信的概念,输控制协议(TCP)进行数据交流,初步掌握网络聊天通信程序的设计方法。 并能巩固和扩展大学期间的学习内容,进行项目的设计开发训练,更好的适应社会的需求。

⑵随着计算机网络技术的快速发展,人们的交流方式越来越多,传统的通信方式在速度和可靠性方面已经很难满足人们的需要,即时通信系统已成为人们的新宠。同时,即时通信系统对现代企业也有着重大意义,它为诸企业开拓了网络应用的新领域。自从它诞生以来,以实时交互、资费低廉等优点,受到广大个人用户的喜爱,已经成为网络生活中不可或缺的一部分。本着学以致用的原则,本人开发了这套多点聊天系统,以满足网络用户的通讯需求。该系统具有操作简单、界面友好、功能专一等特点。

⑶本报告就系统的开发过程做了详细的介绍,并对系统的原理、总体设计等方面做了深入细致的讨论。

二、 正文(课程设计的主要内容,包括实验与观测方法和结果、仪器设备、计算方法、编程原理、数据处理、设计说明与依据、加工整理和图表、形成的论点和导出的结论等。正文内容必须实事求是、客观真切、准确完备、合乎逻辑、层次分明、语言流畅、结构严谨,符合各学科、专业的有关要求。)

任务一:小学数学运算程序

2.1.1主要内容

本程序共分为五个模块:出题模块、答题模块、检查计分模块、评分模块和评价模块;下面分别对这五个模块的算法做一下介绍。

⑴出题模块:一个答题系统自然而然首先就是要有题目且能够根据条件出题;在本模块中,首先设了一个含有15个指针的指针数组,指针数组里的每个指针都指向了相应题号的 1

选择题,这样就实现了对题目的储存;然后用 scanf 函数来实现从键盘上输入题号,用 if

语句控制输题的范围;题号不在 1--15 的范围内就提示选题出错;在输入题号的过程中输入相同题号是在所难免的;但 是在答题系统中输入相同题号显然没有意义,所以另外设了一个静态一维数组来储存已输过的题号;且数组的第一个元素初始化为 0,然后每输入一个题号就赋给这个数组,同时用 for 语句来使当前的题号一一与储存在一维数组中已输过的题号进行比较,如题号有相同,就提示重答,重答的功能是通过对出题模块函数的递归调用来实现的,如题号没有相同,就通过输入的题号借助指针数组里的指针找到与题号对应的选择题,用 puts 函数来实现对选择题的输出;最后用 return 语句将其他模块返回来每道题的小分数返回到主函数中(在主函 数中调用出题模块函数)。

⑵答题模块:出完题后自然是从键盘上输入答案了,在本模块中,提示输入答案后,用

scanf 函数来实现使从键盘上输入的一个答案选项(A、B、C、D) 赋给一个用来记录答案的字符变量;用 return 语句将这个变量返回到出题模块中。

⑶检查计分模块:输入答案选项后,就要判断答案的正确性和计分了,在本模块中,要设一个字符数组来储存每道题的正确答案;用 if 语句来控制在答题模块中输入的答案是否为

A、B、C、D 中的某一个;若不是,就提示选的答案不在答案范围内,若是,根据在出题模块中输的题号使在答题模块中输入的答案选项与储存正确答案数组中的相应答案选项相比较,若相同,就提示答案正确,与此同时用变量记录下这道题所得的小分,否则,就提示答案错误并给出当前题目的正确答案,不计分;用 return 语句将得分返回到出题系统中(在出题 模块函数中调用检查计分模块函数)。

⑷评分模块:做完题后,就要根据做题的对错个数来评分;在本模块中,首先就要提示题目全做完,用switch语句根据总小分来选择性地选出11个不同的分数中的一个分数,并且打印出该分数 (在主函数中调用评分模块函数) 。

⑸评价模块:最后一步就是要根据不同的总得分段来给出不同的评语,在本模块中用 if

语句根据不同的总分数段来打印出不同的评语(在主函数中调 用评价模块函数) 。

主函数将这五个模块直接地或间接地联系在一起,使它们构成本程序,现在就来介绍主函数的算法。主函数对出题模块函数、评价模块函数进行了调用;首先提示开始答题,为了要答 10 次题,就要设一个 for 语句来控制对出题模块函数调用的次数,即需要答一定次数的题,与此同时用一个变量来累加由出题模块函数返回来每道题的小分数;接下来分别对评分模块函数和评价模块函数进行调用。

以上就是对本程序算法的介绍。

2.1.2 程序结构及主要过程

⑴本程序共包含了顺序结构、选择结构、循环结构三种在 C 程序中的主要结构,其中穿插了数组(一维数组、字符数组)、指针 (指针数组)、函数调用(递归调用)等知识,该程序覆盖了本学期所学的除结构体外的大部分知识。

⑵本程序结构和过程思路如下:为了要答题,首先要有题目,设一个指针数组来实现;把相应的题目调出,应用指针的功能实现;调出题目后,就要给用户答题了,用一个输入函数实现;再经过系统的判断,用一个字符数组储存正确答案结合判断语句实现;最后累加计分,用循环结构实现。

⑶总之,该程序的总体结构和过程很好的体现了现在所提倡的对一个程序结构模块化的思想。

2.1.3 程序模块功能说明

本程序一共有五个模块,它们分别是:出题模块(question_out()),答题 模块(answer()),检查计分模块(check()),评分模块(count()),评价模块 (assessment()),现在就来分别介绍这五个模块的功能。

2

⑴出题模块(question_out()):这一模块主要负责对题目的储存和调出这两个功能。

⑵答题模块(answer()):这一模块主要负责将用户端的答案通过键盘输入到系统。

⑶检查计分模块(check()):这一模块主要负责检查判断用户所提供的答案是否正确并根据它来记录每题所的总分。

⑷评分模块(count()):这一模块主要负责统计总得分并打印。

⑸评价模块(assessment()) :这一模块主要负责根据给出的总得分打印相应的评语。

总之这五个模块都有着各自的功能且互相联系,五者在程序中缺一不可。

2.1.4 makefile介绍

⑴Makefile文件介绍

Makefile一个工程中的源文件不计数,其按类型、功能、模块分别放在若干个目录中,makefile定义了一系列的规则来指定,哪些文件需要先编译,哪些文件需要后编译,哪些文件需要重新编译,甚至于进行更复杂的功能操作,因为 makefile就像一个Shell脚本一样,其中也可以执行操作系统的命令。

⑵makefile主要功能

Make工具最主要也是最基本的功能就是通过makefile文件来描述源程序之间的相互关系并自动维护编译工作。而makefile 文件需要按照某种语法进行编写,文件中需要说明如何编译各个源文件并连接生成可执行文件,并要求定义源文件之间的依赖关系。makefile 文件是许多编译器--包括 Windows NT 下的编译器--维护编译信息的常用方法,只是在集成开发环境中,用户通过友好的界面修改 makefile 文件而已。

⑶自动化编译

Makefile带来的好处就是——“自动化编译”,一旦写好,只需要一个make命令,整个工程完全自动编译,极大的提高了软件开发的效率。make是一个命令工具,是一个解释makefile中指令的命令工具,一般来说,大多数的IDE都有这个命令,比如:Delphi的make,Visual C++的nmake,Linux下GNU的make。可见,makefile都成为了一种在工程方面的编译方法。

2.1.5 makefile规则

makefile中的规则是这样的:

TARGET „ : DEPENDENCIES „

COMMAND

目标(TARGET)程序产生的文件,如可执行文件和目标文件;目标也可以是要执行的动作,如“clean”。

依赖(DEPENDENCIES)是用来产生目标的输入文件,一个目标通常依赖于多个文件。

命令(COMMAND)是make执行的动作,一个可以有多个命令,每个占一行。注意:每个命令行的起始字符必须为TAB字符!

有依赖关系规则中的命令通常在依赖文件变化时负责产生target文件,make执行这些命令更新或产生target。规则可以没有依赖关系,如包含target “clean”的规则。

规则解释如何和何时重做该规则中的文件,make根据依赖关系执行产生或更新目标;规则也说明如何和何时执行动作。有的规则看起来很复杂,但都符合上述模式。

makefile中的规则描述如何生成特定的文件,即规则的目标。规则列出了目标的依赖文件,指定生成或更新目标的命令。规则的次序是不重要的,除非是确定缺省目标:缺省目标是第一个makefile中的第一个规则;如果第一个规则有多个目标,第一个目标是缺省的。有两个例外:以’.’开头的目标不是缺省目标;模式规则对缺省目标没有影响。通常我们所写的地一个规则是编译整个或makefile中指定的所有程序。

3

2.1.6 Makefile文件

main:main.o input.o chuti.o

gcc main.o input.o chuti.o -o main

main.o:main.c myhead.h

gcc -c main.c

input.o:main.c

gcc -c input.c

chuti.o:main.c

gcc -c chuti.c

2.1.7 源程序

#include

#include

void function( ){

int index,itest_total,N=200;

int iflag,num1,num2,itotal,iarrsum[200],iarranswer[200],iarrop[4];

itotal=0;

printf("请输入题数:n");

scanf("%d",&itest_total);

for(index=0;index

{

iflag=rand()%4+1;

switch(iflag)

{

case 1:

num1=rand()%10;

num2=rand()%10;

iarrsum[index]=num1+num2;

printf("%d+%d=n",num1,num2);

break;

case 2:

num1=rand()%10;

num2=rand()%10;

iarrsum[index]=num1-num2;

printf("%d-%d=n",num1,num2);

break;

case 3:

num1=rand()%10;

num2=rand()%10;

iarrsum[index]=num1*num2;

printf("%d*%d=n",num1,num2);

break;

case 4:

num1=rand()%10;

num2=rand()%10;

4

iarrsum[index]=num1/num2;

printf("%d/%d=n",num1,num2);

break;

default:

break;

}

}

printf("依次输入每题的答案:n");

for(index=0;index

{

scanf("%d",&iarranswer[index]);

getchar();

if(iarranswer[index]==iarrsum[index])

itotal++;

}

}

printf("正确率:%f%%n",100.0*((itotal*1.0)/itest_total));

}

int main()

{

char flag; //开始有一轮测试,以后根据用户的输入开始新一轮测试或结束测试;

do{

function();

printf("请输入'Y'表示开始新一轮测试,其他字母表示结束测试:n");

scanf("%c",&flag);

}while(flag!= 'Y');

return 0; }

2.1.8 试验过程及结果

⑴输入题数5

⑵得到计算式

5

⑶输入每题答案

⑷运行结果

输入y重新开始,输入其他字母结束。

2.1.6结果分析

这是一个小学数学运算测试程序,要求完成 10 以内的加减运算。 能根据用户 输入的想要练习的题目数自行出题。用户输入答案后可以判断正误,并能给出算对及算错的题目数,最后计算出分数。同时,对于用户算错的题目会发出报警声,提示用户注意,同时给出正确结果。做完一次测试后用户可决定是否继续进行下一次 测试。该程序基本实现了以上功能,且计算结果合理正确,满足实验要求。

任务二:linux下C语言设计简单聊天程序

2.2.1实验题目

熟悉linux操作系统下最简单实用的通信程序socket.最好能全部完成,否则按照完成情况打分。

2.2.2实验目的

通过对socket的编写,可以了解linux下最简单实用的进程通信方法,为后续信号灯、消息队列等学习奠定基础。

2.2.3实验设备及环境

6

⑴硬件设备:PC机一台

⑵软件环境:安装Linux操作系统,并安装相关的程序开发环境,如C C++tshbsh等编程语言环境。

2.2.4实验内容及要求

⑴内容:本课题是建立聊天通信模型:设计一个聊天室软件。包括服务器端和客户端。

主要功能为:Ⅰ、服务器端功能:①初始化 socket, 创建服务器端。②维护一个链表,保存所有用户的 IP 地址、端口信息。③接受用户传送来的聊天信息,然后向链表中的所用用户转发。④接受用户传送来的连接判断命令,并向用户发出响应命令。Ⅱ、客户端功能: 客户端界面上的两个文本框,一个用于显示接受的聊天信息,一个用来接受用户输入的聊天信息。当按下“发送”按钮时将信息发送给服务器。

⑵要求:①用C语言编程实现linux简单的聊天室功能。②用户程序命名为client.c;服务器程序命名为server.c。③绑定端口等信息见实验方法内容,要求client可以通过socket连接server。④在client,提示输入服务器ip。⑤若连接server 的socket建立成功,返回提示信息。⑥Client输入的聊天内容在client端(多个client端)和server端同时显示。⑦多个client可同时接入server,进入聊天室,最多支持20个client。⑧Client端输入quit退出连接,server端提示client退出。⑨可选择使用多线程实现多客户端。

2.2.5实验方法内容

⑴主要的常量变量

①客户端:

#define TRUE 1

#define PORT 5000

int quit=0; //quit表示是否用户确定退出

②服务器端:

#define MAXLINE 1000 //在一条消息中最大的输出字符数

#define LISTENQ 20 //最大监听队列

#define PORT 5000 //监听端口

#define MAXFD 20 //最大的在线用户数量

void *get_client(void *);

int i,maxi=-1;//maxi表示当前client数组中最大的用户的i值

int client[MAXFD];

⑵主要模块

①客户端:

int main(void)

void *get_server(void* sockfd)

//get_server函数,用于接受服务器转发的消息

②服务器端:

int main()

void *get_client(void *sockfd) //运行get_client函数,处理用户请求

2.2.6代码

⑴客户端代码

#ifdef HAVE_CONFIG_H

#include

#endif

#include "client.h"

7

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

{

int cstcp,csudp,ccudp;

int server_port=8000; //c-s tcp port

int CC_port=5000; //c-c udp port

struct hostent *hostPtr = NULL;

struct sockaddr_in CS_link = { 0 };

struct sockaddr_in CC_link = { 0 };

char hostname[80] = "";

char buf[bufsize+1]="";

char *msg;

char *S_addr = NULL;

int recvn;

int childPid;

if (2 != argc)

{

fprintf(stderr, "Usage: %s n", argv[0]);

exit(1);

}

S_addr = argv[1];

cstcp = tcpSocket();

GetHostName(hostname, sizeof(hostname)); //get local host

CreateSockAddr(S_addr,&CS_link,server_port); //C-S

Connect(cstcp,(struct sockaddr*) &CS_link);

msg="Client OPC 0.0.1n";

Send(cstcp,msg);

Recv(cstcp,buf);

printf("%s",buf);

printf("Connect to server successfully,please input your nicknamen");

Fgets(buf);

Send(cstcp,buf); //send user name

bzero(buf,bufsize);

Recv(cstcp,buf);

while(strncmp(buf,":x",2)==0)

{

printf("%s","The name has been used,please change your name:n");

Fgets(buf);

Send(cstcp,buf); //send user name

bzero(buf,bufsize);

Recv(cstcp,buf);

}

printf("%s",buf);

Send(cstcp,":l"); //send com to get userlist form server

printf("OnLine Users:n");

8

bzero(buf,bufsize);

Recv(cstcp,buf); //get userlist from server

printf("%s",buf);

childPid = fork();

switch (childPid)

{

case -1: // ERROR

perror("fork()");

exit(1);

case 0: // child process

while(1)

{

bzero(buf,bufsize);

if(Recv(cstcp,buf)>0)

if(strncmp(buf,":q",2)==0)

exit(0); //get userlist from server

printf("%s",buf);

}

⑵服务器端代码

#include

int tcpSocket()

{ int n;

if ( (n = socket(PF_INET,SOCK_STREAM,0))==-1)

{

perror("TCP Socket error");

exit(1);

}

return(n);

}

void Setsockopt(int s)

{ int on = 1;

struct linger linger = { 0 };

linger.l_onoff = 1;

linger.l_linger = 30;

if ( setsockopt(s, SOL_SOCKET, SO_REUSEADDR, (const char *) &on, sizeof(on))==-1)

{ perror("Setsockopt(...,SO_REUSEADDR,...)");

exit(1); }

if ( setsockopt(s, SOL_SOCKET, SO_LINGER, (const char *) &linger,

sizeof(linger))==-1)

{

perror("Setsockopt(...,SO_LINGER,...)");

exit(1);

}

}

9

int Bind(int sockfd, const struct sockaddr *my_addr, socklen_t addrlen)

{

return bind(sockfd,my_addr,addrlen);

}

void Listen(int s)

{

void GetHostName(char *buffer, int length)

{

struct utsname sysname = { 0 };

int status = 0;

status = uname(&sysname);

if (-1 != status)

{

strncpy(buffer, me, length);

}

else

{

perror("GetHostName()");

exit(1);

}

}

void CreateSockAddr(const char *hostname,struct sockaddr_in *sockaddress,int port)

{

struct hostent *host = NULL;

host = gethostbyname(hostname);

if (NULL == host)

{

host = gethostbyaddr(hostname,

strlen(hostname), AF_INET);

}

}

(void) memset(sockaddress, 0, sizeof(sockaddress));

(void) memcpy(&((*sockaddress).sin_addr), host->h_addr, host->h_length);

sockaddress->sin_addr.s_addr=htonl(INADDR_ANY);

sockaddress->sin_family = AF_INET;

sockaddress->sin_port = htons(port);

}

ssize_t Send(int s, const void *buf)

{

ssize_t sendn;

if( -1==(sendn=send(s, buf, strlen(buf), 0)))

{

perror("Send()");

close(s);

10

}

return sendn;

}

ssize_t Recv(int s, void *buf)

char *Fgets(char *s)

{

bzero(s,bufsize);

}

void Rtrim(char *buf)

{

int i;

for(i=0;i<255;i++)

{

if(buf[i]=='n')

{

buf[i]=0;

break;

}

}

2.2.7实验过程

⑴服务器打开

 ⑵客户端打开,并输入了地址,昵称

 ⑶服务器端显示

11

 ⑷客户端2进入

 ⑸服务器显示

 ⑹张三输入

12

 ⑺李四端显示

 ⑻服务器显示

 ⑼李四输入

13

 ⑽张三显示

⑾服务器显示

2.2.8结果分析

⑴这是一个聊天室程序,可以实现群聊的功能,即当某个客户发出消息后,服务器和其他个客户端都能收到此消息。且能够显示客户端的用户名。但客户端退出聊天室后,服务器和其他在线客户端会有提示。

⑵实现群聊的机制是:当某个客户端需要发送消息是,它将此消息发送给服务器,服务器再将此消息转发给各客户端,各客户端之间是无连接的,即相互之间不能直接通信。因此,在服务器中,有两个线程,主线程用来监听是否有客户端登录服务器,若有,建立与其连接的套接字,并存入在线客户序列里,辅助线程是接收转发线程,其依次读取个客户端,看是否有消息送达,若有取出,并转发给各其他客户端。在客户端也有两个线程,主线程用来向服务器发送消息,辅助线程用来接收服务器发出的消息。

⑶存在的问题是:①当有用户下线是,虽会在服务器和各客户端提示用户下线,但是并未删除其在服务器中的套接字,致使后来用户不能进入。②服务器的辅助线程对各客户端采取轮流监听的策略,但是因为使用read()函数会阻塞线程,致使出现各客户端必须按登陆顺序依次发言的尴尬情况。经过查找,可以使用select()函数跨过阻塞,正在试验中。

14

三、 结论(应当准确、完整、明确精练;也可以在结论或讨论中提出建议、设想、尚待解决问题等。)

3.1设计过程中的感受与体会

为了增强我们的C程序的实际操作与设计能力我编写了这个答题系统,下面就来谈下我在这次程序设计过程中的感受与体会。从程序的整体构思到程序的算法设计及程序的编写再到最后程序的调试,我经历了重重困难和考验,但是我并没有放弃,我运用本学期所学到的知识和课外的一些知识,从想到查再到问,最后完成了该系统。在设计过程中,我不仅仅是完成了一个系统,更重要的是我在这次设计中巩固了本学期所学到的知识,锻炼了自己的实践能力;另外在这次设计的过程中,我懂得了在设计遇到困难的时候怎样去解决这个困难。从书上网上查阅资料,向比我们知识丰富的人虚心请教,这是我们解决问题的主要方法,这样也同时扩宽了我们的知识面,打破了课堂的局限,在以后的人生道路中,我们也应该保持住这种精神。只要这样我们相信今后我们的人生道路会像这次程序设计一样取得成功。以上就是我在这次程序设计过程中的感受与体会。

3.2遇到的问题与解决方法

在设计本系统的过程中,并不是一帆风顺的,设计与调试期间我也遇到了很多困难,现在就将我遇到的困难和困难的解决方法列出。

⑴在输入题号的时候,因为scanf函数后面就遇到答案输入scanf函数,所以在输入了题号后按回车,回车被输入答案的 scanf 函数所接收,本应接收答案却接收了回车,那自然就不对了。经过查找资料,我们想到了解决方方法,在两个scanf函数的中间插入一个接收字符的函数getchar(),以接收回车字符,这样问题就迎刃而解了。

⑵遇到相同题号要重答的功能是我们最后才添加进去的,这一功能是我设计这一程序中遇到的最大困难,我解决的时间也最长。最后,我想到的解决方法是定义一个数组来储存已输过的题号,这样在每输入题号存入数组的同时与数组里的先前元素比较,如相同就进行到函数的递归调用,就完成了重答的功能;如不相同,就按正常的顺序进行。这样,这一功能的缺失被我们完美的弥补,相信这些问题和解决方法是我们学习的重要的资料。

四、 参考文献

[1] 谭浩强.C程序设计(第三版).清华大学出版社.2005

[2] 杨树青,王欢. Linux环境下C编程指南.清华大学出版社.2007

[3] 余祥宣,崔国华,邹海明.计算机算法基础(第三版).华中科技大学出版社.2006

[4] 陈博,孙宏彬,於岳. Linux实用教程.人民邮电出版社.2008

[5] 赵克林,游祖元. C语言实例教程.西南师范大学出版社.2006

五、

签名:

年 月 日

课程设计成绩(五级分制)

15

指导教师评语


本文标签: 模块 用户 输入 函数 程序