项目二. 网络安全传输系统
sprint0-产品规划与设计
第1课-产品功能展示与模型设计
1. 功能
在底层开发上,先进行驱动开发,再往后发展就是对应的系统开发。要成为系统开发的工程师不了解应用编程的话也是行不通的。
本项目的服务器运行在pc的虚拟机上,客户端则安排在开发板上。我们在虚拟机上可以运行客户端,接下来我们在开发板上进行登录。可以选择的选项有:
update Files
Download Files
Exit
也就是客户端可以将文件进行上传和下载服务器上的内容。
2. 模型
服务器:
在账号管理子系统中我们可以使用直接的编程来完成,但是我们是无法处理更加深入的问题的。所以我们选择了嵌入式数据库的处理方法。
对于传输子系统,我们为了安全的特性,我们进行相应的加密传输机制--OpenSSl机制。在服务器上的设计上,我们选择并发,为了更好的优化,选择线程池技术。
客户端:
只有一个传输子系统--OpenSSl机制。
sprint1-传输子系统设计
第2课-系统程序框架搭建
为了完成我们的目的,我们采用化整为零的思想。即先搭建不带安全传输系统的程序,再把安全传输的知识应进去。
我们先编写下面两个程序:
client.c
#include <stdio.h>
#include <sys/types.h>
#include <netinet/in.h>
#include <sys/socket.h>
#include <sys/stat.h>
#include <errno.h>
#include <unistd.h>
#include <fcntl.h>
#include <string.h>
#define port 3333
int sockclient;
struct sockaddr_in sockaddr1;
char ipaddr[15];
int linkS()
{
if((sockclient=socket(AF_INET,SOCK_STREAM,0))==-1)
{
perror("socket");
exit(0);
}
memset(&sockaddr1,0,sizeof(sockaddr1));
sockaddr1.sin_family = AF_INET;
sockaddr1.sin_addr.s_addr = inet_addr(ipaddr);
sockaddr1.sin_port = htons(port);
if(connect(sockclient,(struct sockaddr* )&sockaddr1,sizeof(sockaddr1))==-1)
{
perror("connect");
exit(0);
}
return 1;
}
//~~~~~~~~~~~~~~~~~~~~~~~上传文件~~~~~~~~~~~~~~~~~~~~~~~~~
void upload_file(char *filename)
{
int fd;
char buf[1024];
int count=0;
int size = strlen(filename);
char cmd = ‘U‘;
struct stat fstat;
if((fd=open(filename,O_RDONLY))==-1)
{
perror("open: ");
return;
}
/*发送上传命令*/
write(sockclient,&cmd,1);
/*发送文件名*/
write(sockclient,(void *)&size,4);
write(sockclient,filename,size);
/*发送文件长度*/
if(stat(filename,&fstat)==-1)
return;
write(sockclient,(void *)&(fstat.st_size),4);
/*发送文件内容*/
while((count=read(fd,(void *)buf,1024))>0)
{
write(sockclient,&buf,count);
}
close(fd);
}
//~~~~~~~~~~~~~~~~~~~~~~~下载文件~~~~~~~~~~~~~~~~~~~~~~~~~
void download_file(char *filename)
{
int fd;
char buf[1024];
int count=0;
int filesize = 0;
int tmpsize = 0;
int namesize = 0;
char cmd = ‘D‘;
int size = strlen(filename);
/*发送下载命令*/
write(sockclient,(void *)&cmd,1);
/*发送文件名*/
write(sockclient,&size,4);
write(sockclient,filename,size);
/*创建文件*/
if((fd=open(filename,O_RDWR|O_CREAT,0777))<0)
{
perror("open error:\n");
}
/*接收文件长度*/
read(sockclient,&filesize,4);
while((count=read(sockclient,(void *)buf,1024))>0)
{
write(fd,&buf,count);
tmpsize += count;
if(tmpsize==filesize)
break;
}
close(fd);
}
void quit()
{
char cmd = ‘Q‘;
write(sockclient,(void *)&cmd,1);
system("clear");
exit(0);
}
void menu()
{
char command;
char file_u[30];
char file_d[30];
char tmp;
char c;
while(1)
{
printf("\n------------------------------ 1.Upload Files ------------------------------\n");
printf("------------------------------ 2.Download Files ------------------------------\n");
printf("------------------------------ 3.Exit ------------------------------------\n");
printf("Please input the Client command:");
command=getchar();
switch(command)
{
case ‘1‘:
{
printf("Upload File:");
while ((c=getchar()) != ‘\n‘ && c != EOF);
fgets(file_u,30,stdin);
file_u[strlen(file_u)-1]=‘\0‘;
upload_file(file_u);
}
break;
case ‘2‘:
{
printf("Download Files:");
while ((c=getchar()) != ‘\n‘ && c != EOF);
fgets(file_d,sizeof(file_d),stdin);
file_d[strlen(file_d)-1]=‘\0‘;
download_file(file_d);
}
break;
case ‘3‘:
quit();
break;
default:
printf("Please input right command\n");
break;
}
}
}
int main(int argc,char *args[])
{
if(argc!=2)
{
printf("format error: you mast enter ipaddr like this : client 192.168.153.129\n");
exit(0);
}
strcpy(ipaddr,args[1]);
linkS();
menu();
close(sockclient);
return 0;
}
server.c
#include <stdio.h>
#include <sys/types.h>
#include <netinet/in.h>
#include <sys/socket.h>
#include <sys/stat.h>
#include <errno.h>
#include <unistd.h>
#include <fcntl.h>
#include <string.h>
char clientIP[15]; /*文件列表*/
int sockfd;
int new_fd;
struct sockaddr_in server_addr;
struct sockaddr_in client_addr;
int sin_size,portnumber = 3333;
void handle(char cmd)
{
char filename[100];
int filesize = 0;
int tmpsize = 0;
int namesize = 0;
int count=0;
int fd;
struct stat fstat;
char buf[1024];
switch(cmd)
{
case ‘U‘:
{
/*接收文件名*/
read(new_fd,&namesize,4);
read(new_fd,(void *)filename,namesize);
filename[namesize]=‘\0‘;
/*创建文件*/
if((fd=open(filename,O_RDWR|O_CREAT,0777))<0)
{
perror("open error:\n");
}
/*接收文件大小*/
read(new_fd,&filesize,4);
while((count=read(new_fd,(void *)buf,1024))>0)
{
write(fd,&buf,count);
tmpsize += count;
if(tmpsize==filesize)
break;
}
close(fd);
}
break;
case ‘D‘:
{
/* 接收文件名 */
read(new_fd,&namesize,4);
read(new_fd,filename,namesize);
filename[namesize]=‘\0‘;
if((fd=open(filename,O_RDONLY))==-1)
{
perror("open: ");
return;
}
/*发送文件长度*/
if(stat(filename,&fstat)==-1)
return;
write(new_fd,&(fstat.st_size),4);
/*发送文件内容*/
while((count=read(fd,(void *)buf,1024))>0)
{
write(new_fd,&buf,count);
}
close(fd);
}
break;
}
}
/*主函数*/
void main()
{
int i=0;
char cmd;
if((sockfd=socket(AF_INET,SOCK_STREAM,0))<0)
{
perror("socket error");
exit(-1);
}
bzero(&server_addr,sizeof(struct sockaddr_in));
server_addr.sin_family=AF_INET;
server_addr.sin_addr.s_addr=htonl(INADDR_ANY);
server_addr.sin_port=htons(portnumber);
if(bind(sockfd,(struct sockaddr*)(&server_addr),sizeof(struct sockaddr))<0)
{
perror("Bind error");
exit(1);
}
if(listen(sockfd, 5)==-1)
{
perror("listen error");
exit(1);
}
while(1)
{
if((new_fd = accept(sockfd,(struct sockaddr *)(&client_addr),&sin_size)) == -1)
{
perror("accept error!");
exit(-1);
}
//strcpy(clientIP,inet_ntoa(client_addr.sin_addr));
while(1)
{
/*读取命令*/
read(new_fd,&cmd,1);
if(cmd == ‘Q‘)
{
close(new_fd);
break;
}
else
{
handle(cmd);
}
}
close(new_fd);
}
close(sockfd);
}
我们将上面的程序编写在两个不同的文件夹中。我们通过命令gcc server.c -o server和gcc client.c -o client,生成两个程序:server和client。我们在一个文件夹中运行./server,在另一个文件夹中运行./client 192.168.153.129。这里的地址根据个人的地址不同而不同。对应的程序是我们服务器端程序server的运行地址。
我们就可以在client程序下进行对应的通讯了。
要是在开发板上运行,就要使用arm-linux-gcc client.c -o client 生成开发板需要的client文件。 我们在哪个文件夹中运行程序就可以在哪个文件夹中上传与下载诚谢
第3课-OpenSSL加密系统
1. 未加密传输的弊端
网络间的文件传输,要是能够通过抓包软件抓住的话。我们能抓到TCP的包以及ENTTEC的包。我们会看到我们的信息被其他人截获了。也就是说,未加密传输是不可取的,我们需要对网络传输进行加密。
2. 加密基本原理
公钥和私钥:
它们总是成对出现的,可以看成一个是客户端使用的(公钥),一个是服务端使用的(私钥)。公钥需要私钥来解开,私钥也需要公钥来解开。
数字证书:
客户端替换公钥盗取别人的信息,服务器端(私钥)并不会了解是否有问题。为了更加安全的使用,防止信息的窃取,我们引入数字证书。数字证书由权威机构发布,有着公钥的名称以及持有者的信息。数字证书的存在,能够防止公钥的替换,增加了可靠性。
3. OpenSSl简介
(1)SSL简介
SSL协议位于TCP/IP协议与各种应用协议之间,利用数据加密技术,可确保数据在网络之间传输过程中不会被截取以及窃听。
SSL协议分为两层:SSL记录协议(SSL Record Protocol):它建立在可靠地传输协议(TCP)之上,为高层协议提供数据封装、压缩、加密等基本功能的支持。SSL握手协议(SSL Handshake Protocol):他建立在SSL记录协议之上,用于在实际的数据传输开始前,通讯双方进行身份认证、协议加密算法、交换加密秘钥等。
(2)OpenSSl
OpenSSL 是一个强大的安全套接字层密码库,囊括主要的密码算法、常用的密钥和证书封装管理功能及SSL协议,并提供丰富的应用程序供测试或其它目的使用。
也可以看成是一个开源的SSL协议。
流程如下:
① 开始
② SSL_library_init(); //SSL库初始化
③ OpeOpenSSl_add_all_algorithms; //载入所有SSL算法
④ SSl_load_error_strings(); //载入所有SSL错误消息
⑤ SSL_CTX_new(SSLv2)_server_method; //产生一个SSL_CTX
⑥ sockfd = socket(AF_INET,SOCK_STREAM,0);
⑦ connect(sockfd);
⑧ SSL_new(ctx); //产生一个新的SSL
⑨ SSL_set_fd(ssl,newfd); //socket加入到SSL
⑩ SSL_connect(ssl); //建立SSL通道
11 SSL_read(ssl,buffer,MAXBUF); //接收服务器来的消息
12 SSL_write(ssl,buffer,strlen(buffer)); //消息给服务器
13 SSL_shutdown(ssl); //关闭SSL通道
14 SSL_free(ssl); //释放SSL
15 close(sockfd);
16 SSL_CTX_free(ctx); //释放SSL
17 结束
其中6,7,15是普通的socket操作,其他的是ssl增加的操作。
4. 移植OpenSSL库到开发板
https://blog.csdn.net/shaoyizhe2006/article/details/7975434
完事之后,我们将对应的库导到我们系统或者开发板的bin目录内。
步骤:
我们用samba服务器将文件导入到我们的linux系统之中,进行解压
tar -xvzf opensell-1.0.0.tar.gz
进入到该文件夹中,cd opensell-1.0.0s,创建一个新的文件夹
mkdir _install
(1)配置:
配置是一切的基础,我们在对应的文件夹中输入下面的指令:
./config no-asm shared --prefix=/home/free/part5-2/opensell-1.0.0s/_install
注意:
这里的路径一定要写全路径,路径写的不对的话后面的编译与安装就会出问题。
no-asm是在交叉编译过程中不使用汇编代码加速编译过程,原因是它的汇编代码对arm格式不支持。如果没有这个参数编译一定出错。shared生成动态数据连接库。prefix=路径,就是你安装的目录。编译完成后在目录下有四个文件夹:bin lib include ssl。这四个目录是非常重要的,openssl编程需要这些目录的配合。
(2)修改
修改makefile
1) CC= gcc 改成 CC = arm-none-linux-gnueabi-gcc;(根据你自己的交叉编译环境设置,我的交叉编译环境是:arm-none-linux-gnueabi-)
2) 删除 CFLAG= 中的 “-march=pentium”;
3) AR=ar $(ARFLAGS) r 改为
AR=arm-none-linux-gnueabi-ar $(ARFLAGS) r;
4) ARD=ar $(ARFLAGS) d 改为
ARD=arm-none-linux-gnueabi-ar $(ARFLAGS) d;
5)RANLIB= /usr/bin/ranlib 改为
RANLIB= arm-none-linux-gnueabi-ranlib;
其中有的项我们删除,没有的不管,基本山只有第1,3,5是用得到的。
(3)编译:make
(4)安装:make
install
以上步骤执行完成后,在_install文件夹中,会找到bin lib include ssl四个子文件夹。
5. 传递,我们进入到_install文件夹中的lib中,输入指令:
cp libcrypto*
/home/free/part3/rootfs/lib
cp libssl*
/home/free/part3/rootfs/lib
这样我们就完成了本节的内容。
第4课-加密传输优化
1. 服务器模型
我们首先看一下ssl的操作模型:
流程如下:
18 开始
19 SSL_library_init(); //SSL库初始化
20 OpenSSl_add_all_algorithms; //载入所有SSL算法
21 SSl_load_error_strings(); //载入所有SSL错误消息
22 SSL_CTX_new(SSLv2)_server_method; //产生一个SSL_CTX
23 SSL_CTX_use_certificate_file //载入用户的数字证书
24 SSL_CTX_use_PrivateKey_file //载入用户私钥
25 SSL_set_fd(ssl,newfd); //socket加入到SSL
26 SSL_new(ctx); //产生一个新的SSL
27 newfd = accept(servfd);
28 bind(servfd);
29 sockfd = socket(AF_INET,SOCK_STREAM,0);
30 SSL_CTX_check_privat_key; //检查用户是否连接正确
31 SSL_accept(ssl); //建立SSL连接
32 SSL_write(ssl,buffer,strlen(buffer)); //消息给服务器
33 SSL_read(ssl,buffer,MAXBUF); //接收服务器来的消息
34 SSL_shutdown(ssl); //关闭SSL通道
35 SSL_free(ssl); //释放SSL
36 close(newfd); //关闭
37 结束
其中10,11,12,19是普通的socket操作,其他的是ssl增加的操作。
我们编写的server.c程序如下:
#include <stdio.h>
#include <sys/types.h>
#include <netinet/in.h>
#include <sys/socket.h>
#include <sys/stat.h>
#include <errno.h>
#include <unistd.h>
#include <fcntl.h>
#include <string.h>
#include <openssl/ssl.h>
#include <openssl/err.h>
char clientIP[15]; /*文件列表*/
int sockfd;
int new_fd;
struct sockaddr_in server_addr;
struct sockaddr_in client_addr;
int sin_size,portnumber = 3333;
SSL_CTX *ctx;
SSL *ssl;
void handle(char cmd)
{
char filename[100];
int filesize = 0;
int tmpsize = 0;
int namesize = 0;
int count=0;
int fd;
struct stat fstat;
char buf[1024];
switch(cmd)
{
case ‘U‘:
{
/*接收文件名*/
//read(new_fd,&namesize,4);
//read(new_fd,(void
*)filename,namesize);
SSL_read(ssl,&namesize,4);
SSL_read(ssl,(void *)filename,namesize);
filename[namesize]=‘\0‘;
/*创建文件*/
if((fd=open(filename,O_RDWR|O_CREAT,0777))<0)
{
perror("open error:\n");
}
/*接收文件大小*/
//read(new_fd,&filesize,4);
SSL_read(ssl,&filesize,4);
while((count=SSL_read(ssl,(void *)buf,1024))>0)
{
write(fd,&buf,count);
tmpsize
+= count;
if(tmpsize==filesize)
break;
}
close(fd);
}
break;
case ‘D‘:
{
/* 接收文件名 */
SSL_read(ssl,&namesize,4);
SSL_read(ssl,filename,namesize);
filename[namesize]=‘\0‘;
if((fd=open(filename,O_RDONLY))==-1)
{
perror("open: ");
return;
}
/*发送文件长度*/
if(stat(filename,&fstat)==-1)
return;
SSL_write(ssl,&(fstat.st_size),4);
/*发送文件内容*/
while((count=read(fd,(void *)buf,1024))>0)
{
SSL_write(ssl,&buf,count);
}
close(fd);
}
break;
}
}
/*主函数*/
void main()
{
int i=0;
char cmd;
char pwd[100];
char* temp;
/*SSL初始化*/
SSL_library_init();
OpenSSL_add_all_algorithms();
SSL_load_error_strings();
ctx = SSL_CTX_new(SSLv23_server_method());
/* 载入数字证书,
此证书用来发送给客户端。 证书里包含有公钥 */
getcwd(pwd,100);
if(strlen(pwd)==1)
pwd[0]=‘\0‘;
if (SSL_CTX_use_certificate_file(ctx, temp=strcat(pwd,"/cacert.pem"), SSL_FILETYPE_PEM) <= 0)
{
ERR_print_errors_fp(stdout);
exit(1);
}
/* 载入用户私钥 */
getcwd(pwd,100);
if(strlen(pwd)==1)
pwd[0]=‘\0‘;
if (SSL_CTX_use_PrivateKey_file(ctx, temp=strcat(pwd,"/privkey.pem"), SSL_FILETYPE_PEM) <= 0)
{
ERR_print_errors_fp(stdout);
exit(1);
}
/* 检查用户私钥是否正确 */
if (!SSL_CTX_check_private_key(ctx))
{
ERR_print_errors_fp(stdout);
exit(1);
}
if((sockfd=socket(AF_INET,SOCK_STREAM,0))<0)
{
perror("socket error");
exit(-1);
}
bzero(&server_addr,sizeof(struct sockaddr_in));
server_addr.sin_family=AF_INET;
server_addr.sin_addr.s_addr=htonl(INADDR_ANY);
server_addr.sin_port=htons(portnumber);
if(bind(sockfd,(struct sockaddr*)(&server_addr),sizeof(struct sockaddr))<0)
{
perror("Bind error");
exit(1);
}
if(listen(sockfd, 5)==-1)
{
perror("listen error");
exit(1);
}
while(1)
{
if((new_fd = accept(sockfd,(struct sockaddr
*)(&client_addr),&sin_size)) == -1)
{
perror("accept error!");
exit(-1);
}
/*创建SSL*/
ssl = SSL_new(ctx);
SSL_set_fd(ssl, new_fd);
if (SSL_accept(ssl) == -1)
{
perror("accept");
close(new_fd);
}
while(1)
{
/*读取命令*/
//read(new_fd,&cmd,1);
SSL_read(ssl,&cmd,1);
if(cmd == ‘Q‘)
{
close(new_fd);
break;
}
else
{
handle(cmd);
}
}
/*SSL退出*/
SSL_shutdown(ssl);
SSL_free(ssl);
close(new_fd);
}
close(sockfd);
}
在相应的文件夹中输入gcc
server.c -o server -lssl生成server运行文件。在这之前我们把数字证书和私钥也放在server的文件夹中,两个文件分别是:cacert.pem,privkey.pem。
2. 制作公钥/私钥
我们的加密传输就是基于公钥/私钥的,而公钥就是放在我们的数字证书里面的。大致的步骤是服务器将数字证书传递给客户端,这样客户端就有公钥了。之后是在我们的服务器里面加载私钥。以后再通信的时候就可以通过公钥/私钥来进行了。
我们的之前安装的openssl之中,包含着“库”以及“工具”。我们使用其中的工具就可以创建数字证书和私钥。
产生私钥:
openssl genrsa
-out privkey.pem 2048
genrsa表示参数,out表示输出,privkey.pem是文件名字,2048是位数。
产生公钥:
openssl req -new
-x509 -key privkey.pem -out cacert.pem -days 1095
创建公钥需要把私钥传递给它-key
privkey.pem表示的就是将私钥传递给它,
cacert.pem表示创建好的公钥所在的数字证书,
-days 1095表示天数的1095。
在生成文件的时候,会填写一些参数,这些参数随便填写就好。
3. 客户端模型
流程如下:
38 开始
39 SSL_library_init(); //SSL库初始化
40 OpeOpenSSl_add_all_algorithms; //载入所有SSL算法
41 SSl_load_error_strings(); //载入所有SSL错误消息
42 SSL_CTX_new(SSLv2)_server_method; //产生一个SSL_CTX
43 sockfd = socket(AF_INET,SOCK_STREAM,0); //创建
44 connect(sockfd); //连接服务器
45 SSL_new(ctx); //产生一个新的SSL
46 SSL_set_fd(ssl,newfd); //socket加入到SSL
47 SSL_connect(ssl); //建立SSL通道
48 SSL_read(ssl,buffer,MAXBUF); //接收服务器来的消息
49 SSL_write(ssl,buffer,strlen(buffer)); //消息给服务器
50 SSL_shutdown(ssl); //关闭SSL通道
51 SSL_free(ssl); //释放SSL
52 close(sockfd); //关闭
53 SSL_CTX_free(ctx); //释放SSL
54 结束
其中6,7,15是普通的socket操作,其他的是ssl增加的操作。
编写的程序client.c如下:
#include <stdio.h>
#include <sys/types.h>
#include <netinet/in.h>
#include <sys/socket.h>
#include <sys/stat.h>
#include <errno.h>
#include <unistd.h>
#include <fcntl.h>
#include <string.h>
#include <openssl/ssl.h>
#include <openssl/err.h>
#define port 3333
int
sockclient;
struct sockaddr_in sockaddr1;
char ipaddr[15];
SSL_CTX *ctx;
SSL *ssl;
int linkS()
{
if((sockclient=socket(AF_INET,SOCK_STREAM,0))==-1)
{
perror("socket");
exit(0);
}
memset(&sockaddr1,0,sizeof(sockaddr1));
sockaddr1.sin_family
= AF_INET;
sockaddr1.sin_addr.s_addr
= inet_addr(ipaddr);
sockaddr1.sin_port
= htons(port);
if(connect(sockclient,(struct sockaddr* )&sockaddr1,sizeof(sockaddr1))==-1)
{
perror("connect");
exit(0);
}
/*创建SSL*/
ssl = SSL_new(ctx);
SSL_set_fd(ssl, sockclient);
SSL_connect(ssl);
return 1;
}
//~~~~~~~~~~~~~~~~~~~~~~~上传文件~~~~~~~~~~~~~~~~~~~~~~~~~
void upload_file(char *filename)
{
int fd;
char buf[1024];
int count=0;
int size = strlen(filename);
char cmd = ‘U‘;
struct stat fstat;
if((fd=open(filename,O_RDONLY))==-1)
{
perror("open: ");
return;
}
/*发送上传命令*/
//write(sockclient,&cmd,1);
SSL_write(ssl,&cmd,1);
/*发送文件名*/
//write(sockclient,(void
*)&size,4);
//write(sockclient,filename,size);
SSL_write(ssl,(void *)&size,4);
SSL_write(ssl,filename,size);
/*发送文件长度*/
if(stat(filename,&fstat)==-1)
return;
//write(sockclient,(void
*)&(fstat.st_size),4);
SSL_write(ssl,(void *)&(fstat.st_size),4);
/*发送文件内容*/
while((count=read(fd,(void *)buf,1024))>0)
{
//write(sockclient,&buf,count);
SSL_write(ssl,&buf,count);
}
close(fd);
}
//~~~~~~~~~~~~~~~~~~~~~~~下载文件~~~~~~~~~~~~~~~~~~~~~~~~~
void download_file(char *filename)
{
int fd;
char buf[1024];
int count=0;
int filesize = 0;
int tmpsize = 0;
int namesize = 0;
char cmd = ‘D‘;
int size = strlen(filename);
/*发送下载命令*/
//write(sockclient,(void
*)&cmd,1);
SSL_write(ssl,(void *)&cmd,1);
/*发送文件名*/
//write(sockclient,&size,4);
//write(sockclient,filename,size);
SSL_write(ssl,&size,4);
SSL_write(ssl,filename,size);
/*创建文件*/
if((fd=open(filename,O_RDWR|O_CREAT,0777))<0)
{
perror("open error:\n");
}
/*接收文件长度*/
//read(sockclient,&filesize,4);
SSL_read(ssl,&filesize,4);
while((count=SSL_read(ssl,(void *)buf,1024))>0)
{
write(fd,&buf,count);
tmpsize
+= count;
if(tmpsize==filesize)
break;
}
close(fd);
}
void quit()
{
char cmd = ‘Q‘;
//write(sockclient,(void
*)&cmd,1);
SSL_write(ssl,(void *)&cmd,1);
system("clear");
/*SSL退出*/
SSL_shutdown(ssl);
SSL_free(ssl);
close(sockclient);
SSL_CTX_free(ctx);
exit(0);
}
void menu()
{
char command;
char file_u[30];
char file_d[30];
char tmp;
char c;
while(1)
{
printf("\n------------------------------ 1.Upload Files ------------------------------\n");
printf("------------------------------ 2.Download Files ------------------------------\n");
printf("------------------------------ 3.Exit
------------------------------------\n");
printf("Please input the Client
command:");
command=getchar();
switch(command)
{
case ‘1‘:
{
printf("Upload File:");
while ((c=getchar()) != ‘\n‘ && c != EOF);
fgets(file_u,30,stdin);
file_u[strlen(file_u)-1]=‘\0‘;
upload_file(file_u);
}
break;
case ‘2‘:
{
printf("Download Files:");
while ((c=getchar()) != ‘\n‘ && c != EOF);
fgets(file_d,sizeof(file_d),stdin);
file_d[strlen(file_d)-1]=‘\0‘;
download_file(file_d);
}
break;
case ‘3‘:
quit();
break;
default:
printf("Please input right
command\n");
break;
}
}
}
int main(int argc,char *args[])
{
if(argc!=2)
{
printf("format error: you mast enter ipaddr
like this : client 192.168.0.6\n");
exit(0);
}
strcpy(ipaddr,args[1]);
/*SSL库初始化*/
SSL_library_init();
OpenSSL_add_all_algorithms();
SSL_load_error_strings();
ctx = SSL_CTX_new(SSLv23_client_method());
linkS();
menu();
return 0;
}
我们在对应的文件夹里面输入:
arm-linux-gcc -I
/home/free/part5-2/_install/include/ -L /home/free/part5-2/_install/lib
client.c -o client -lssl -lcrypto
这里我们需要加入额外的指令库-lssl
-lcrypto(加密库)
-I
/home/free/part5-2/_install/include/表示用到的头文件所在的位置,因为我们之前变异的是在pc机上运行的文件,pc机上已经有了这个头文件,所以不需要指明。
-L
/home/free/part5-2/_install/lib 表示用到的库的所在的位置。
我们将编译好的client文件复制到我们的开发板中。
4. 运行
在linux系统中运行./server,在开发板中运行./client 192.168.153.129,这是出现了下拉菜单,我们便可以进行文件的上传与下载。
sprint2-线程池技术优化
第5课-线程池优化技术
问题:
我们之前编辑好的服务器与客户端是只能一对一的,也就是只能有一个客户端进行运行。这种程序的处理模式一般是,一个客户端进行处理,不处理完成,服务器端移植就会处于等待状态。现实的工作中,我们要使用并发的方式。一旦客户端连接上了,我们不是用主程序去处理,而是交给子程序或者线程来处理。我们一般会考虑线程,因为线程的开销是比子进程的开销小的。
当我们要处理多个线程,特别多的时候,我们创件很多的线程也会很麻烦的。于是我们使用线程池的技术,将待使用的线程都放在线程池中,使用的时候从池中抽取,使用完成在放回去。
1. 线程池工作原理
线程池就是有一堆已经创建好了的线程,当有新的任务需要处理的时候,就从这个池子里面取一个空闲等待的线程来处理该任务,当处理完成了就再次把该线程放回池中,以供后面的任务使用,当池子里的线程全都处理忙碌状态时,这时任务需要稍作等待。
线程的创建和销毁比之进程的创建和销毁是轻量级的,但是当我们的任务需要大量进行大量线程的创建和销毁操作时,这个消耗就会变成的相当大。线程池的好处就在于线程复用,一个任务处理完成后,当前线程可以直接处理下一个任务,而不是销毁后再创建,非常适用于连续产生大量并发任务的场合。
2. 线程池使用示例
我们看一下程序thread.c:
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <pthread.h>
typedef struct task
{
void *(*process) (void *arg);
void *arg;
struct task *next;
} Cthread_task;
/*线程池结构*/
typedef struct
{
pthread_mutex_t queue_lock;
pthread_cond_t queue_ready;
/*链表结构,线程池中所有等待任务*/
Cthread_task *queue_head;
/*是否销毁线程池*/
int shutdown;
pthread_t *threadid;
/*线程池中线程数目*/
int max_thread_num;
/*当前等待的任务数*/
int cur_task_size;
} Cthread_pool;
static Cthread_pool *pool = NULL;
void *thread_routine (void *arg);
void pool_init (int max_thread_num)
{
int i = 0;
pool = (Cthread_pool *) malloc (sizeof (Cthread_pool));
pthread_mutex_init (&(pool->queue_lock), NULL);
/*初始化条件变量*/
pthread_cond_init (&(pool->queue_ready), NULL);
pool->queue_head = NULL;
pool->max_thread_num = max_thread_num;
pool->cur_task_size = 0;
pool->shutdown = 0;
pool->threadid = (pthread_t *) malloc (max_thread_num * sizeof (pthread_t));
for (i = 0; i <
max_thread_num; i++)
{
pthread_create (&(pool->threadid[i]), NULL, thread_routine,
NULL);
}
}
/*向线程池中加入任务*/
int pool_add_task (void *(*process) (void *arg), void *arg)
{
/*构造一个新任务*/
Cthread_task *task = (Cthread_task *) malloc (sizeof (Cthread_task));
task->process = process;
task->arg = arg;
task->next = NULL;
pthread_mutex_lock (&(pool->queue_lock));
/*将任务加入到等待队列中*/
Cthread_task *member = pool->queue_head;
if (member != NULL)
{
while (member->next != NULL)
member = member->next;
member->next = task;
}
else
{
pool->queue_head = task;
}
pool->cur_task_size++;
pthread_mutex_unlock (&(pool->queue_lock));
pthread_cond_signal (&(pool->queue_ready));
return 0;
}
/*销毁线程池,等待队列中的任务不会再被执行,但是正在运行的线程会一直
把任务运行完后再退出*/
int pool_destroy ()
{
if (pool->shutdown)
return -1;/*防止两次调用*/
pool->shutdown = 1;
/*唤醒所有等待线程,线程池要销毁了*/
pthread_cond_broadcast (&(pool->queue_ready));
/*阻塞等待线程退出,否则就成僵尸了*/
int i;
for (i = 0; i <
pool->max_thread_num; i++)
pthread_join (pool->threadid[i], NULL);
free (pool->threadid);
/*销毁等待队列*/
Cthread_task *head = NULL;
while (pool->queue_head !=
NULL)
{
head = pool->queue_head;
pool->queue_head = pool->queue_head->next;
free (head);
}
/*条件变量和互斥量也别忘了销毁*/
pthread_mutex_destroy(&(pool->queue_lock));
pthread_cond_destroy(&(pool->queue_ready));
free (pool);
/*销毁后指针置空是个好习惯*/
pool=NULL;
return 0;
}
void * thread_routine (void *arg)
{
printf ("starting
thread 0x%x\n",
pthread_self ());
while (1)
{
pthread_mutex_lock (&(pool->queue_lock));
while (pool->cur_task_size == 0
&& !pool->shutdown)
{
printf ("thread 0x%x is waiting\n", pthread_self ());
pthread_cond_wait
(&(pool->queue_ready), &(pool->queue_lock));
}
/*线程池要销毁了*/
if (pool->shutdown)
{
/*遇到break,continue,return等跳转语句,千万不要忘记先解锁*/
pthread_mutex_unlock
(&(pool->queue_lock));
printf ("thread 0x%x will exit\n", pthread_self ());
pthread_exit (NULL);
}
printf ("thread
0x%x is starting to work\n", pthread_self ());
/*待处理任务减1,并取出链表中的头元素*/
pool->cur_task_size--;
Cthread_task *task = pool->queue_head;
pool->queue_head = task->next;
pthread_mutex_unlock (&(pool->queue_lock));
/*调用回调函数,执行任务*/
(*(task->process))
(task->arg);
free (task);
task = NULL;
}
/*这一句应该是不可达的*/
pthread_exit (NULL);
}
void * myprocess (void *arg)
{
printf ("threadid
is 0x%x, working on task %d\n", pthread_self (),*(int *) arg);
sleep (1);/*休息一秒,延长任务的执行时间*/
return NULL;
}
int main (int argc, char **argv)
{
pool_init (3);/*线程池中最多三个活动线程*/
/*连续向池中投入10个任务*/
int *workingnum = (int *) malloc (sizeof (int) * 10);
int i;
for (i = 0; i < 10; i++)
{
workingnum[i] = i;
pool_add_task (myprocess, &workingnum[i]);
}
/*等待所有任务完成*/
sleep (5);
/*销毁线程池*/
pool_destroy ();
free (workingnum);
return 0;
}
我们把该程序放进linux系统,输入命令:gcc thread.c -o thread -lpthread,对于线程的编译我们一定要后加-lpthread。运行文件./thread就会向我们显示线程池的工作情况。
3. 优化服务端程序
根据线程池的方法,我们改进server.c代码,如下:
#include <stdio.h>
#include <sys/types.h>
#include <netinet/in.h>
#include <sys/socket.h>
#include <sys/stat.h>
#include <errno.h>
#include <unistd.h>
#include <fcntl.h>
#include <string.h>
#include <openssl/ssl.h>
#include <openssl/err.h>
#include <pthread.h>
char clientIP[15]; /*文件列表*/
int sockfd;
int new_fd;
struct sockaddr_in server_addr;
struct sockaddr_in client_addr;
int sin_size,portnumber = 3333;
SSL_CTX *ctx;
typedef struct task
{
void *(*process) (int arg);
int arg;
struct task *next;
} Cthread_task;
/*线程池结构*/
typedef struct
{
pthread_mutex_t queue_lock;
pthread_cond_t queue_ready;
/*链表结构,线程池中所有等待任务*/
Cthread_task *queue_head;
/*是否销毁线程池*/
int shutdown;
pthread_t *threadid;
/*线程池中线程数目*/
int max_thread_num;
/*当前等待的任务数*/
int cur_task_size;
} Cthread_pool;
Cthread_pool *pool = NULL;
void * thread_routine (void *arg)
{
printf ("starting
thread 0x%x\n",
pthread_self ());
while (1)
{
pthread_mutex_lock (&(pool->queue_lock));
while (pool->cur_task_size == 0
&& !pool->shutdown)
{
printf ("thread 0x%x is waiting\n", pthread_self ());
pthread_cond_wait
(&(pool->queue_ready), &(pool->queue_lock));
}
/*线程池要销毁了*/
if (pool->shutdown)
{
/*遇到break,continue,return等跳转语句,千万不要忘记先解锁*/
pthread_mutex_unlock
(&(pool->queue_lock));
printf ("thread 0x%x will exit\n", pthread_self ());
pthread_exit (NULL);
}
printf ("thread
0x%x is starting to work\n", pthread_self ());
/*待处理任务减1,并取出链表中的头元素*/
pool->cur_task_size--;
Cthread_task *task = pool->queue_head;
pool->queue_head = task->next;
pthread_mutex_unlock (&(pool->queue_lock));
/*调用回调函数,执行任务*/
(*(task->process)) (task->arg);
free (task);
task = NULL;
}
/*这一句应该是不可达的*/
pthread_exit (NULL);
}
void pool_init (int max_thread_num)
{
int i = 0;
pool = (Cthread_pool *) malloc (sizeof (Cthread_pool));
pthread_mutex_init (&(pool->queue_lock), NULL);
/*初始化条件变量*/
pthread_cond_init (&(pool->queue_ready), NULL);
pool->queue_head = NULL;
pool->max_thread_num = max_thread_num;
pool->cur_task_size = 0;
pool->shutdown = 0;
pool->threadid = (pthread_t *) malloc (max_thread_num * sizeof (pthread_t));
for (i = 0; i <
max_thread_num; i++)
{
pthread_create (&(pool->threadid[i]), NULL, thread_routine,
NULL);
}
}
int pool_add_task (void *(*process) (int arg), int arg)
{
/*构造一个新任务*/
Cthread_task *task = (Cthread_task *) malloc (sizeof (Cthread_task));
task->process = process;
task->arg = arg;
task->next = NULL;
pthread_mutex_lock (&(pool->queue_lock));
/*将任务加入到等待队列中*/
Cthread_task *member = pool->queue_head;
if (member != NULL)
{
while (member->next != NULL)
member = member->next;
member->next = task;
}
else
{
pool->queue_head = task;
}
pool->cur_task_size++;
pthread_mutex_unlock (&(pool->queue_lock));
pthread_cond_signal (&(pool->queue_ready));
return 0;
}
void handle(char cmd,SSL *ssl)
{
char filename[100];
int filesize = 0;
int tmpsize = 0;
int namesize = 0;
int count=0;
int fd;
struct stat fstat;
char buf[1024];
switch(cmd)
{
case ‘U‘:
{
/*接收文件名*/
//read(new_fd,&namesize,4);
//read(new_fd,(void
*)filename,namesize);
SSL_read(ssl,&namesize,4);
SSL_read(ssl,(void *)filename,namesize);
filename[namesize]=‘\0‘;
/*创建文件*/
if((fd=open(filename,O_RDWR|O_CREAT,0777))<0)
{
perror("open error:\n");
}
/*接收文件大小*/
//read(new_fd,&filesize,4);
SSL_read(ssl,&filesize,4);
while((count=SSL_read(ssl,(void *)buf,1024))>0)
{
write(fd,&buf,count);
tmpsize
+= count;
if(tmpsize==filesize)
break;
}
close(fd);
}
break;
case ‘D‘:
{
/* 接收文件名 */
SSL_read(ssl,&namesize,4);
SSL_read(ssl,filename,namesize);
filename[namesize]=‘\0‘;
if((fd=open(filename,O_RDONLY))==-1)
{
perror("open: ");
return;
}
/*发送文件长度*/
if(stat(filename,&fstat)==-1)
return;
SSL_write(ssl,&(fstat.st_size),4);
/*发送文件内容*/
while((count=read(fd,(void *)buf,1024))>0)
{
SSL_write(ssl,&buf,count);
}
close(fd);
}
break;
}
}
void * process(int arg)
{
int tmp_fd = arg;
SSL
*ssl;
char cmd;
/*创建SSL*/
ssl = SSL_new(ctx);
SSL_set_fd(ssl, tmp_fd);
if (SSL_accept(ssl) == -1)
{
perror("accept");
close(tmp_fd);
}
while(1)
{
/*读取命令*/
//read(new_fd,&cmd,1);
SSL_read(ssl,&cmd,1);
if(cmd == ‘Q‘)
{
/*SSL退出*/
SSL_shutdown(ssl);
SSL_free(ssl);
close(tmp_fd);
break;
}
else
{
handle(cmd,ssl);
}
}
return NULL;
}
/*主函数*/
void main()
{
int i=0;
char pwd[100];
char* temp;
/*SSL初始化*/
SSL_library_init();
OpenSSL_add_all_algorithms();
SSL_load_error_strings();
ctx = SSL_CTX_new(SSLv23_server_method());
/* 载入数字证书,
此证书用来发送给客户端。 证书里包含有公钥 */
getcwd(pwd,100);
if(strlen(pwd)==1)
pwd[0]=‘\0‘;
if (SSL_CTX_use_certificate_file(ctx, temp=strcat(pwd,"/cacert.pem"), SSL_FILETYPE_PEM) <= 0)
{
ERR_print_errors_fp(stdout);
exit(1);
}
/* 载入用户私钥 */
getcwd(pwd,100);
if(strlen(pwd)==1)
pwd[0]=‘\0‘;
if (SSL_CTX_use_PrivateKey_file(ctx, temp=strcat(pwd,"/privkey.pem"), SSL_FILETYPE_PEM) <= 0)
{
ERR_print_errors_fp(stdout);
exit(1);
}
/* 检查用户私钥是否正确 */
if (!SSL_CTX_check_private_key(ctx))
{
ERR_print_errors_fp(stdout);
exit(1);
}
if((sockfd=socket(AF_INET,SOCK_STREAM,0))<0)
{
perror("socket error");
exit(-1);
}
bzero(&server_addr,sizeof(struct sockaddr_in));
server_addr.sin_family=AF_INET;
server_addr.sin_addr.s_addr=htonl(INADDR_ANY);
server_addr.sin_port=htons(portnumber);
if(bind(sockfd,(struct sockaddr*)(&server_addr),sizeof(struct sockaddr))<0)
{
perror("Bind error");
exit(1);
}
if(listen(sockfd, 5)==-1)
{
perror("listen error");
exit(1);
}
//1. 初始化线程池
pool_init(5);
while(1)
{
if((new_fd = accept(sockfd,(struct sockaddr
*)(&client_addr),&sin_size)) == -1)
{
perror("accept error!");
exit(-1);
}
//2. 执行process,将process任务交给线程池
pool_add_task(process,new_fd);
}
close(sockfd);
}
sprint3-账号管理子系统设计
第6课-Sqlite嵌入式数据库移植
1. 数据库基础知识
在计算机系统中,保存数据的方式一般有两种:
(1)普通文件方式
(2)数据库方式
相比于普通文件方式,使用数据库来管理大批量数据(数据之间一般有关联)具有更高的效率与安全性。
数据库系统一般由3个部分构成:
(1)数据库
(2)数据库管理系统
(3)数据库访问应用
2. 数据库的系统构成
在数据库中,数据都是以表的形式存在。表与表之间可能存在关联关系。
SQL(结构化查询语言)是一种特殊的编程语言,用于访问数据库中的数据。
简单的SQL语言:
(1)创建一张表
create table tabname(col1 type1 [not null]
[primary key],col2 type2 [not null],..)
(2)向表中插入数据
insert into table1(field1,field2)
values(value1,value2)
(3)从表中查询需要的数据
select * from table1 where field1 like ’%value1%’
(4)删除表中的数据
delete from table1 where 范围
3. 移植sqlite到开发板
概念:https://www.toutiao.com/a6618886302395793934/
sqlite3移植到ARM开发板:https://blog.csdn.net/xiongzhizhu/article/details/52372655
将压缩包文件发送到linux系统,在对应工作目录中进行解压。进入解压目录,新建_install文件夹。
配置:./configure --host=arm-linux
--prefix=/home/free/part5-2/sqlite-autoconf-3070800/_install
编译:make
在编译的时候,出现问题,报错3.7.8文件出现问题。我们打开Makefile文件:vim Makefile
直接输入:/3.7.8找到对应文字,删除前面一个多余的空格就好。
安装:make install
安装完成后,我们进入_install目录,可以看到四个文件:bin include lib share
我们进入bin目录,看到sqlite3文件,将它拷贝到我们的开发板的根文件系统中:
cp
sqlite3 /home/free/part3/rootfs/sbin/
我们进入include文件夹,里面放了一些头文件,它们是给我们后期写的数据库程序用的头文件,暂时用不到。
lib文件夹里面放了一些我们调用时候用到的一些库,我们将其中的动态库拷贝出去:
cp
*so* /home/free/part3/rootfs/lib/
这样我们就完成了我们相应的移植工作。
4. Sqlite数据库访问应用程序
创建一个关于学生信息的表,并将它们提取出来。
test_db.c
#include <stdio.h>
#include <sqlite3.h>
static int callback(void *NotUsed, int argc, char **argv, char **azColName)
{
int i;
for(i=0; i<argc; i++)
{
printf("%s
= %s\n",
azColName[i], argv[i] ? argv[i] : "NULL");
}
printf("\n");
return 0;
}
int main(int argc, char **argv)
{
sqlite3 *db;
char *zErrMsg = 0;
int rc;
if( argc!=3 )
{
fprintf(stderr, "Usage: %s DATABASE SQL-STATEMENT\n", argv[0]);
}
/*打开数据库*/
rc = sqlite3_open(argv[1], &db);
if( rc )
{
fprintf(stderr, "Can‘t open database: %s\n", sqlite3_errmsg(db));
sqlite3_close(db);
}
/*执行sql语言*/
rc = sqlite3_exec(db, argv[2], callback, 0, &zErrMsg);
if( rc!=SQLITE_OK )
{
fprintf(stderr, "SQL error: %s\n", zErrMsg);
}
/*关闭数据库*/
sqlite3_close(db);
return 0;
}
我们将该.c文件拷贝到linux系统,进行编译:arm-linux-gcc tset_db.c -I
./sqlite-autoconf-3070800/_install/include/ -L
./sqlite-autoconf-3070800/_install/lib/ -lsqlite3 -o test_db
生成test_db文件。将它拷贝到开发板中:cp test_db /home/free/part3/rootfs/
5. 测试
我们开发板运行起来,linux系统中要开启nfs服务器:/etc/init.d/nfs start,开启tftp服务器:/etc/init.d/xinetd start,关闭防火墙:/etc/init.d/iptables stop,关闭对应管理系统:
setenforce permissive,以上的一切操作,是在红帽系统上运行的。
(1)创建
我们在开发板上输入:./test_db
xy.db “create table stu(name varchar(10),number smallint);”。我们建立的数据库的名字要是没有的话就会新建立一个数据库,这里的名字是xy.db,我们创建数据库的表的名字是stu,表中有两列,第一列是name,类型是10个长度的字符串,第二列是number,类型是smallint。
(2)插入
./test_db xy.db “insert
into stu values(‘david’,1);”
./test_db xy.db “insert
into stu values(‘simon’,2);”
(3)查询
./test_db xy.db “select
* from stu;”
这是界面会显示:
name=david
number=1
name=simon
number=2
该信息由callback()函数打印出来。
(4)删除
./test_db xy.db “delete
from stu where 1;”
将表中的信息删除。
第7课-账号管理子系统设计(未完成)
思路:
我们事先在数据库中存入我们需要的身份信息,我们对比访问的用户填写的身份信息。若是在数据库中存在,那么很好允许进入,功能允许使用,否则不能。
修改的client.c:
#include <stdio.h>
#include <sys/types.h>
#include <netinet/in.h>
#include <sys/socket.h>
#include <sys/stat.h>
#include <errno.h>
#include <unistd.h>
#include <fcntl.h>
#include <string.h>
#include <openssl/ssl.h>
#include <openssl/err.h>
#include <sqlite3.h>
#define port 3333
int
sockclient;
struct sockaddr_in sockaddr1;
char ipaddr[15];
SSL_CTX *ctx;
SSL *ssl;
int linkS()
{
if((sockclient=socket(AF_INET,SOCK_STREAM,0))==-1)
{
perror("socket");
exit(0);
}
memset(&sockaddr1,0,sizeof(sockaddr1));
sockaddr1.sin_family
= AF_INET;
sockaddr1.sin_addr.s_addr
= inet_addr(ipaddr);
sockaddr1.sin_port
= htons(port);
if(connect(sockclient,(struct sockaddr* )&sockaddr1,sizeof(sockaddr1))==-1)
{
perror("connect");
exit(0);
}
/*创建SSL*/
ssl = SSL_new(ctx);
SSL_set_fd(ssl, sockclient);
SSL_connect(ssl);
return 1;
}
//~~~~~~~~~~~~~~~~~~~~~~~上传文件~~~~~~~~~~~~~~~~~~~~~~~~~
void upload_file(char *filename)
{
int fd;
char buf[1024];
int count=0;
int size = strlen(filename);
char cmd = ‘U‘;
struct stat fstat;
if((fd=open(filename,O_RDONLY))==-1)
{
perror("open: ");
return;
}
/*发送上传命令*/
//write(sockclient,&cmd,1);
SSL_write(ssl,&cmd,1);
/*发送文件名*/
//write(sockclient,(void *)&size,4);
//write(sockclient,filename,size);
SSL_write(ssl,(void *)&size,4);
SSL_write(ssl,filename,size);
/*发送文件长度*/
if(stat(filename,&fstat)==-1)
return;
//write(sockclient,(void
*)&(fstat.st_size),4);
SSL_write(ssl,(void *)&(fstat.st_size),4);
/*发送文件内容*/
while((count=read(fd,(void *)buf,1024))>0)
{
//write(sockclient,&buf,count);
SSL_write(ssl,&buf,count);
}
close(fd);
}
//~~~~~~~~~~~~~~~~~~~~~~~下载文件~~~~~~~~~~~~~~~~~~~~~~~~~
void download_file(char *filename)
{
int fd;
char buf[1024];
int count=0;
int filesize = 0;
int tmpsize = 0;
int namesize = 0;
char cmd = ‘D‘;
int size = strlen(filename);
/*发送下载命令*/
//write(sockclient,(void
*)&cmd,1);
SSL_write(ssl,(void *)&cmd,1);
/*发送文件名*/
//write(sockclient,&size,4);
//write(sockclient,filename,size);
SSL_write(ssl,&size,4);
SSL_write(ssl,filename,size);
/*创建文件*/
if((fd=open(filename,O_RDWR|O_CREAT,0777))<0)
{
perror("open error:\n");
}
/*接收文件长度*/
//read(sockclient,&filesize,4);
SSL_read(ssl,&filesize,4);
while((count=SSL_read(ssl,(void *)buf,1024))>0)
{
write(fd,&buf,count);
tmpsize
+= count;
if(tmpsize==filesize)
break;
}
close(fd);
}
void quit()
{
char cmd = ‘Q‘;
//write(sockclient,(void
*)&cmd,1);
SSL_write(ssl,(void *)&cmd,1);
system("clear");
/*SSL退出*/
SSL_shutdown(ssl);
SSL_free(ssl);
close(sockclient);
SSL_CTX_free(ctx);
exit(0);
}
void menu()
{
char command;
char file_u[30];
char file_d[30];
char tmp;
char c;
while(1)
{
printf("\n------------------------------ 1.Upload Files ------------------------------\n");
printf("------------------------------ 2.Download Files ------------------------------\n");
printf("------------------------------ 3.Exit
------------------------------------\n");
printf("Please input the
Client command:");
while ((c=getchar()) != ‘\n‘ && c != EOF);
command=getchar();
switch(command)
{
case ‘1‘:
{
printf("Upload File:");
while ((c=getchar()) != ‘\n‘ && c != EOF);
fgets(file_u,30,stdin);
file_u[strlen(file_u)-1]=‘\0‘;
upload_file(file_u);
}
break;
case ‘2‘:
{
printf("Download Files:");
while ((c=getchar()) != ‘\n‘ && c != EOF);
fgets(file_d,sizeof(file_d),stdin);
file_d[strlen(file_d)-1]=‘\0‘;
download_file(file_d);
}
break;
case ‘3‘:
quit();
break;
default:
printf("Please input right
command\n");
break;
}
}
}
char passwd_d[10];
static int callback(void *NotUsed, int argc, char **argv, char **azColName)
{
int i;
for(i=0; i<argc; i++)
{
//printf("%s
= %s\n", azColName[i], argv[i] ? argv[i] : "NULL");
strcpy(passwd_d,argv[i]);
}
printf("\n");
return 0;
}
int login()
//因为有返回值,所以用int型
{
char username[10];
char passwd[10];
sqlite3 *db;
char sql[50];
int success;
//1. 通知用户输入用户名和密码
printf("User
name: ");
scanf("%s",username);
printf("Password:
");
scanf("%s",passwd);
//2. 根据用户名,从数据库提取正确的密码
sprintf(sql, "select
passwd from tb0 where name=‘%s‘;",username); //组合函数
sqlite3_open("user.db", &db);
sqlite3_exec(db, sql, callback, 0, NULL);
sqlite3_close(db);
//3. 比较用户输入的密码和数据库提取出的密码,以决定是否登录成功
success = strcmp(passwd,passwd_d);
return success;
}
int main(int argc,char *args[])
{
if(argc!=2)
{
printf("format error: you mast enter ipaddr
like this : client 192.168.0.6\n");
exit(0);
}
strcpy(ipaddr,args[1]);
if (login()!=0)
{
printf("wrong
username or password!\n");
exit(0);
}
/*SSL库初始化*/
SSL_library_init();
OpenSSL_add_all_algorithms();
SSL_load_error_strings();
ctx = SSL_CTX_new(SSLv23_client_method());
linkS();
menu();
return 0;
}
我们进入相应的文件夹输入下面的信息(不能有误):
arm-linux-gcc -I
/home/free/patrt5-2/openssl-1.0.0s/_install/include/
-I
/home/free/patrt5-2/sqlite-autoconf-3070800/_install/include/
-L
/home/free/patrt5-2/openssl-1.0.0s/_install/lib/
-L /home/free/patrt5-2/sqlite-autoconf-3070800/_install/lib/
client.c -o
client -lssl -lcrypto -lsqlite3
测试:
(1)我们将文件导入开发板:cp
client /home/free/patrt3/rootfs
(2)我们在linux系统中运行服务器:./server
(3)在开发板中首先创建表格:
创建:
./test_db
user.db “creat table tb0(name vachar(10),passwd varchar(10));”
插入:
./test_db
user.db “insert into tb0 values(‘free’,123);”
./test_db
user.db “insert into tb0 values(‘free2’,456);”
./test_db
user.db “insert into tb0 values(‘free’3,789);”
(4)运行客户端
./client
192.168.153.129
我们按照提示输入用户名字和密码后可以运行出以前的用户界面。
原文地址:https://www.cnblogs.com/free-1122/p/11365976.html