Linux MySql数据库开发
MySql与C联合开发
开发库的安装
MySql C API
编程实例
1)MySql服务器
mysql-server-3.23.54a-11
mysql-server-3.23.58-1.9
2)MySql客户端
mysql-3.23.54a-11
mysql-3.23.58-1.9
3)MySql开发接口库
mysql-devel-3.23.54a-11
mysql-devel-3.23.58-1.9
l mysqyld服务要运行起来。最好是设为开机自动启动。方法:
l chkconfig –level 2345 mysqld on
l ntsysv
l service mysqld start / stop /restart
l 图形方式
MySqyl客户端命令行工具为mysql。其常用法为:
mysql [OPTIONS] [database] <script.sql >output.tab
常用参数有:
-?, --help: 帮助
-D, --database=DB_name: 指定数据库
-h, --host=Host_name: 指定主机
-u, --user=DB_user_name: 指定用户
-p[…], --password[=…]: 指定密码
在命令提示符下输入:mysql或 mysql –u root进入mysql系统。提示符为“>”,可以使用的命令有:
show databases / tables;
use database;
create database db;
create table tbl;
drop database db/ table tbl;
select … from tbl … where …
insert … into tbl … values …
update … tbl … set … where …
delete … from tbl … where …
不同版本的MySql有不同的目录设置。
目录:/usr/include/mysql
使用:#include <mysql/mysql.h>
说明:必要时可使用-I参数
静态库:/usr/lib/mysql/libmysqlclient.a
动态库:/usr/lib/mysql/libmysqlclient.so
使用参数:-L/usr/lib/mysql -lmysqlclient
//头文件
#include <stdio.h>
#include <mysql/mysql.h>
main()
{
//变量声明
MYSQL mysql;
MYSQL_RES *result;
MYSQL_ROW row;
// 初始化数据结构
mysql_init(&mysql);
//连接数据库
mysql_real_connect(&mysql,"localhost","root",0,"mydb",0,NULL,0);
//执行查询语句mysql_query
mysql_query(&mysql,"SELECT * FROM person");
//保存结果
result=mysql_store_result(&mysql);
//处理结果集
while((row=mysql_fetch_row(result))){
fprintf(stdout,"%s|\t%s\n",row[0],row[2]);
}
mysql_free_result(result); //清理数据
mysql_close(&mysql); //关闭连接
}
编译链接时要使用-L和-l参数。例如:
cc -o mysql mysql.c -L/usr/lib/mysql -lmysqlclient
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
作用:是初始化MYSQL变量,为mysql_real_connect()做准备。
用法:MYSQL *mysql_init(MYSQL *mysql)
返回值:MYSQL句柄或描述符;内存不足是为NULL;
功能:链接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.
功能:查询实施
用法:
int mysql_query(MYSQL *mysql, const char *query)
说明:query为数据库操作命令字符串,本义是查询(select),可包括select,update,insert,delete等对数据库操作的命令。mysql_query不能用来处理二进制数据,处理二进制数据可使用mysql_real_query。
返回值:0表示正常,非0表示发生了错误。
功能:结果集处理。如果使用mysql_query运行的是一个SELECT语句,或其它可以返回结果的查询,可用函数mysql_store_result来访问返回结果并并将其保存在一个变量中,以便做进一步处理。
用法:
MYSQL_RES *mysql_store_result(MYSQL *mysql)
MYSQL_RES *mysql_use_result(MYSQL *mysql)
说明:mysql为mysql_real_connect函数的返回值。
使用了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)
当对数据库使用完毕后,应对所创建的变量等进行释放:
mysql_free_result(result);
mysql_close(&mysql);
方法有2:利用函数的返回值来判断函数执行是否正确;2.使用mysql提供的错误号和错误信息:
错误号:unsigned int mysql_errno(MYSQL *mysql)
错误信息:char *mysql_error(MYSQL *mysql)
获取客户机版本信息: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)
守候进程(daemon),也叫精灵进程,或服务器进程,是生存期长的一种进程。它们常常在系统引导装入时起动,在系统关闭时终止。因为它们没有控制终端,所以说它们是在后台运行的。
一般而言,服务器是一个进程,它等待客户机与其联系,提出某种类型的服务要求。
C/S模式在操作过程中采取的是主动请求方式。
服务对客户机服务可将结果或信息返回给客户机(双向),也可不返回(单向)。
就双向C/S来讲,又分为重复型和并发型两种。
在网络编程中,一般来说,TCP服务器是并发的,而UDP服务器是重复的。
重复型服务器通过以下步骤进行交互:
(1)打开一通信通道并告知本地主机,它愿意在某一公认地址端口上接收客户请求。
(2)等待一个客户请求的到来;
(3)处理客户请求;
(4)将服务结果数据发送给请求的客户;
(5)返回步骤(2)
重复型服务器主要的问题发生在第(3)步。在这期间,它不能接收其它客户请求,因而不能为其他客户机提供服务。
并发型服务器采用以下步骤进行交互:
(1)打开一通信通道并告知本地主机,它愿意在某一公认地址端口上接收客户请求。
(2)等待一个客户请求的到来;
(3)启动一个新的服务器来处理这个客户的请求。
启动一个新的服务器的方法可能不同,可能是生成一个新的进程或线程,这依赖于所使用的操作系统或开发系统的支持。新生成的服务器对客户的全部请求进行处理。处理完毕后,终止这个新服务器。
(4)返回步骤(2)
并发服务器的优点在于它是利用生成其他服务器的方法来处理客户的请求。它可以同时并发地为多个客户机服务。
(1)打开一通信通道,并连接到服务器所在主机的特定端口。
(2)向服务器发服务请求,等待并接收应答;
(3)请求结束后关闭通信通道。
在中间件诞生之前,多采用传统C/S二层结构。这种二层结构是计算机及软件技术发展的一大进步。
虽然它带来了相当的灵活性,但也逐渐暴露出其客户端和服务器端负担过重的现象,并且拓展性也较差,无法跨越不同的业务部门和业务系统。
这不仅表现在技术上有难度,更重要的是在制度和安全方面。于是,三层结构出现了。
所谓三层结构,就是在原有的两层结构之间,增加一个中间层。该中间层既包括服务也包括请求。
服务包括事务处理应用服务和数据库查询代理等,对于更多实时业务系统而言,该层的主要作用是通讯转发、协议转换和安全控制;
请求主要表现在当需要跨越不同业务系统和业务部门时,根据实际业务的需要,再以C/S方式向对方或目的方提出请求,当服务信息返回后,再将自己的客户请求的应答信息返回给自己的客户,这才是实现中间件的真正目的。
两层结构向三层结构转变后,中间件可分担部分客户端和服务端的工作量,因此客户端和服务器端的负载就相应减轻了。最关键的是中间件的引入实现了跨平台、跨业务部门的服务和传输,解决了信息传输不可靠和部门、企业间的安全控制等问题。
中间件,从本质上是对分布式应用的抽象,它抛开了与应用相关的业务的细节,保留了典型的分布交互模式的关键特征,将纷繁复杂的分布式系统经过提炼和必要的隔离后,以统一的层面形式呈现给应用。
中间件在整个分布式系统中起数据总线的作用,将各种异构系统通过中间件有机地结合成一个整体。
它的工作机制是:当客户端上的应用程序需要从网络中某个节点处获取一定的数据或服务时,这些数据和服务可能处于一个运行着和客户端不同的硬、软件、网络或操作系统的平台上。C/S应用程序中负责寻找数据的部分只需访问一个中间件系统,由中间件完成到网络中找到数据源或服务,进而实现传输客户请求、重组答复信息,最后将结果送回应用程序的任务。
(1)消息中间件:主要功能是在不同的网络协议、不同的操作系统和不同的应用程序之间提供可靠的消息传送。
(2)交易中间件:负责正确传递交易,对交易完整性进行管理,调度应用程序的运行,保证整个系统运行的高效性。
(3)对象中间件:它类似于软总线,解决面向对象、数据一致性、应用集成等关键任务的需求。
(4)安全平台:以公钥基础设施(PKI)为核心、建立在一系列相关国际安全标准之上的一个开放式应用开发平台。它向上为应用系统提供开发接口,向下提供统一的密码算法接口及各种IC卡、安全芯片等设备的驱动接口。
(5)数据库中间件:ODBC。
socket程序库是UNIX网络上最普及的API,可调用socket程序库提供的各个程序开发网络软件和网络系统。
TCP/IP协议被集成到UNIX内核中时,相当于在UNIX系统引入了一种新型的I/0操作。UNIX用户进程与网络协议的交互作用比用户进程与传统的I/O设备相互作用复杂得多。
在UNIX系统中,网络应用编程界面有两类:UNIX BSD的套接字(socket)和UNIX System V的TLI。由于Sun公司采用了支持TCP/IP的UNIX BSD操作系统,使TCP/IP的应用有更大的发展,其网络应用编程界面(套接字)在网络软件中被广泛应用。
流式套接字(SOCK-STREAM):提供了一个面向连接、可靠的数据传输服务,数据无差错、无重复地发送,且按发送顺序接收。内设流量控制,避免数据流超限;数据被看作是字节流,无长度限制。文件传送协议(FTP)即使用流式套接字。
数据报式套接字(SOCK-DGRAM) :提供了一个无连接服务。数据包以独立包形式被发送,不提供无错保证,数据可能丢失或重复,并且接收顺序混乱。
原始式套接字(SOCK-RAW):该接口允许对较低层协议,如IP、ICMP直接访问。常用于检验新的协议实现或访问现有服务中配置的新设备。
创建套接字—socket()
地址绑定—bind()
建立连接—connect()与accept()
监听连接—listen()
数据传输—send()/write()与recv()/read()
关闭套接字—close()
应用程序在使用套接字前,必须拥有一个套接字,系统调用socket()向应用程序提供创建套接字的手段。其调用格式如下:
#include <sys/socket.h>
int socket(int domain, int type, int protocol);
该调用要接收3个参数,根据这3个参数建立一个套接字,并将相应的资源分配给它,同时返回一个整型套接字号,只和一个特定的协议相联系。实际上指定了相关五元组中的“协议”这一元。
domain(协议族):AF_INET,AF_UNIX,AF_IPX,…
type(类型):SOCK_STREAM,SOCK_DGRAM
protocol(协议):0-让系统使用指定类型和协议族上的默认协议。
当一个套接字用socket()创建后,存在一个名字空间(地址族),但它没有被命名,bind()将套接字地址(包括本地主机地址和本地端口地址)与所创建的套接字号绑定起来,即将名字赋予套接字,以指定本地半相关。其调用格式如下:
#include <sys/socket.h>
int bind(int sockfd,const struct sockaddr *my_addr, int addrlen);
说明:
sockfd:为socket成功时的返回值—套接字描述符。
my_addr为本方地址数据结构。
addrlen=sizeof(my_addr).
此调用用于面向连接服务器,表明它愿意接收连接。1isten()需在accept()之前调用,其调用格式如下:
#include <sys/socket.h>
int listen(int s,int backlog);
listen()在执行调用过程中可为没有调用过bind()的套接字s完成所必需的连接,并建立长度为backlog的请求连接队列。
backlog定义最大长度的socket等待队列。默认值为5.
这两个系统调用用于完成一个完整相关的建立,其中connect()用于客户建立连接。accept()用于使服务器等待来自某客户进程的实际连接。
功能:客户方调用发出的请求
用法:
int connect(int sockfd,const structct sockaddr * server_addr, socklen_t addrlen);
在面向连接的协议中,该调用导致本地系统和外部系统之间连接实际建立。
功能:服务器调用,从等待从编号为s的套接字上接受客户连接请求。
用法:
int accept(int s, struct sockaddr *addr, socklen_taddrlen);
调用accept()后,服务器等待从编号为s的套接字上接受客户连接请求,连接请求是由客户方的connect()调用发出的。
当有连接请求到达时,accept()调用将请求连接队列上的第一个客户方套接字地址及长度放入addr和addrlen,并创建一个与s有相同特性的新套接字号。
新的套接字可用于处理服务器并发请求。
当一个连接建立以后,就可以传输数据了。常用的系统调用有send()和recv()。send()调用用于在参数s指定的已连接的数据报或流套接字上发送输出数据,格式如下:
int send(int s,void *buf,int len,int flags);
int recv(int s,void *buf,int len,int flags);
recv/send可由文件系统调用read/write所取代。
在处理二进制数据处理时,后者优于前者。
close()关闭套接字s,并释放分配给该套接字的资源;如果s涉及一个打开的TCP连接,则该连接被释放。
close()的调用格式如下:
int close(int s);
网络字节顺序:TCP/IP使用大端字节来传输协议信息。
大端(尾)数(big-endian)把高位字节放在低地址,而小尾(端)数(little-endian)则把高位字节存放在高地址空间。
需要在机器字节顺序与网络字节顺序转换的可使用下列函数:
unsignd int htonl(unsigned int hostlong)
unsignd short htons(unsigned int hostshort)
unsignd int nhtonl(unsigned int netlong)
unsignd int nhtons(unsigned int netshort)
前两个是正向的,后两者是反向的。这时里的“长整数”指的是32位,而不是C语言的长整数。
应用程序常常需要在点分十进制的IP与二进制形式IP间的转换。以下函数可以实现这种转换:
#include <netinet/in.h>
#include <socket.h>
#include <arpa/inet.h>
int inet_aton(const char *ddaddress, struct in_addr*adress)
char *inet_ntoa(struct in_addr address);
unsigned long int inet_addr(const char *ddaddress);
struct hostent {
char *h_name; //Official name of host
char **h_aliases; // Alias list.
int h_addrtype; // Hostaddress type
int h_length; //Length of address
char **h_addr_list; // List of addrsfrom NS
};
参见netdb.h
主机信息及相关数据结构和函数
#include <netdb.h>
struct hostent *gethostbyname(const char *name)
name:为主机名。可以是域名,也可在/etc/hosts中定义。
struct hostent *gethostbyadress(const char *addr, intlen, int type)
addr::是一个指向in_addr的结构(见netinet/in.h);
len指明addr的长度;
type:对于SCOK_STREAM,type为AF_INET。
struct servent {
char *s_name; // Officialservice name.
char **s_aliases; // Alias list.
int s_port; //Port number.
char *s_proto; // Protocol touse.
};
参见netdb.h
#include <netdb.h>
struct servent *getservbyname(const char *name, const char*protocal);
name与protocal均定义在/etc/services中:
name:服务名;
protocal:为协议
struct servent *getservbyport(int port, const char *protocal);
直接使用数字作为端口号,建议不要这样做,因为不便配置。
IP socket地址
#include <sys/socket.h>
#include <netinet/in.h>
struct sockaddr_in {
short int sin_family; // AF_INET
unsigned short int sin_port; // port No.
struct in_addr sin_addr; //IP addr.
}
现在几乎所有支持网络开发的系统,都支持socket开发。比如VB的winsock控件,Windows socket库,MS MFC的CAsynSocket和Csocket类,.net的System.Net.Sockets,Java,Delphi,perl等都有相应的类。
不同开发环境可实现指定功能,完成通信和联网。
这里给出UNIX Socket的开发框架示例程序,他们分别是客户端、服务端和中间件。
客房端程序:源程序见文件c.c。
服务器端程序:源程序见文件s.c。
根据中间件的定义和描述。中间件编程并不困难。它既是服务器,又是客户机。作为服务器,它要为它的客户端提供服务;作为客户机,当客户机提出的请示超出它的服务能力时,它又要向它自己的服务器替自己的客户机提出请求。当然根据客户提出服务请的不同,它的服务器可能不止一个。至于它要向多少个服务器提出请求,要看客户端的请求而定。比如,银行的中间件,当要代理移动公司业务时,要将请求发往移动或联通公司,而当要代理固定电话业务时,要将请求发往网通或电信或铁通公司等。
在设计程序时它需要一个服务用于接收客户端的请求,而针对自己的服务方,则有多少个服务方就要定义多少个服务。
我以只有一个服务方为模型,设计出中间件程序。源程序见文件m.c。
我们模拟银行代理移动公话费业务来说明中间代理业务的具体实现。
1、移动公司方(最终服务器方)
功能:记帐、查询、交费、统计。
1)手机话务数据库
数据库名称:cellphone
总控信息表:main
基本信息表:user
话务信息表:payment
交易流水表:journal
2)表结构
(1)总控信息表main
use cell;
create table main
(
unit_id char(13) not null,
unitname char(40) not null,
unitaddr char(40) default ' ',
curr_jnl bigint default 1
);
create table user
(
user_no char(13) not null primary key,
username char(40) not null,
user_id char(18) not null,
userlevel char(2) default '01',
password char(30) ,
usertime datetime,
preserve char(20)
);
create table payment
(
user_no char(13) not null primary key,
curr_fee int,
month_fee int,
cut_off int,
transtime datetime ,
preserve char(20) default ' '
);
create table journal
(
journal char(20) not null primary key,
trans_type char(2),
trans_mount int,
trans_all varchar(250)
);
2、中间件方
简化中间件方的功能,只让它记经过酵的交易流水信息,流水信息采用文本方式。当然采用数据库更要,在进行统计等服务操作时更容易实现。
功能:记流水、统计
2、客户方
客户是交易的发起者。实现功能与服务器方同。
数据库名称:bank
总控信息表:main
交易信息表:trans
交易流水表:journal