网络编程(wireshare抓数据包及分析、三次握手与四次挥手、数据库sqlite3及操作)笔记-day15


前言

今天整理了网络编程的下篇,主要归纳了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头: 	   源端口号  目的端口号 

网络编程(wireshare抓数据包及分析、三次握手与四次挥手、数据库sqlite3及操作)笔记-day15_第1张图片 

1.1 以太网包头

分析以太网包头:wrieshark 抓数据包 过滤筛选:arp

网络编程(wireshare抓数据包及分析、三次握手与四次挥手、数据库sqlite3及操作)笔记-day15_第2张图片

网络编程(wireshare抓数据包及分析、三次握手与四次挥手、数据库sqlite3及操作)笔记-day15_第3张图片

以太网头 : 源MAC地址 目的MAC地址网络编程(wireshare抓数据包及分析、三次握手与四次挥手、数据库sqlite3及操作)笔记-day15_第4张图片

1.2 IP报文

IP头:        源IP地址  目的IP地址

网络编程(wireshare抓数据包及分析、三次握手与四次挥手、数据库sqlite3及操作)笔记-day15_第5张图片网络编程(wireshare抓数据包及分析、三次握手与四次挥手、数据库sqlite3及操作)笔记-day15_第6张图片

1.3 UDP报文

UDP头:     源端口号  目的端口号

网络编程(wireshare抓数据包及分析、三次握手与四次挥手、数据库sqlite3及操作)笔记-day15_第7张图片

网络编程(wireshare抓数据包及分析、三次握手与四次挥手、数据库sqlite3及操作)笔记-day15_第8张图片

1.4 TCP报文

TCP头:        源端口号  目的端口号

网络编程(wireshare抓数据包及分析、三次握手与四次挥手、数据库sqlite3及操作)笔记-day15_第9张图片

网络编程(wireshare抓数据包及分析、三次握手与四次挥手、数据库sqlite3及操作)笔记-day15_第10张图片

二、TCP(安全可靠分析)

为什么TCP安全可靠?? 
三次握手:连接的时候 connect --- accept 
			SYN -- 连接的时候 
			ACK -- 确认 

四次挥手:断开的时候 close 
			FIN -- 断开的时候 
			ACK -- 确认
重传确认 

2.1 连接(三次握手)

(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.2 断开(四次挥手)及ACK攻击

   (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.3 非阻塞接收

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);

2.4 综合练习

练习:文件服务器作业讲解  取文件列表、下载文件、上传文件
用到的知识点:  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;
}

三、数据库sqlite3

3.1 数据库相关及安装

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 
	//数据表:数据文件中的数据表 学生信息表 
	//字段: 数据表中的列

3.2 sqlite语句(增删改查)


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;//删除表	

3.3 shell脚本执行sqlite

///
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;

3.4 C语言操作sqlite

*****************************************************************************	
用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;
}

3.5 代码案例及分析

###################################练习########################################
(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语言对数据库的增、删、改、查及关闭),各个知识点的代码编写及分析。

千丈布、根根织成 加油!

你可能感兴趣的:(嵌入式Linux,C开发程序设计,tcp/ip,网络协议,ubuntu,数据库架构,sqlite3)