附件1:封面
《Linux系统应用与编程》课程设计
说明书
设计题目:模拟手机交费系统
专业:计算机科学与技术
指导教师:
班级:
学号:
姓名:
同组人:
计算机科学与工程学院
2013年06月27日
前言
伴随着社会的发展,网络信息交流已经渐渐取代面对面的交谈。其中手机是一种不可缺少的通信工具,他已经渗入到人们的社会生活的各个方面。由于经常要使用手机,所以还要涉及到手机服务收费,手机交费系统的开发就很有必要了。
当代是一个追求效率的时代,随着们生活水平的提高,消费项目,服务种类的增加,社会需要方便和快捷的缴费方式。在当今网络社会,技术成熟,发挥网络技术优势,利用网络实现快捷缴费已经成为可能。利用电子商务网络以及众多的营业网点实时收费,在几乎不增加系统负担的情况既可缓解目前收费单位营业厅的紧张状况,又方便用户缴费,提高了费用回收率。
手机缴费系统是基于UNIX Socket和mysql数据库设计一个交易型中间件系统。在Linux环境下使用GNU C++,在Linux make开发工具的管理和控制下,利用Linux系统提供的Socket库和mysql数据库在网络底层开发交易型中间件,同时简要介绍了客户端和服务器的工作模式。此模拟手机交费系统采用“客户端—中间件—服务器”模式,其中主要包括三大部分: 客户端,中间件和服务器端。客户端主要实现用户管理、查询、交易(交费,购物等),撤销和统计等功能;中间件要完成与客户要求相符的功能——是本地的本地处理,否则发往服务器端——接受客户端数据,组织服务器端所需数据,重组服务端返回数据,并返回给客户方;服务器端主要实现客户端或中间件提出的业务请求并做好留迹工作。
本设计主要是利用UNIX系统提供的Socket库在网络底层,C++语言,mysql 数据库,以及软件工程的思想方法和TCP/IP设计出的一个模拟手机交费中间件系统。
本设计包括了模拟手机交费系统的开发环境,设计目的,总体设计,详细设计,具体实现代码,以及设计中遇到的问题及解决方法。将服务器端,中间件和客户端进行连接后可模拟出现实中手机交费的的功能。它可以实现手机费用查询,话费充值,打印花费清单详目等基本功能。
目录
前言 (2)
1、系统环境(硬件环境、软件环境) (4)
2、设计目的和要求 (4)
2.1、设计目的 (4)
2.2、设计要求 (5)
3、总体设计 (6)
3.1系统模型 (6)
3.2系统流程图 (7)
4、详细设计 (8)
4.1设计方法 (8)
4.2功能模块说明 (8)
4.3开发接口说明 (8)
5、调试与测试 (11)
5.1调试 (11)
5.2运行结果 (12)
6、遇到的问题及解决方法 (14)
主要问题: (14)
7、源程序清单 (15)
7.1客户端 (15)
7.2中间件 (24)
7.3服务器 (27)
8、心得体会 (34)
9、参考文献 (37)
1、系统环境(硬件环境、软件环境)
硬件环境:intel p4 3.06GHz 80G硬盘
软件环境:基于windows xp虚拟机下的Red Linux 或者Fedora操作系统
2、设计目的和要求
2.1、设计目的
基于UNIX Socket和MySQL数据库,设计一个交易型中间件系统。
这里的中间件是指交易型中间件。交易型中间件是指用在不同行业、不同部门间的通讯转发和协议转换的软件,在不同的行业、不同的系统间提供通讯转发和协议转换的桥梁作用。例如电子商务、银行代理业务软件等都是这种类型的软件。本设计不是基于WEB的,不需要很多与WEB开发相关的知识,主要是利用linux系统提供的Socket库在网络底层,开发交易型中间件。
模拟手机缴费系统,通过对移动通信话费查询与缴费业务进行了系统全面的分析研究。针对现有系统中实时性不足而造成用户欠费太多使公司企业蒙受损失的问题。采用开发工具Linux进行系统设计。基于linux Socket和mysql数据库,设计一个交易型中间件系统。提供通讯转发和协议转换的桥梁作用。例如电子商务、银行代理业务软件等都是这种类型软件。这里的中间件是指交易型中间件。交易型中间件是指用在不同行业、不同部门间的通讯转发和协议转换的软件,在不同的行业、不同的系统间。这里主要是利用linux系统提供的Socket 库在网络底层,开发交易型中间件。
本次设计要求实现对移动话费信息输入、查询、编辑以及话费统计各明细项目的数据编辑;可自主设定条件从而达到对话费数据的多角度查询功能;方便导入、导出数据及输出报表。
2.2、设计要求
(1)客户端
客户端程序可命名为client,要求带有两个命令行参数一个是服务(自己定义,并保存在/etc/services中,比如mysvr1 5678),另一个是目的主机(自己定义,并保存在/etc/hosts内,也可使用DNS来解析),缺省时为本机。
客户端至少要实现如下功能:用户管理;查询;交易(交费,购物等);撤销;留迹;本地业务统计。
客户端的环境可以是Windows系统,也可以的Linux系统。
(2)中间件
中间件程序命名为middleware,要带有三个参数,一个是用于服务器的服务(名字,与客户端同),第二个作为请求的服务(自己定义,并保存在/etc/services中,要区别于客户端,比如mysvr2 5679),第三个为目的主机(定义在/etc/hosts内),缺省时为本机。
中间件要完成与客户要求相符的功能:是本地的本地处理,否则发往服务器方。具体地是:接收客户方数据;组织服务器方所需数据;重组服务方返回数据,并返回给客户方;留迹;本地业务统计。
中间件的环境可以是Windows系统,也可以的Linux系统。
(3)服务器
服务器程序可命名为server,要求带有一个服务参数,与中间件的mysvr2同。
服务器方程序要完成客户端或中间件提出的业务请求,并做好留迹工作。
服务端环境必须是Linux系统。
(4)留迹或log
留迹或log工作在客户端、中间件和服务器三方都要做,以供统计或核对使用。客户端或中间件方可以使用文本文件或数据库,但在服务器方必须使用MySQL数据库。
3、总体设计
3.1系统模型
请求1 请求2
返回 返回
请求服务
响应服务
请求数据 返回结果
这个程序是模拟手机交费系统,所以运行时服务器一直处于工作状态,等待来自中间件发过来的服务或数据。而中间件只有在客户端传来数据或者要求服务时才处于运行状态。C/S 应用程序中复制寻找数据的步伐只需要访问一个中间件系统,由中间件完成网络中获得的数据源或服务,进行事先传输客户请求,重组回复信息,最后将结果送回应用程序的任务。
Client 1
Client 2
Log
中 间 件 (Middleware )
Log
服务器端(Server ) Log
Mysql database Log
3.2系统流程图
Socker()建立服务方
套接字,描述符为s
bingd()绑定套接字
与本地地址和端口
Linsten()通知TCP,准备接受Socker()建立服务方套接字,描述符为s
accept()等待客户方连接Connect()建立与服务方链接
recv()/read()读取客户方数据Send()/write()向服务器方发送请求
成功后返回与s具相
同特性的新套接字
服务处理及数据加等待服务器方
send()/write向客户方提供服务结果recv()/read读取服务方数据
Close()关闭新套接字s,结束本次对话
Close()关闭最初套接字s,结束服务器Close()关闭最初套接字s,结束本次对话
4、详细设计
4.1设计方法
在Linux环境下,使用GNU C++,在UNIX/Linux make开发工具的的管理和控制下,利用UNIX/Linux Socket库在网络的底层进行开发设计。
4.2功能模块说明
本系统主要由服务器,中间件,和客户端构成。
(1)客户机:只负责发送和接收请求信息,此模块的目的就是将信息呈现给用户看,并提供相应的操作选择。此模块处理信息的过程较简单,原理如基本功能的现金支付所述,在此不再多做介绍。
(2)中间件:与客户机和服务器相连接,通过判断倒数第二个字符来判断选择哪个服务器,具体的是把客户端发来的信息转发给哪个服务器。通过服务器反馈回来信息的特殊字符来判断此信息是由哪个服务器发送而来。简单来说,中间件在整个分布式系统中起数据总线的作用,将各种异构系统通过中间件有机地结合成一个整体。
(3)服务器:手机服务器在这一部分,需要实现的功能是直接缴费。它的过程是接收从中间件过来的信息,在手机服务器的客户账户上相应加上对应的话费,即更新手机服务器数据库。更新成功直接之后,将缴费成功的信息传回中间件。
4.3开发接口说明
Mysql提供有多种开发接口:PHP,ODBC,PERL,C/C++,JAVA
C常用的开发接口有:
mysql_init,mysql_real_connect,mysql_query,mysql_store_result,mysql_fetch_row,mysql_free_result,mysql_close,mysql_init
(1)mysql_init
作用:是初始化MYSQL变量,为mysql_real_connect()做准备。
用法:MYSQL *mysql_init(MYSQL *mysql)
返回值:MYSQL句柄或描述符;内存不足是为NULL;
(2)mysql_real_connect
功能:链接mysql数据库;
用法:MYSQL *mysql_real_connect(MYSQL *mysql,\
const char *host, const char *user, \
const char *passwd, const char *db, \
unsigned int port, const char *unix_socket, \
unsigned int client_flag)
说明:如果port!=0,则将作为TCP/IP端口使用,默认为0;如果unix_socket!=NULL,则可指定socket或命名PIPE,默认为NULL;Client_flag 可以指定特定的值(略),默认为0.
(3)mysql_query
功能:查询实施
用法:
int mysql_query(MYSQL *mysql, const char *query) 说明:query为数据库操作命令字符串,本义是查询(select),可包括select,update,insert,delete等对数据库操作的命令。mysql_query不能用来处理二进制数据,处理二进制数据可使用mysql_real_query。
返回值:0表示正常,非0表示发生了错误。
(4)mysql_store_resul
功能:结果集处理。如果使用mysql_query运行的是一个SELECT语句,或其它可以返回结果的查询,可用函数mysql_store_result来访问返回结果并并将其保存在一个变量中,以便做进一步处理。
用法:
MYSQL_RES *mysql_store_result(MYSQL *mysql)
MYSQL_RES *mysql_use_result(MYSQL *mysql)
说明:mysql为mysql_real_connect函数的返回值。
(5)结果集的使用
使用了mysql_store_result函数保存结果后,可以使用以下函数对结果集进行处理。
①获得结果集中的行数:
my_ulonglong mysql_num_rows(MYSQL_RES *result)
②获得结果集行的域字段数:
unsigned int mysql_num_fields(MYSQL_RES *result)
unsigned int mysql_num_fields(MYSQL *mysql)
③读取结果集中的一行:
MYSQL_ROW mysql_fetch_row(MYSQL_RES *result)
④获取结果集中行的域段数:
unsigned int mysql_field_count(MYSQL *mysql)
⑤获得结果集中的域的属性:
MYSQL_FIELD *mysql_fetch_field(MYSQL_RES *result)
⑥获得域属性数组:
MYSQL_FIELD *mysql_fetch_fields(MYSQL_RES *result)
⑦查询被update、delete、insert等受影响的行:
my_ulonglong mysql_affected_rows(MYSQL *mysql)
(6)善后工作
当对数据库使用完毕后,应对所创建的变量等进行释放:
mysql_free_result(result);
mysql_close(&mysql);
(7)错误处理
方法有2:利用函数的返回值来判断函数执行是否正确;2.使用mysql提供的错误号和错误信息:
错误号:unsigned int mysql_errno(MYSQL *mysql)
错误信息:char *mysql_error(MYSQL *mysql)
(8)辅助函数
获取客户机版本信息:char *mysql_get_client_info(void)
获取主机信息:char *mysql_get_host_info(MYSQL *mysql)
获取协议版本信息:unsigned int mysql_get_proto_info(MYSQL *mysql) 获取服务器版本信息:char *mysql_get_server_info(MYSQL *mysql)
获取可用数据库列表:MYSQL_RES *mysql_list_dbs(MYSQL *mysql, const char *wild)
获取数据库的可有表列表:MYSQL_RES *mysql_list_tables(MYSQL *mysql, const char *wild)
5、调试与测试
5.1调试
(1)把数据库文件导入数据库
#mysql –u root #mysql –u root #mysql –u root (2)创建服务端口 Myclt 56787/tcp Mymid 56788/tcp (3) 对程序进行编译连接 #gcc –o cc mainmenu26.c #gcc –o mm mid.c #gcc –o ss server.c –L/usr/lib/mysql -lmysqlclient (4) 运行程序 #./mm 切换到终端2 #./ss 切换到终端4 #./cc 5.2运行结果 6、遇到的问题及解决方法 主要问题: 1、首先遇到的问题是总是提示”段错误”,开始时摸不着头脑,就和同学一块商量查资料,知道所谓的段错误就是指访问的内存超出了系统所给这个程序的内存空间,简而言之,产生段错误就是访问了错误的内存段,一般是你没有权限,或者根本就不存在对应的物理内存,尤其常见的是访问0地址。后来经过我们的讨论,查找错误位置,慢慢修改过来了。 2、由于对数据库的了解不够深,所以在服务器中调用数据库出现问题。在实现客户端通过中间件与服务器通信时,总是提示套接字错误。进过调试发现这个问题是在服务器接收到数据后,在查询数据库时没有将参数传给buf,所以服务器没有给客户端回复信息。 3、可能是不够细心,所以编程过程中,总是会出现指针类型与字符数组类型不匹配的问题,这是小问题,看着错误提示,稍微修改一下就好了。 4、在使用字符分割函数char *strtok(char *str, const char *delim);时,后面那个参数我们使用了空格,所以经常出现警告或者错误。后来我们把后面一个参数改为“|”,问题就解决了。 5、还有一个很严重的错误,就是服务器程序中的while循环用错了位置,定义的 sendmg()函数与recmg();函数没有放一起,所以不能实现服务器收到信息后立即回应,以至于运行时出现死循环。解决方法就是:我们把sendmg ()函数放在recmg();函数后面,问题就迎刃而解了。 7、源程序清单 7.1客户端 #include #include #include #include #include #include #include #include #include #include #define rec_length 200 struct hostent *hp; struct sockaddr_in sin; struct servent *sp; char buf[500]; int s; char cellnum[30]; void mainmenu(); void login(); void regcell(); void usrmg(); void query(); void recharge(); void suite(); void usrinfo(); void cellinfo(); void bussness(); void clientlog(); main(){ if((sp=getservbyname("myclt","tcp"))==NULL){ //get struct servent *sp from /etc/services exit(-2); } if((hp=gethostbyname("localhost"))==0){ //get struct hostent *hp from /etc/hosts or DNS exit(-3); } // to build struct sockaddr_in sin from sp & hp bzero(&sin,sizeof(sin)); bcopy(hp->h_addr,&sin.sin_addr,hp->h_length); sin.sin_family=hp->h_addrtype; sin.sin_port=sp->s_port; if((s=socket(AF_INET,SOCK_STREAM,0))==-1){ // create SOCKET s exit(-4); } if(connect(s,&sin,sizeof(sin))==-1){ //connect to server with struct sockaddr_in sin close(s); exit(-5); } mainmenu(); } void mainmenu(){ system("clear"); char choice[200]; printf(" -------------------------------------------\n"); printf(" | |\n"); printf(" | 1:login |\n"); printf(" | |\n"); printf(" | 2:register |\n"); printf(" | |\n"); printf(" | 3:logout |\n"); printf(" | |\n"); printf(" | |\n"); printf(" -------------------------------------------\n"); while(1) { int i=0; printf("Please choose a fuction:"); gets(choice); if(strlen(choice)!=1) { printf("Invalid input,please try again:\n"); } else { if(choice[0]=='1') { login(); } if(choice[0]=='2') { regcell(); } if(choice[0]=='3') { return; } } } } void login() { char password[30]; char result[100]; printf("Please input your cellnumber(the length is 11):\n"); gets(cellnum); while(strlen(cellnum)!=11) { printf("your number is too long or too short,please input another one:\n"); gets(cellnum); } printf("Please input your password(the length is 6):\n"); gets(password); while(strlen(password)!=6) { printf("your password is too long or too short,please input it again to verify:\n"); gets(password); } sprintf(buf,"1%s %s",cellnum,password); if(write(s,buf,rec_length)!=rec_length) { close(s);exit(-6); } if(read(s,result,rec_length)==0) { close(s); exit(-3); } if(result[0]=='Y'){ system("clear"); printf("Login Sucessfully!\n"); clientlog(cellnum,"LoginSucessfully"); usrmg(); } else { printf("This number doesn't exsist or your password is wrong!\n"); clientlog(cellnum,"LoginFiled"); login(); } } void regcell() { char telephone[20]; char password[30]; char id[20]; char result[100]; printf("You are now registering:\n"); printf("Input your cellphone number:\n"); gets(telephone); while(strlen(telephone)!=11) { printf("your number is too long or too short,please input again:\n"); gets(telephone); } printf("please input your password:\n"); gets(password); while(strlen(password)!=6) { printf("your password is too long or too short,please input it again to verify:\n"); gets(password); } printf("please enter your id number:\n"); gets(id); while(strlen(id)!=18) { printf("your id is too short or too long,please input again:\n"); gets(id); } sprintf(buf,"2%s %s %s",telephone,password,id); if(write(s,buf,rec_length)!=rec_length){ close(s);exit(-6); } if(read(s,result,rec_length)==0){ close(s); exit(-3); } if(result[0]=='Y') { system("clear"); printf("Congratulate register successfully!\n"); clientlog(cellnum,"RegisterSucessfully"); usrmg(); } else { system("clear"); printf("This number has already existed\n"); clientlog(cellnum,"RegisterFailed"); regcell(); } } void usrmg() { char choice[200]; printf(" -------------------------------------\n"); printf(" | |\n"); printf(" | 1:query |\n"); printf(" | 2:recharge by bank |\n"); printf(" | 3:do bussnss |\n"); printf(" | 4:reback |\n"); printf(" | 5:exit |\n"); printf(" | |\n"); printf(" -------------------------------------\n"); while(1) { printf("Please enter a choice:\n"); gets(choice); if(strlen(choice)!=1) { printf("Invalid choice,please try again:\n"); gets(choice); } else { if(choice[0]=='1') { system("clear"); query(); } if(choice[0]=='2') { system("clear"); bankpay(); } if(choice[0]=='3') { system("clear"); bussness(); } if(choice[0]=='4') { system("clear"); mainmenu(); } if(choice[0]=='5') { exit(0); } } } } void query(){ char choice[200]; printf(" -------------------------------------\n"); printf(" | |\n"); printf(" | 1:check usrinfo |\n"); printf(" | 2:check suite |\n"); printf(" | 3:check cellinfo |\n"); printf(" | 4:reback |\n"); printf(" | 5:exit |\n"); printf(" | |\n"); printf(" -------------------------------------\n"); while(1) { printf("Please enter a choice:\n"); memset(choice,0,sizeof(choice)); gets(choice); if(strlen(choice)!=1) { printf("Invalid choice!\n"); } else { if(choice[0]=='1') { usrinfo();