前言

今天整理了网络编程的下篇,主要归纳了wireshark抓数据包及分析、TCP安全可靠原因分析(三次握手、四次挥手)、数据库sqlite3及操作(shell脚本和C语言对数据库的增、删、改、查及关闭),各个知识点的代码编写及分析。

万石谷、粒粒积累 上菜!


提示:以下是本篇文章正文内容,下面案例可供参考

一、网络数据包

1 抓取网络数据包  wireshark(此软件可以用来分析网络数据报)注意: 安装过程中会提示   安装一个组件 winpcap (需要安装)重点: wireshark问题   过滤 tcp udp arp1.1 分析以太网包头第2章 链 路 层.PDF1.2 分析IP报文第3章IP:网际协议.PDFUDP报文第11章UDP:用户数据报协议.PDFTCP报文第17章 TCP:传输控制协议.PDF以太网头 : 源MAC地址 目的MAC地址
IP头:       源IP地址  目的IP地址
UDP头:     源端口号  目的端口号
TCP头:      源端口号  目的端口号 

1.1 以太网包头

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

以太网头 : 源MAC地址 目的MAC地址

1.2 IP报文

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

1.3 UDP报文

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

1.4 TCP报文

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

二、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 <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/socket.h>
#include <netinet/in.h>
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);        //发送connectmemset(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;}}elsecount = 3;}else{count = 0;printf("server is no answer\n");         server_on = 0;close(fd);}}}close(fd);
}///server.c/
#include <stdio.h>
#include <string.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <fcntl.h>
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.252bind(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默认属性, 给flagfcntl(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 <stdio.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <unistd.h>
#include <fcntl.h>int main()
{int fd = socket(AF_INET, SOCK_STREAM, 0); //SOCK_STREAM 不要写成SOCK_DGRAMstruct 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.252bind(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_DONTWAITrecv(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.debsudo 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> .tablestu_infosqlite> //注意事项//*********************************************************************以.开头的是命令 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  englishb、向学生成绩中添加三条记录00001     100  60   7000002     80   90   9000003     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 columnselect * 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.sqldrop 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 <sqlite3.h>
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);//关闭数据库 }elseperror("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 通过这个值获取, 可以写NULLvoid *data 可以写NULLerrmsg  : 如果数据库操作出错,此处保存错误信息返回值:成功返回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");elseprintf("sqlite3_exec失败的错误原因是%s\n",errmsg);sqlite3_close(db);//关闭数据库}elseperror("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语句, selectresultp:用来指向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");}}elseprintf("sqlite3_get_table失败的错误原因是%s\n",errmsg);sqlite3_close(db);//关闭数据库elseperror("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语言对数据库的增、删、改、查及关闭),各个知识点的代码编写及分析。

千丈布、根根织成 加油!

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

  1. 网络基础之传输层协议介绍(“三次握手”和“四次挥手”)

    文章目录 一.TCP/IP协议簇的传输层协议: 1.TCP和UDP 2.TCP报文段 二.TCP的"三次握手"和"四次挥手" 1."三次握手" ...

  2. 终于懂了TCP的三次握手和四次挥手(超精彩分析)

    前言 由于之前有一篇文章获得了超高的认同: 主要写了TCP和UDP协议区别 如果你感兴趣可以看一下: https://blog.csdn.net/qq_17623363/article/details ...

  3. 脑残式网络编程入门(一):跟着动画来学TCP三次握手和四次挥手

    转自即时通讯网:http://www.52im.net/ 1.引言 网络编程中TCP协议的三次握手和四次挥手的问题,在面试中是最为常见的知识点之一.很多读者都知道"三次"和&quo ...

  4. 网络编程 | tcp协议 | udp协议 | 三次握手与四次挥手

    目录 网络编程 为什么要用网络编程 软件开发的结构 1. C/S架构 2.B/S架构 网络基础 一个程序如何在网络上找到另一个程序? ip地址 什么是端口 远程数据传输发展史 OSI七层网络模型 前言 ...

  5. 深入理解TCP三次握手与四次挥手过程以及抓包实验

    一.前言 最近,我正好在做socket相关的实验,发现现在对计算机网络知识有一点点模糊,借此机会,熟悉一下TCP连接过程并利用WireShark工具进行测试. 二.TCP报文首部 源端口号:占16比特 ...

  6. 网络编程知识预备(2) —— 三次握手与四次挥手、半连接状态、2MSL

    参考:网络编程知识预备(2) --三次握手与四次挥手.流量控制(滑动窗口).拥塞控制.半连接状态.2MSL_行稳方能走远的博客-CSDN博客 目录 一.三次握手 什么是三次握手? 三次握手图解 三次握 ...

  7. TCP、UDP、TCP三次握手与四次挥手、TCP如何保证可靠传输、TCP异常分析、拆包和粘包等

    4.OSI模型 4.1.OSI七层模型 4.2.七层模型功能 ​ 物理层:利用传输介质为数据链路层提供物理连接,实现比特流的透明传输,如网线:网卡标准. ​ 数据链路层:接收来自物理层的位流形式的数据 ...

  8. 【Linux网络编程】TCP三次握手和四次挥手

    00. 目录 文章目录 00. 目录 01. 三次握手 02. 四次挥手 03. 三次握手和四次挥手原因 04. 2MSL 05. 附录 01. 三次握手 在 TCP/IP 协议中,TCP 协议提供可 ...

  9. 【Linux网络编程】浅谈 TCP 三次握手和四次挥手

    三次握手 在 TCP/IP 协议中,TCP 协议提供可靠的连接服务,采用三次握手建立一个连接. 第一次握手:建立连接时,客户端发送 syn 包(syn=j)到服务器,并进入 SYN_SEND 状态,等 ...

最新文章

  1. Python中re的match、search、findall、finditer区别正则
  2. 编译安装apr 1.5.2 时报 rm: cannot remove `libtoolT': No such file or directory
  3. 人类高质量视觉模型YOLOP开源:同时处理三大视觉任务,还能各种超越SOTA…
  4. android shortcut livefoulder
  5. C语言数字3转变字符 3 程序,大学c语言知识点总结
  6. mysql crash 如何导出数据库_mysql 如何做到crash后无损恢复数据的
  7. api demo 京东商品详情_jd-demo
  8. 技术债务_如何处理技术债务并节省理智
  9. 检测到无效的异常处理程序例程。_异常控制流(1):异常概述和基本类型
  10. 数据分析必备软件Excel安装包+激活工具
  11. 安卓扫码枪开发,拦截扫码事件
  12. android 多个 前台 挂机,安卓手机如何进行多开挂机?
  13. 不怕汗水与晃动的运动耳机推荐,六款专业的运动耳机
  14. autojs指纹验证
  15. 计算机主板的定义,垃圾佬折腾笔记 篇一:HP Z228主板自测接口定义
  16. 计算机二级教程书pdf MS,全国计算机等级考试二级教程MS Office高级应用教学大纲4.pdf...
  17. php留言板留言界面
  18. 《深入理解Linux内核》-3.3. 进程切换
  19. mysql proxy atlas_mysql-proxy Atlas
  20. 向量的线性表示和线性相关

热门文章

  1. 最新支持的各个版本Visual C ++ 2017 2015 2013 2010 2008等版本下载
  2. 个人永久性免费-Excel催化剂功能第69波-打造最专业易用的商务图表库,即点即用的高级Excel图表...
  3. CentOS7克隆虚拟机需要修改的配置
  4. 数值代数_图像处理_一维图像恢复例子(shaw)
  5. php 递归递实现无限层级
  6. MM要学会的71个烹饪技巧
  7. 滁州职业学院计算机,2020年滁州职业技术学院分类职业技能(系统根据考生所填第一志愿发布试卷进行测试)1、机电类专业技能(满分80分)...
  8. Maven中打胖包和瘦包
  9. 【电子基础】总结·嵌入式硬件基础
  10. 第一次深度学习实习生面试经历