今天整理了网络编程的下篇,主要归纳了wireshark抓数据包及分析、TCP安全可靠原因分析(三次握手、四次挥手)、数据库sqlite3及操作(shell脚本和C语言对数据库的增、删、改、查及关闭),各个知识点的代码编写及分析。
万石谷、粒粒积累 上菜!
提示:以下是本篇文章正文内容,下面案例可供参考
1 抓取网络数据包 wireshark(此软件可以用来分析网络数据报)
注意: 安装过程中会提示 安装一个组件 winpcap (需要安装)
重点: wireshark问题 过滤 tcp udp arp
1.1 分析以太网包头
第2章 链 路 层.PDF
1.2 分析IP报文
第3章IP:网际协议.PDF
UDP报文
第11章UDP:用户数据报协议.PDF
TCP报文
第17章 TCP:传输控制协议.PDF
以太网头 : 源MAC地址 目的MAC地址
IP头: 源IP地址 目的IP地址
UDP头: 源端口号 目的端口号
TCP头: 源端口号 目的端口号
分析以太网包头:wrieshark 抓数据包 过滤筛选:arp
IP头: 源IP地址 目的IP地址
UDP头: 源端口号 目的端口号
TCP头: 源端口号 目的端口号
为什么TCP安全可靠??
三次握手:连接的时候 connect --- accept
SYN -- 连接的时候
ACK -- 确认
四次挥手:断开的时候 close
FIN -- 断开的时候
ACK -- 确认
重传确认
(1)TCP在连接的时候,需要三次握手(请详细解释一下三次握手过程)
三次握手 连接的时候用的是SYN位,确认的时候用的是ACK位
(1) 客户端给服务器发送一个请求,假设发送的序号是200,SYN=1,代表请求连接
(2) 服务器端应答,并发送一个请求 假设发送的序号是500,
确认序号是201(对客户端的发送序号200确认) ACK=1(确认) SYN=1
(3) 客户端端应答 发送的需要在原来的基础上顺序增加1,201(上一次客户端的发送序号是200),
确认序号是501(对服务器发送的序号500确认) ACK=1(确认)
(2)TCP在断开连接的时候,需要四次挥手(请详细解释一四次挥手过程)
断开连接的时候 close()函数,用的FIN位 和 ACK位
(1)发起端: 发出断开请求 FIN=1,假设发送序号是400
(2)应答端: 发出应答 ACK=1, 假设发送序号是1000,确认序号是401(因为发起端的发出请求断开的发送序号400)
(3)应答端: 发出断开请求 FIN=1 发送序号是1001(因为应答端第一次发送的序号是1000,再一次发送在原来的基础上+1)
(4)发起端: 发出应答 ACK=1, 发送序号是401(因为发起端第一次发送序号是400),确认序号是1002(应答端发送的断开请求序号是1001)
(3)重传确认
ACK攻击:
客户端在连接服务器的时候,最后一次握手,需要给服务器一个ACK,确认我要去连接服务器
但是,客户端就不给这个ACK,让服务器处于等待,造成ACK攻击
2 客户端如何判断服务器断开
方法1: 通过recv的返回值 <= 0,如果 <=0 说明服务器出问题了
但recv 返回值 <= 0的判断可能会漏掉某些情况
方法2: 因为recv <= 0 并不能检测到所有断开情况,可以使用心跳包实现
原理: 客户端 定期(每隔 0.1秒)给服务器发送1包数据,服务收到后回应,
如果客户端检测到,发送了多包数据,服务器仍然没有回应,说明,服务器出问题了,
客户端重连,直到服务器恢复为止
流程:服务器功能: 等待客户端连接;一旦有客户端发送connect, 回应ok
客户端功能: 连接成功后,每隔 0.1秒钟发送connect, 接收服务器的ok.
如果长时间收不到ok, 客户端重新连接服务器
/client.c///
#include
#include
#include
#include
#include
int count = 3; //客户端接收不到服务器应答的次数,如果 == 0, 说明服务器不在线
int server_on = 0; //是否连接成功标志 0,断开 1 连接
int i = 0;
int main(int argc, char *argv[])
{
int fd;
struct sockaddr_in youaddr;
youaddr.sin_family = AF_INET;
youaddr.sin_port = htons(atoi(argv[2]));
youaddr.sin_addr.s_addr = inet_addr(argv[1]);
char buf[100] = "connect";
while(1)
{
sleep(1);
if(!server_on)
{
fd = socket(AF_INET, SOCK_STREAM, 0);
int ret = connect(fd, (struct sockaddr *)&youaddr, sizeof(youaddr));
if(ret == 0)
server_on = 1; //连接成功
else
{
printf("connect error %d\n", i);
close(fd); //连接失败,因为是无限循环,1秒钟后再重连
i++;
continue;
}
}
strcpy(buf, "connect");
send(fd, buf, sizeof(buf), 0); //发送connect
memset(buf, 0, sizeof(buf));
usleep(100000);
if(server_on)
{
if(recv(fd, buf, sizeof(buf), MSG_DONTWAIT) > 0)
{
printf("buf is %s\n", buf);
if(strcmp(buf, "ok") != 0) //没收到ok
{
count--;
if(count == 0)
{
printf("server is disconnect\n");
server_on = 0;
}
}
else
count = 3;
}
else
{
count = 0;
printf("server is no answer\n");
server_on = 0;
close(fd);
}
}
}
close(fd);
}
///server.c/
#include
#include
#include
#include
#include
int fd1;
int main()
{
int newfd, ret;
char buf[100] = { 0 };
int fd = socket(AF_INET, SOCK_STREAM, 0);
fcntl(fd, F_SETFL, fcntl( fd, F_GETFD, 0 )|O_NONBLOCK );
struct sockaddr_in myaddr;
myaddr.sin_family = AF_INET;
myaddr.sin_port = htons(55555);
myaddr.sin_addr.s_addr = htonl(INADDR_ANY); //192.168.20.252
bind(fd, (struct sockaddr *)&myaddr, sizeof(myaddr));
listen(fd, 5);
while(1)
{
newfd = accept(fd, NULL, NULL); //非阻塞等待客户端连接
if(newfd > 0)
{
printf("fd %d connect newfd %d\n", fd, newfd);
fd1 = newfd;
}
ret = recv(fd1, buf, sizeof(buf), MSG_DONTWAIT);//非阻塞等待客户端发送数据
if(ret > 0)
{
printf("recv %s\n", buf);
if(strcmp(buf, "connect") == 0) //收到了connect
{
strcpy(buf, "ok");
send(fd1, buf, sizeof(buf), MSG_DONTWAIT); //回应ok
}
}
usleep(100000); //0.1 s (usleep 是以微秒为单位)
}
close(fd);
close(newfd);
}
上面的服务器采用的是循环服务器,
循环服务器要求: accept 和 recv 都是非阻塞方式接收数据:
2.1实现非阻塞方式接收
文件I/O (不仅仅socket通信)
在打开文件后设置 O_NONBLOCK (非阻塞方式)
int fd = socket(AF_INET, SOCK_STREAM, 0); //socket描述符
1) int flags = fcntl(fd, F_GETFD, 0); //F_GETFD 取出socket默认属性, 给flag
fcntl(file ----> control), 文件控制
F_GETFD----> 读出文件处理方式标志位 (O_RDWR | O_TRUNC | .....)
取出文件标志位
2) fcntl(fd, F_SETFL, flags | O_NONBLOCK);
给文件标志位增加 O_NONBLOCK( 非阻塞 )
F_SETFL ---> 设置文件处理方式标志位
accept 函数变成非阻塞方式
2.2 非阻塞方式练习
参照上面两个函数,实现并验证accept 非阻塞模式
#include
#include
#include
#include
#include
int main()
{
int fd = socket(AF_INET, SOCK_STREAM, 0); //SOCK_STREAM 不要写成SOCK_DGRAM
struct sockaddr_in myaddr;
int flags = fcntl(fd, F_GETFD, 0); //(1) 取出标志位
fcntl(fd, F_SETFL, flags | O_NONBLOCK); // (2)重新设置标志位
myaddr.sin_family = AF_INET;
myaddr.sin_port = htons(33333);
myaddr.sin_addr.s_addr = htonl(INADDR_ANY); //192.168.30.252
bind(fd, (struct sockaddr *)&myaddr, sizeof(myaddr));
listen(fd, 5); //设置同时连接最大值5个
int newfd = accept(fd, NULL, NULL); //非阻塞等待客户端连接
printf("11111111111111 %d\n", newfd); //-1 (正常 4), 因为没有客户端连接,所以-1
}
讲解:recv函数的第四个参数
recv / send
ssize_t recv(int sockfd, void *buf, size_t len, int flags);
ssize_t send(int sockfd, const void *buf, size_t len, int flags);
flags ---> 通常是0 以阻塞方式接收
如果以非阻塞方式接收数,flags 设置为 MSG_DONTWAIT
recv(newfd, buf, sizeof(buf), MSG_DONTWAIT);
练习:文件服务器作业讲解 取文件列表、下载文件、上传文件
用到的知识点: socket 编程、文件I/O、并发服务器、目录操作
/server.c//
#include "my.h"
typedef struct
{
int type; //1 列表 2 下载 3 上传 4 退出
char filename[50]; //保存文件名
char filedata[100]; //保存文件内容
int len; //包数据长度,每次读取多少块,都存储在len变量中
}MSG;
char pathname[100] = { 0 };//用来保存文件名的绝对路径
//处理客户端的列表请求
void doFileList(MSG* ps, int newsockfd)
{
struct dirent* ep = NULL;
//1.打开我们的共享文件夹
DIR* dp = opendir("/home/linux/aaaa");
if(dp == NULL)
{
perror("opendir failed");
exit(-1);
}
//2.循环读取文件的名字,读取一个名字就发送一个给客户端
while(1)
{
ep = readdir(dp);
if(ep == NULL)//目录文件的所有内容读取完毕
{
break;
}
if(ep->d_name[0] == '.')//剔除隐藏文件
continue;
sprintf(ps->filename,"%s",ep->d_name);
send(newsockfd, ps, sizeof(MSG), 0);
}
//上面的while(1)循环结束了,说明全部文件内容读取完毕
//额外在多发送一个给客户端,告诉他已经全部发送完毕
ps->type = -1;
send(newsockfd, ps, sizeof(MSG), 0);
closedir(dp);
}
//处理客户端的下载请求
void doGetFile(MSG* ps, int newsockfd)
{
//1.打开要下载的文件
sprintf(pathname,"/home/linux/aaaa/%s",ps->filename);
//给客户端一个应答,告诉客户端要下载的文件是否存在
FILE* fp = fopen(pathname, "r");
if(fp == NULL)//说明下载的文件不存在,告诉服务器一声
{
ps->type = -1;//-1代表不存在
send(newsockfd, ps, sizeof(MSG), 0);
return;//提前结束函数
}
//如果存在也告诉客户端一声存在
ps->type = 0;//代表存在
send(newsockfd, ps, sizeof(MSG), 0);
//2.告诉客户端是否存在之后,再开始将文件的内容循环读取出来,发送给客户端
while((ps->len=fread(ps->filedata, 1, sizeof(ps->filedata), fp)) > 0)
{
send(newsockfd,ps,sizeof(MSG), 0);
}
//循环结束之后,在额外多发送一次,告诉客户端已经全部发送完毕
ps->type = -2;
send(newsockfd,ps,sizeof(MSG), 0);
fclose(fp);
}
//只要有一个客户端连接,就服务器创建一个新的线程,与客户端交互
void* do_client(void* p)
{
int ret;
int newsockfd = *((int*)p);
MSG s = { 0 };//用来保存接收到的数据
while(1)
{
ret = recv(newsockfd, &s, sizeof(s), 0);
if(ret > 0)
{
printf("type:%d %s\n",s.type,s.filename);
switch(s.type)
{
case 1://处理列表请求
doFileList(&s, newsockfd);
break;
case 2:
doGetFile(&s, newsockfd);
break;
case 3:
//doPutFile();
break;
case 4://客户端断开连接,退出请求
break;
}
}
else
{
printf("%d断开连接!!\n",newsockfd);
close(newsockfd);
pthread_exit(NULL);
}
}
}
int main(int argc, const char *argv[])
{
int ret;
pthread_t id;
int newsockfd;
char buf[100] = { 0 };//用来保存接收到的数据
struct sockaddr_in myaddr = { 0 };//用来保存自己电脑的IP地址和端口号
//1.创建一个流式套接字
int sockfd = socket(AF_INET, SOCK_STREAM, 0);
if(sockfd == -1)
{
perror("socket failed");
exit(-1);
}
printf("sockfd is %d\n",sockfd);
//2.绑定自己电脑的IP地址和端口号
//由于运行程序,偶尔会出现绑定失败,所以我们判断是否绑定成功,对bind函数的返回值做判断
myaddr.sin_family = AF_INET;
myaddr.sin_port = htons(5555);
myaddr.sin_addr.s_addr = htonl(INADDR_ANY);
//myaddr.sin_addr.s_addr = inet_addr("192.168.110.157");
ret = bind(sockfd, (struct sockaddr*)&myaddr, sizeof(myaddr));
if(ret == -1)
{
perror("bind failed");
exit(-1);
}
printf("bind OK!!\n");
//3.设置监听 ,允许同时连接的最大个数是5个
listen(sockfd, 5);
while(1)
{
newsockfd = accept(sockfd, NULL, NULL);
printf("连接成功!,accept解除阻塞,newsockfd is %d\n",newsockfd);
pthread_create(&id, NULL, do_client, &newsockfd);
}
//6.关闭套接字
close(sockfd);
return 0;
}
//client.c//
#include "my.h"
typedef struct
{
int type; //1 列表 2 下载 3 上传 4 退出
char filename[50]; //保存文件名
char filedata[100]; //保存文件内容
int len; //包数据长度
}MSG;
int sockfd;
MSG s;//来回发送数据的结构体变量
//客户端 列表请求函数
void doFileList()
{
int count = 0;
s.type = 1;//代表获取列表的请求
send(sockfd, &s, sizeof(s), 0);//请求发送给服务器
//服务器会将可以下载的所有的文件名字传递来
while(1)
{
recv(sockfd, &s, sizeof(s), 0);
if(s.type == -1)//说明已经全部接收完毕
break;
printf("%s ",s.filename);
count++;
if(count % 5 == 0)
{
printf("\n");
}
}
printf("\n");
}
//客户端 下载文件的请求
void doGetFile()
{
FILE* fp = NULL;
int ret;
s.type = 2;//代表下载文件的请求
printf("请您输入要下载的文件名称:\n");
scanf("%s",s.filename);
send(sockfd, &s, sizeof(s), 0);//请求发送给服务器
//接收服务器端的应答
recv(sockfd, &s, sizeof(s), 0);
if(s.type == -1)
{
printf("要下载的%s不存在!!\n",s.filename);
return;//提前结束函数
}
//循环接收,打开一个文件
fp = fopen(s.filename,"w");
while(1)
{
recv(sockfd, &s, sizeof(s), 0);
if(s.type == -2)
{
printf("下载%s完毕!!\n",s.filename);
break;
}
fwrite(s.filedata, 1, s.len, fp);
}
//关闭文件
fclose(fp);
}
int main(int argc, const char *argv[])
{
if(argc != 3)
{
printf("忘记传递参数了!! ./client 192.168.110.157 6666\n");
exit(-1);
}
int n;//代表输入的请求
char buf[100] = { 0 };//用来保存即将发送的数据
struct sockaddr_in serveraddr = { 0 };//用来保存提前知道的服务器的IP地址和端口号
//1.创建一个流式套接字
sockfd = socket(AF_INET, SOCK_STREAM, 0);
if(sockfd == -1)
{
perror("socket failed");
exit(-1);
}
//2.连接服务器,在连接服务器之前,已经提前知道了服务器的IP地址和端口号
serveraddr.sin_family = AF_INET;
serveraddr.sin_port = htons(atoi(argv[2]));
serveraddr.sin_addr.s_addr = inet_addr(argv[1]);
connect(sockfd, (struct sockaddr*)&serveraddr, sizeof(serveraddr));
while(1)
{
printf("********************************************\n");
printf(" 1.列表 2.下载 3.上传 4.退出\n");
printf("********************************************\n");
scanf("%d",&n);
switch(n)
{
case 1://给服务器发送一个获取列表的请求
doFileList();
break;
case 2://给服务器发送一个下载文件的请求
doGetFile();
break;
case 3:
// doPutFile();
break;
case 4:
printf("即将结束程序!!\n");
sleep(2);
s.type = 4;
send(sockfd, &s,sizeof(s), 0);
exit(-1);
break;
}
}
//4.关闭套接字
close(sockfd);
return 0;
}
1 数据库(学生管理系统、库存系统)
(1)大型数据库(银行、电信、qq、百度、京东、阿里)
oracle甲骨文公司 oracle数据库 IBM公司 DB2
(2)中型数据库
微软 sql server
(3)小型数据库
mysql (Web网站)
sqlite (用在嵌入式设备、手机)
安装使用特别方便
2.安装数据库
sqlite(平台ubuntu)
(1) 用下载好的安装包安装
libsqlite3-0_3.7.2-1ubuntu0.1_i386.deb (.deb的安装包可以用dpkg -i 来进行安装)
///将文件拖入虚拟机,依次安装.deb包
sudo dpkg -i libsqlite3-0_3.7.2-1ubuntu0.1_i386_1.deb
sudo dpkg -i libsqlite3-dev_3.7.2-1ubuntu0.1_i386_2.deb
sudo dpkg -i sqlite3_3.7.2-1ubuntu0.1_i386_3.deb
//安装三个包后,执行下面命令
sqlite3 my.db //用sqlite3打开数据文件名字叫my.db //如果文件存在,直接打开,文件不存在,创建并打开
//数据库文件是以 .db结尾
(2) 打开数据库(或者创建数据库)
sqlite3 my.db
数据库扩展名:数据库文件的扩展名一定以.db结尾
1) 输入 .开头的命令(非sql语句命令)
.quit //退出
.help //查看帮助文档
.table //将数据库中的所有数据表显示出来
ctrl + l //清屏
2) 还有一种命令不是以.开头的,这种叫sql语句,sql语句必须以;号结尾
3.数据库相关概念
(1)数据库:将所有的数据,各种类型,描述各种事物的数据放到一起,如:my.db文件,就是一个数据库文件
//数据库文件以.db结尾
(2)数据表:描述某一个事物的基本信息的表
//举例:
图书管理系统 学生信息表
编号 姓名 性别 年龄 班级 爱好
0001 阿丹 女 19 19121 睡觉
0002 阿三 男 20 19121 打豆豆
图书信息表
图书编号 书名 出版社 作者 分类
借阅表
记录
表里面的一条信息
字段 数据表中的列名字 列名
//数据库文件 my.db
//数据表:数据文件中的数据表 学生信息表
//字段: 数据表中的列
4.sqlite语句 //增 删 改 查
(1) 创建一个学生信息表
学生信息表:字段有:姓名,学号,年龄,成绩
表名
create table 表名 (字段 类型,字段 类型,......);
字段
姓名 年龄 成绩
sqlite> create table stu_info (name varchar(20),age integer,score float);
sqlite> .table
stu_info
sqlite>
//注意事项//
*********************************************************************
以.开头的是命令
sql 语句(是所有数据库通用的)
注意,上面语句可以在终端中执行,执行时 一定不要加. , 语句结束要加;
sqlite3 规定以.开头的都是sqlite3的命令,不是以.开头的都是sql语句
*********************************************************************
(2) 插入记录
所有字段全部填入值
insert into stu_info values('zhangsan',19,98.8);
(3) 查询记录
1)显示所有信息记录
select 和 from 之间 代表的是要显示的字段
sqlite> select * from stu_info; //*代表所有字段
.header on //显示的时候加上表头 字段的名字
.mode column //显示的时候按列对齐
//select 和 from 之间 代表的是要显示的字段
select name,age from stu_info; //只显示 name 和 age 字段
//上下键可以调用出历史sql语句和命令
2)查询一条指定记录
select * from stu_info where age = 20; //显示年龄是20岁的人信息
----------------------------------------------------------------------------------
#####练习#####
a、创建学生成绩表(stu_score 字段如下)
学生编号 数学 语文 英语
number math chinese english
b、向学生成绩中添加三条记录
00001 100 60 70
00002 80 90 90
00003 90 70 80
c、查询出编号000003的记录和数学成绩为100的记录
create table stu_score (number varchar(20), math integer, chinese integer,english integer);
insert into stu_score values('000001',100,60,70);
insert into stu_score values('000002',80,90,90);
insert into stu_score values('000003',90,70,80);
select * from stu_score where number = '000003';
select * from stu_score where math = 100;
(5) 其他各种查询
1) 查询年龄是20的记录
select * from stu_info where age = 20; //显示年龄是20岁的信息
2) 查询年龄 > 19的记录
select * from stu_info where age > 19; //显示年龄大于19岁的信息
3) 查询记录 只想看年龄大于19岁的姓名、年龄字段
select name,age from stu_info where age > 19; //显示年龄大于19岁的信息
4) 如果想按顺序输出所有记录,按成绩排序
select * from stu_info order by score; //按照成绩 从小到大的顺序显示信息
select * from stu_info order by score desc;//按照成绩 dest 从大到小的顺序显示信息
5) 模糊查询(假设有一个人名我记不住了,但能记住名字中的一个字, 就可以用模糊查询)
查询名字当中带li字符的人
//名字记不住的部分,用 % 替代
sqlite> select * from stu_info where name like 'da%'; //只能搜索以da开头的名字
sqlite> select * from stu_info where name like '%lang';//只能搜索以lang结尾的名字
sqlite> select * from stu_info where name like '%an%';//所有名字中包含an的
6) 多条件查询
想查询年龄大于 25 并且 名字中带'a' 的学生信息
sqlite> select * from stu_info where age > 25 and name like '%a%';
and ----> &&
or ----> ||
(6) 统计记录条数
1)统计表中有多少人
sqlite> select count(*) from stu_info;
2)统计表中年龄为19岁的学生有多少人
sqlite> select count(*) from stu_info where age = 19;
(7) 统计所有人的年龄总和
sqlite> select sum(age) from stu_info;
(8) 统计所有人的年龄平均值
sqlite> select avg(age) from stu_info;
(9) 删除记录
1)删除满足指定条件的目录
sqlite> delete from stu_info where name = 'asan'; //删除名字是asan的信息
2)删除所有记录(清空表)
sqlite> delete from stu_info; //由于没有条件限制,相当于清空表
(10)修改记录
更新 表明 修改的字段 指定某一条
sqlite> update stu_score set chinese = 100 where number = '0000002';
将学号为0000002的学生,语文成绩修改为100分
(11)多表联合(查询数学成绩 > 90的所有学生的基本信息和成绩)
//多表联合查询的前提条件是 两张表必须要有共有字段
#表1:学生基本信息表
name number age
#表2:成绩表
nubmer math chinese english
查询显示 数学成绩 学生的基本信息 满足条件为 数学成绩大于80
//select 和 from之间代表的是要显示的字段
select stu_score.math ,stu_info.* from stu_info, stu_score where stu_info.number = stu_score.number and stu_score.math > 80;
要显示到屏幕上的字段 两个表名 where条件 共有字段条件和查询条件
//多表联合查询的时候,条件要加上共有字段相等 关联在一起
(12) 删除表
drop table stu_info;//删除表
///
5、通过脚本执行sql语句
ls /home/linux > a.txt //输出重定向
ls /home/linux >> a.txt //追加重定向
shell脚本
mkdir aaa;
cd aaa;
touch a.c;
test.sh
bash test.sh //批处理
//shell脚本 是 .sh结尾的
//sql脚本,是 .sql结尾的
将所有的sql语句,写到一个文件中,然后执行这个文件,就把所有语句都执行了
写一个文件 book_info.sql //脚本文件名必须以sql结尾
drop table if exists book_info;
create table book_info (number varchar(20), book_name varchar(20));
insert into book_info values('10001','sanguoyanyi');
输入重定向
drop table if exists book_info;
create table book_info (name varchar(30), number varchar(20), date varchar(30));
insert into book_info values('三国演义', '000001','1999.12.1');
insert into book_info values('水浒传', '000002','1990.12.14');
insert into book_info values('西游记', '000003','189.11.12');
insert into book_info values('红楼梦', '000004','1999.12.13');
.header on
.mode column
select * from book_info;
*********************************************************************
执行文件
ls /home/linux > a.txt //将原本输出在屏幕上的内容,重定向到a.txt文档中
sqlite3 my.db < book.sql //输入重定向
*********************************************************************
/
#####练习####
图书管理系统
//你用脚本创建三张表
1 创建图书基本信息数据表 图书编号 图书名称 出版社 作者 出版日期
2 创建办理借阅卡的学生基础表 学生编号 学生姓名 所在系 所在班
3 创建学生借阅表 学生编号 图书编号 借阅日期 借阅时间 是否归还
4 添加记录并查询
//sqlite3 my.db
//练习sql语句使用
(1)查询指定出版社的图书信息
(2)查询指定作者的图书信息
(3)查询某个编号的学生基本信息
(4)查询已借出图书的图书名称
(5)修改某个编号的图书的基本信息
(6)查询已归还图书的名字及归还者名字
(7)删除借阅时间大于2天的借阅信息
//图书基本信息表
book_info.sql
drop table if exists book_info;
create table book_info (number varchar(20), name varchar(20), press varchar(20),
author varchar(20),date varchar(20));
insert into book_info values('1001','shuhuzhuan','renminyoudian','shinaian','1934.3.23');
insert into book_info values('1002','sanguoyanyi','qinghuadaxue','luoguanzhong','1931.1.23');
insert into book_info values('1003','hongloumeng','huaqingyuanjian','caoxueqin','1924.3.23');
insert into book_info values('1004','xiyouji','renminyoudian','wuchengen','1913.3.23');
.header on
.mode column
select * from book_info;
*****************************************************************************
用c语言操作数据库(打开数据库,执行sql语句)
//打开数据库sqlite3_open
//操作数据函数(增 删 改 创建) sqlite3_exec()
//操作数据函数(查找) sqlite3_get_table()
//关闭数据库 sqlite3_close()
1 打开数据库
(1)头文件及函数原型
#include
int sqlite3_open(char *path, sqlite3 **db);
功能:打开数据库文件,如果文件存在直接打开,不存在创建并打开
FILE* fp = NULL;
fp = fopen("./haha.c","r");//文件流指针
sqlite3 * //句柄
sqlite3 *db = NULL; //用来指向打开的数据库文件
sqlite3_open("./my.db",&db); //如果my.db文件存在,直接打开,如果不存在,创建并打开
(2)参数说明:
path 要打开的数据库,如果不存在,就创建 //可以相对路径也可以是绝对路径
db [出参], 数据打开成功,从这个参数传出一个指向数据库的指针
(3)返回值: 0 成功 非0 失败
(4)实例
sqlite3 * db = NULL; //用来指向打开的数据库文件
sqlite3_open("./my.db",&db); //如果my.db文件存在,直接打开,如果不存在,创建并打开
2.关闭数据库
sqlite3_close(db);
int sqlite3_close(sqlite3 *db);
功能:关闭sqlite数据库
返回值:成功返回0,失败返回错误码
(4)实例演示
sqlite3_close(db);
####################################################################################
gcc test.c -lsqlite3
编译-lsqlite3
gcc -o test test.c -lsqlite3
#include "my.h"
int main(int argc, const char *argv[])
{
sqlite3* db = NULL;//数据库操作句柄
int ret = sqlite3_open("./my.db",&db);//如果当前目录下有my.db数据库文件,直接打开,如果不存在,创建并打开
if(ret == 0)
{
printf("sqlite3_open sucessful!!\n");
sqlite3_close(db);//关闭数据库
}
else
perror("sqlite3_open failed");
return 0;
}
2 执行数据库操作的sql语句 //创建 增 删 改
int sqlite3_exec(sqlite3 *db, const char *sql, sqlite3_callback callback, void *data, char **errmsg);
char sql[100] = "create table stu_info (name varchar(20), age integer, score float);"
sqlite3_exex(db, sql, NULL, NULL, NULL);
参数 db , open的第二个参数,db指向打开的数据库
sql, 操作数据库的语句 "delete from stu_info;" //学生信息表清空
callback 如果执行select , select 通过这个值获取, 可以写NULL
void *data 可以写NULL
errmsg : 如果数据库操作出错,此处保存错误信息
返回值:成功返回0,失败返回错误编号
#include "my.h"
int main(int argc, const char *argv[])
{
char* errmsg = NULL;//用来保存数据库操作语句执行的失败的错误因
char sql[200] = { 0 };//用来保存数据库操作语句
sqlite3* db = NULL;//数据库操作句柄
int ret = sqlite3_open("./my.db",&db);//如果当前目录下有my.db数据库文件,直接打开,如果不存在,创建并打开
if(ret == 0)
{
printf("sqlite3_open sucessful!!\n");
//sqlite3_exec函数执行什么功能,取决于sql数组中保存的操作语句
//操作数据库的前提条件是必须是已经打开了数据库
//sprintf(sql,"create table stu_info (name varchar(20), age integer, score float);");
//sprintf(sql,"insert into stu_info values('haha',19,98.5);");
//sprintf(sql,"update stu_info set age = 15 where name = 'haha';");
sprintf(sql,"delete from stu_info where name = 'haha';");
ret = sqlite3_exec(db, sql, NULL, NULL, &errmsg);
if(ret == 0)
printf("sqlite3_exec sucessful!!\n");
else
printf("sqlite3_exec失败的错误原因是%s\n",errmsg);
sqlite3_close(db);//关闭数据库
}
else
perror("sqlite3_open failed");
return 0;
}
3. 查询语句 //select 的时候,就要用sqlite_get_table();
"select * from stu_info;"
int sqlite3_get_table(sqlite3 *db, const char *sql, char ***resultp, int *nrow, int *ncolumn, char **errmsg);
功能:执行SQL操作
db:数据库句柄, open的第二个参数
sql:SQL语句, select
resultp:用来指向sql执行结果的指针
nrow:满足条件的记录的数目, 不包括表头
ncolumn:每条记录包含的字段数目,相当于列数
errmsg:错误信息指针的地址, 如果语句执行出错,那么errmsg中是错误信息
返回值:成功返回0,失败返回错误码
#include "my.h"
int main(int argc, const char *argv[])
{
int i;
char* errmsg = NULL;//用来保存数据库操作语句执行的失败的错误因
int row,column;//用来保存查找信息的行数(行数不包含表头)和列数
char** resultp = NULL;//用来保存查找的信息的那个字符指针数组的首地址
char sql[200] = { 0 };//用来保存数据库操作语句
sqlite3* db = NULL;//数据库操作句柄
int ret = sqlite3_open("./my.db",&db);//如果当前目录下有my.db数据库文件,直接打开,如果不存在,创建并打开
if(ret == 0)
{
printf("sqlite3_open sucessful!!\n");
//操作数据库的前提条件是必须是已经打开了数据库
sprintf(sql,"select * from stu_info;");//查询所有信息
// sprintf(sql,"select * from stu_info where score > 90.0;");//查询满足条件的信息
// sprintf(sql,"select name from stu_info where score > 90.0;");//查询满足条件的信息
ret = sqlite3_get_table(db, sql, &resultp, &row, &column, &errmsg);
if(ret == 0)
{
printf("sqlite3_get_table sucessful!!\n");
printf("row is %d colum is %d\n",row,column);
for(i = 0; i < (row+1)*column; i++) //row+1是为了将表头也打印输出
{
printf("%s ",resultp[i]);
if((i+1)%column == 0)
printf("\n");
}
}
else
printf("sqlite3_get_table失败的错误原因是%s\n",errmsg);
sqlite3_close(db);//关闭数据库
else
perror("sqlite3_open failed");
return 0;
}
///返回值的形式,将name数组的首地址传递给main函数///
#include "my.h"
//返回值的形式将name数组的首地址,传递给main函数
char** fun()
{
//延长name数组的生命周期,直到整个程序的结束
static char* name[] = {"name","age","score","haha","18","97.4","xixi","17","95.4","wuwu","20","89.4"};
return name;//name就是数组的首地址
//name == &name[0]
//name[0] 的类型是 char* 所以 &name[0]的类型是char**
}
int main(int argc, const char *argv[])
{
//要求name字符指针数组,在fun函数中定义,要求你调用fun函数得到name数组的首地址
//(1)返回值的形式
//(2)参数上地址传递
//返回值是什么类型,我们就定义一个什么类型的变量来保存返回值
int i;
int row = 3, column = 3;
char** resultp = NULL;
resultp = fun();
//循环将name数组中的所有元素打印输出
for(i = 0; i < (row+1)*column; i++)//+1是为了把表头也打印出来
{
printf("%s ",resultp[i]);
if((i+1)%column == 0)
printf("\n");
}
return 0;
}
/参数上地址传递的方式,将name数组的首地址,传递给main函数///
#include "my.h"
//参数上的地址传递形式将name数组的首地址,传递给main函数
void fun(char*** p)//函数调用的时候,实参初始化形参 char*** p = &resultp;
{
//延长name数组的生命周期,直到整个程序的结束
static char* name[] = {"name","age","score","haha","18","97.4","xixi","17","95.4","wuwu","20","89.4"};
// *p代表的就是main函数中的变量resultp,因为p里面装的地址是main函数中的resultp的首地址,所以*p找到的就是main函数中的resultp
*p = name; //相当于main函数中的resultp = name;
//name == &name[0]
//name[0] 的类型是 char* 所以 &name[0]的类型是char**
}
int main(int argc, const char *argv[])
{
//要求name字符指针数组,在fun函数中定义,要求你调用fun函数得到name数组的首地址
//(1)返回值的形式
//(2)参数上地址传递
int i;
int row = 3, column = 3;
char** resultp = NULL;
fun(&resultp);//resultp 是char** &resultp就是char***
//循环将name数组中的所有元素打印输出
for(i = 0; i < (row+1)*column; i++)//+1是为了把表头也打印出来
{
printf("%s ",resultp[i]);
if((i+1)%column == 0)
printf("\n");
}
return 0;
}
###################################练习########################################
(1)从键盘输入表名,创建表(sqlite3_exec())"create table %s (.......);",tablename
(2)从键盘输入编号,姓名,年龄,性别,插入到stu_info中,输入3个人的信息(sqlite3_exec())
(3)打印输出表中所有信息(sqlite3_get_table())
(4)再输入一个年龄,删除 > 此年龄的学生(sqlite3_exec())
(5)打印输出表中所有信息(sqlite3_get_table())散会
#include "my.h"
struct student
{
char number[20];
char name[20];
int age;
char gender[20];
};
sqlite3* db = NULL;
//一段代码的功能出现了重复,你要做的将这个功能封装成一个函数
//函数参数,传递那个语句,就执行哪个操作
void mySqlite3Exec(char* sql)
{
int ret;
char* errmsg = NULL;
ret = sqlite3_exec(db, sql, NULL, NULL, &errmsg);
if(ret == 0)
{
printf("sqlite3_exec sucessful!!\n");
}
else
{
printf("sqlite3_exec failed:%s\n",errmsg);
}
}
void mySqlite3GetTbale(char* sql)
{
int i;
char* errmsg = NULL;
char** resultp = NULL;
int row,column;
int ret = sqlite3_get_table(db, sql, &resultp, &row, &column, &errmsg);
if(ret == 0)
{
for(i = 0; i < (row+1)*column; i++)
{
printf("%s ",resultp[i]);
if((i+1)%column == 0)
printf("\n");
}
}
else
{
printf("sqlite3_get_table failed:%s\n",errmsg);
}
}
int main(int argc, const char *argv[])
{
int i;
char sql[200] = { 0 };//用来保存数据库操作语句
char tablename[30] = { 0 };//用来保存输入的表名
struct student s = { 0 };//用来保存插入的学生信息
//1.打开数据库
int ret = sqlite3_open("./you.db",&db);
if(ret == 0)
{
//(1)创建表
printf("sqlite3_open sucessful!!\n");
printf("Please input tablename:\n");
scanf("%s",tablename);
sprintf(sql,"create table %s (number varchar(20), name varchar(20), age integer, gender varchar(20));",tablename);
mySqlite3Exec(sql);
//(2)插入信息
for(i = 0; i < 3; i++)
{
printf("Please input number name age gender:\n");
scanf("%s%s%d%s",s.number,s.name,&s.age,s.gender);
sprintf(sql,"insert into %s values('%s','%s',%d,'%s');",tablename,s.number,s.name,s.age,s.gender);
mySqlite3Exec(sql);
}
//(3)显示所有信息
sprintf(sql,"select * from %s;",tablename);
mySqlite3GetTbale(sql);
//(4)输入一个年龄
printf("Please input age:\n");
scanf("%d",&s.age);
sprintf(sql,"delete from %s where age > %d;",tablename,s.age);
mySqlite3Exec(sql);
//(5)显示所有信息
sprintf(sql,"select * from %s;",tablename);
mySqlite3GetTbale(sql);
sqlite3_close(db);//关闭数据库
}
else
{
perror("sqlite3_open failed");
}
return 0;
}
这里对文章进行总结:
今天整理了网络编程的下篇,主要归纳了wireshark抓数据包及分析、TCP安全可靠原因分析(三次握手、四次挥手)、数据库sqlite3及操作(shell脚本和C语言对数据库的增、删、改、查及关闭),各个知识点的代码编写及分析。
千丈布、根根织成 加油!