4.2 接口设计
为了实现系统用户信息的保存机制,根据数据库结构的具体设计,设计数据库接口;由于系统的功能实现要通过网络,依赖于C/M/S工作机制,需要设计套接层接口。
(1) 数据库接口
1) mysql_init(&mysql);//初始化数据结构
2) mysql_real_connect(&mysql,"localhost","root",
NULL,"ser_log",0,NULL,0); //连接数据库
3) mysql_query(&mysql,str);//执行查询语句mysql_query
4) result=mysql_store_result(&mysql); //保存结果
5) while((row=mysql_fetch_row(result))){ //处理结果集
fprintf(stdout,"%s|\t%s\n",row[0],row[2]);
}
善后工作:
6) mysql_free_result(result);//清理数据
7) mysql_close(&mysql);//关闭连接
(2) socket接口
1) 创建套接字:socket()
socket()向应用程序提供创建套接字的手段,其用法如下:
#include <sys/socket.h>
int socket(int domain, int type, intprotocol);
该系统调用使用3个参数,domain为协议族,IPv4时取AF_INET,IPv6时为AF_INET6;type为类型,根据需要可取SOCK_STREAM,SOCK_DGRAM或SOCK_RAW;protocol为协议,一般取0,让系统使用指定类型和协议族上的默认协议。
socket()根据这3个参数建立一个套接字,并将相应的资源分配给它,同时返回一个整型套接字号,它与文件部分系统调用open()的返回值意义相同。因此,把它叫做套按字描述符,以后可以通过该描述符进行网络I/O操作。socket()之后,该套接字符只与一个特定的协议相联系,实际上只指定了相关五元组中的“协议”这一元。
socket()成功时返回套接字描述符,失败时返回-1.
2) 绑定本地地址:bind()
当一个套接字创建之后,还没有被具体化。bind()将本地址(包括本地主机地址和本地端口地址)与新创建的套接字绑定起来,以形成本地半相关。本函数适用于数据报或流类套接口,在connect()或listen()之前调用,其用法为:
int bind(int sockfd,const structsockaddr *my_addr, int addrlen);
参数sockfd为socket()成功时返回的套接字描述符;my_addr为本方地址数据结构指针;addrlen为my_addr所指sockaddr结构的真实长度,一般为sizeof(sockaddr)。
bind()成功时返回0,失败时返回-1,且设置errno。
3) 启用监听:listen()
当一个套接字被创建之后,它被假设为主动的,可以主动发起连接,但作为服务用的套按字必须是被动的。listen()可以将套接字变为被动的,表明它愿意接收连接。1isten()需在bind()之后accept()之前调用,其调用格式如下:
int listen(int sockfd, int backlog);
listen()将套接字sockfd变为被动的,并建立长度为backlog的请求连接队列。backlog定义最大长度的socket等待连接的队列,默认值为5,当等待连接的请求队列长于此值时将被拒绝。
listen()成功时返回0,失败时返回-1,且设置errno。
4) 等待接受连接:accept()
accept()用于使服务器与等待连接的客户建立实际连接,其用法为:
int accept(int sockfd, struct sockaddr*addr,int socklen_t addrlen);
sockfd为socket()返回的描述符;addr是个指向sockaddr型数据结构的指针,是连接建立后内核填入的要求接入方的信息,通过此可以测定哪个地址在哪个端口上呼叫自己。addrlen为addr所指sockaddr型结构的真实大小,通过它可以控制accept不将多余的字节传给addr,通常为sizeof(sockaddr)。
申请建立连接:connect()
在服务器方依次调用listen()和accept()后,就算为提供服务准备好了一切,客户方可在自己调用socket()创建套接字之后,可以调用connect()申请与服务器建立连接了,其用法如下:
int connect(int sockfd,const structsockaddr *server_addr,
int socklen_t addrlen);
sockfd为客户端socket()的返回值;server_addr为所要连接的服务器sockaddr结构,包括了对方IP地址和端口等信息,为连接的目的地;addrlen为server_addr所指sockaddr型结构的真实长度,通常为sizeof(sockaddr)。
5) 数据传输:send()/write与recv()/read()
当建立真正连接后,服务器就可通过accept()产生的新套接字传输数据了。常用的数据发送和接收的系统调用有send()和recv(),用法如下:
int send(int sockfd,void *buf,intlen,int flags);
int recv(int sockfd,void *buf,intlen,int flags);
sockfd为通过accept()和connect()建立的套接字,buf为发送/接收的数据存放位置,len为发送/接收数据的长度。
6) 关闭套接字:close()
当一个套接字使用完毕后也要立即将其关闭以释放系统和网络资源。close()用于关闭套接字并释放分配给该套接字的系统和网络资源,关闭网络连接,其用法为:
int close(int sockfd);
该close()就是文件部分的系统调用close(),它在成功时返回0,失败时返回-1。
4.3 与主机、服务器相关的数据结构及函数
(1) 与主机相关的数据结构及函数
与主机信息相关数据结构为hostent,其定义为
struct hostent {
char *h_name; //Officialname of host
char **h_aliases; //Aliaslist.
int h_addrtype; //Host address type
int h_length; //Length of address
char **h_addr_list; //List of addrs from NS
};
可通过函数gethostbyname()或gethostbyaddr()获得主机的hostent结构信息,它们的用法为:
#include <netdb.h>
struct hostent *gethostbyname(const char*name);
struct hostent *gethostbyaddr(const char*addr,int len,int type);
其中name为主机名,可以是能通过DNS进行解析的官方机名,也可是定义在/etc/hosts中的主机名。addr是一个指向in_addr的结构指针,len指明addr所指字in_addr型变量的长度,type为地址族,对于SCOK_STREAM,type的值为AF_INET。
(2) 与服务器相关的数据结构及函数
与服务器相关的数据结构为servent,其定义如下:
struct servent {
char *s_name; //Officialservice name
char **s_aliases; //Alias list
int s_port; //Port number
char *s_proto; //Protocolto use
};
可以通过函数getservbyname()和getservbyport()获得与某个服务相关的servent结构信息,它们的用法为:
#include <netdb.h>
struct servent *getservbyname(const char*name, const char *protocol);
struct servent *getservbyport(int port,const char *protocol)
其中name为定义在/etc/services的服务名,protocol为所对应的协议,可以为tcp或udp。port为所使用的端口号。
4.4 模块功能说明
1) 网上缴费
用户使用网上缴费功能,首先进行系统登录(若已经注册,则直接登录;若没有注册,则先注册,注册成功后,再登录系统选择服务),输入手机号码和密码,登录成功后,选择缴费功能,输入缴费卡号、密码和充值金额,客户端根据用户提供的信息向中间件发送请求,中间件收到请求后,接受客户端数据,然后发送给服务器,服务器把充值卡号和密码及充值金额对照数据库查询,并返回确认信息,中间件在得到确认信息后,从接受表中检测返回给客户端,并监控错误无返回结果的情况。服务器从接收表中查询到充值信息指令,把充值的具体金额填入数据库,得到“充值费用” 后从主数据库进行处理,并保存银行卡表中相应的余额信息,更新用户基本信息表中的余额。 “服务器处理系统”如果因为错误没有得到结果,把返回结果“系统错误”填入发送错误信息给中间件,然后中间件把信息返回给用户。
2) 信息查询
用户使用查询功能,首先进行系统登录(若已经注册,则直接登录;若没有注册,则先注册,注册成功后,再登录系统选择服务),输入手机号码和密码,登录成功后,可以进行个人账户的相关信息查询,如套餐查询、用户信息查询、消费信息查询等。
1)程序运行主界面
客户端启动后,进入程序运行的主界面,用户根据自己的需求选择不同的功能执行,已注册的用户可以直接登录,进去服务界面,没有注册的用户需进行注册,注册成功后方可登录,选择服务,如图5-6所示。
5.2 windows平台环境
借助MicrosoftVisual Studio 2008运行环境,实现客户端的功能。图5-12所示为用户登录窗体。
编码过程中遇到的问题:
1. 在设计阶段,我们先做了代码设计,而不是数据库设计,导致在一段时间以后我们陷入了迷茫阶段。然后开始设计数据库,突然发现我们的好多的的问题,比如SQL命令已经忘得差不多了。通过看书复习勉强的设计出来现在的数据库。
2. 在编码阶段,在老师给的Linux框架内添加了自己的内容,在数据传输格式上,犯了错,造成了许多不必要的麻烦,还好,在大家的一起努力下,攻破这个小难题。
3. 在整体的功能实现时,我们有各自的见解,我们求同存异的保留系统中的功能,团队是推进发展的基础。
4. 关于这个数据存储上,我们认为应该是密文保存,密文传输,但是由于时间有限这项功能我们无法实现。
5. 我们在windows端客户端设计的时候出现乱码,很是头疼,现在还未解决。
6. 系统在设计之处出现很多明显的漏洞,在我们的不懈努力下,我们完成了初步的功能实现。
7. Linux与windows进程通信出现数据不严格相同我们要继续数据格式的转换。
1) windows端部分源代码
using System;
using System.Collections.Generic;
using System.Collections;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.Net;
using System.Net.Sockets;
using System.Threading;
namespace Linux课程设计{
public partial class Form2 : Form {
private IPAddress myIP = IPAddress.Parse("192.168.17.128");
private IPEndPoint Myserver;
private Socket connectSock;
public Form2(){
InitializeComponent();
}
private void Form2_Load(object sender, EventArgs e){ //创建连接
try {
myIP = IPAddress.Parse("192.168.17.128");
}
catch {
MessageBox.Show("您输入的IP格式不正确,请重新输入!");
}
}
private void button1_Click(object sender, EventArgs e){
try {
Myserver = new IPEndPoint(myIP, Int32.Parse("55555"));
connectSock = new Socket(AddressFamily.InterNetwork,
SocketType.Stream, ProtocolType.Tcp);
connectSock.Connect(Myserver);
Byte[] sendByte = new Byte[64];
string send = "1" + textBox1.Text+ " "+textBox2.Text;
NetworkStream netStream = new NetworkStream(connectSock);
sendByte = System.Text.Encoding.Default.GetBytes(send.ToCharArray());
netStream.Write(sendByte, 0, sendByte.Length);
netStream.Flush();
Byte[] Rec = new Byte[64];
netStream.Read(Rec, 0, Rec.Length);
string RecMessage = "您好 \n" + " 您还有" +
System.Text.Encoding.Default.GetString(Rec);
string str = System.Text.Encoding.Default.GetString(Rec);
if (!str.Equals("Y")) {
Form4 f = new Form4();
f.Show();
this.Hide();
connectSock.Close();
}
else{
textBox1.Text = "";
textBox2.Text = "";
connectSock.Close();
textBox1.Focus();
}
}
catch {
MessageBox.Show("连接尚未建立,无法发送!");
}
}
private void button2_Click(object sender, EventArgs e){
Form3 f = new Form3();
f.Show();
this.Hide();
}
}
}