传输加密项目

阅读: 评论:0

传输加密项目

传输加密项目

1 使用场合

1.1 设计到网络数据传输的对数据传输安全有需求的,就需要对数据进行加密;加密就需要用到密钥,密钥协商服务就是用来生成密钥的,该密钥是对称密钥。
1.2 密钥协商服务总体流程说明:

  1. 客户端发起密钥协商的请求,发送给服务端的随机字符串abc;
  2. 服务端收到客户端的随机字符串之后,自己也生成一个随机字符串123,且将123发送给客户端;
  3. 服务端将abc123作为生成密钥的原材料,通过使用某种算法生成一个新的密钥;
  4. 客户端收到服务端发送的123之后,也进行组合abc123,也使用与服务端相同的算法,生成新的密钥(应该与服务端生成的密钥一样);
  5. 当客户端和服务端都生成密钥之后,可以通过密钥校验验证客户端和服务端生成的密钥是否一致;
  6. 客户端将密钥写入共享内存,服务端将密钥写入共享内存。

2 知识点归纳

2.1网络通信.
socket API函数: socket bind listen accept read | recv send write
三次握手和四次挥手
TCP协议
服务端开发:

服务端的基本开发流程:1 socket2 setsockopt3 bind4 listen5 while(1){	  	}
客户端的基本开发流程:1 socket2 connect3 while(1){write();read();}
	多进程多线程多路复用技术: select poll epoll多路IO复用与多线程或者多进程结合使用第三方库: libevent

2.2 报文编解码
2.3 进程间通信:pipe fifo mmap 本地套接字 网络套接字 共享内存
2.4 数据库操作相关知识点:oracle的occi库
2.5 QT相关
2.6 守护进程的创建步骤
2.7 信号相关知识点: SIGUSR1 SIGUSR2 signal sigaction
2.8 shell编程相关
2.9 加密算法相关
2.10 多线程开发
2.11 c++基础的概念:封装 继承 多态

2.12 子系统划分:

  1. 密钥协商客户端子系统
  2. 密钥协商服务端子系统
  3. 客户端信息注册报备图形化界面系统

2.13 模块划分:

  1. 报文编解码模块
  2. 网络通信模块
  3. 共享内存操作模块
  4. 数据库操作模块
  5. 外联接口

2.14 加密三要素
(密钥+加密算法)+明文==>密文
(密钥+解码算法)+密文==>明文
加密:
明文: 123456
算法: a+密钥
密钥是: 222222
密文: 345678

解密:
密文: 345678
算法: a-密钥
密钥是: 222222
明文: 123456

加密算法:
1 对称加密算法: DES 3DES AES
2 非对称加密: RSA
3 哈希算法: 哈希算法不是用来加密的, 因为不可逆, SHA1 SHA2 MD4 MD5 HMAC

2.15 UML类图
继承关系:使用带有空心箭头的实线来表示,箭头指向的是父类。
单向关联关系:使用带有箭头的实线表示,箭头指向类的成员对象
双向关联关系:使用带有双向箭头的实线来表示,两个类互相包含
自关联:最常见的就是链表操作,箭头指向类自己
聚合关系:使用带有空心菱形的带箭头的实线来表示,空心的菱形在聚合类一侧,箭头指向成员对象
组合关系:使用带有实心菱形的带箭头的实线来表示,空心的菱形在聚合类一侧,箭头指向成员对象
聚合和组合都说整体和部分的关系
依赖关系:类中的成员函数的参数用到了某个类的对象,使用带有箭头的虚线表示,箭头指向被依赖的类

2.16 报文编解码-ASN.1
ASN.1描述了一种对数据进行表示,编码,传输和解码的数据格式。
编码格式(TLV):

例如:name:xiaowu --> name6xiaowu

相关函数:

ITCAST_INT DER_ItAsn1_WriteInteger(ITCAST_UINT32 integer, ITASN1_INTEGER **ppDerInteger);
函数说明:对整形数据进行编码操作
函数参数:integer:输入输出,表示待编码的整形数据ppDerInteger:传输参数,编码之后的数据
返回值:成功或失败ITCAST_INT DER_ItAsn1_ReadInteger(ITASN1_INTEGER *pDerInteger, ITCAST_UINT32 *pInteger);
函数说明:对整形数据解码
参数说明:pDerInteger:传入参数,表示待解码的数据pInteger:传出参数,表示解码之后的数据
返回值:成功或失败ITCAST_INT DER_ItAsn1_WritePrintableString(ITASN1_PRINTABLESTRING *pPrintString, ITASN1_PRINTABLESTRING **ppDerPrintString);
函数说明:编码字符串数据
函数参数:pPrintString:输入参数,表示要编码的数据ppDerPrintString:输出参数,表示编码之后的数据
返回值:成功或失败ITCAST_INT DER_ItAsn1_ReadPrintableString(ITASN1_PRINTABLESTRING *pDerPrintString, ITASN1_PRINTABLESTRING **ppPrintString);
函数说明:解码函数,将ANYCAST_ANYBUF类型解码到第二个参数
参数说明:pDerPrintString:输出参数,表示待解码的数据ppPrintString:输出参数,存放解码之后的数据
返回值:成功或失败ITCAST_INT DER_ITCAST_String_To_AnyBuf(ITCAST_ANYBUF **pOriginBuf, unsigned char * strOrigin, int strOriginLen);
函数说明:将char *转换成ITCAST_ANYBUF类型
函数参数:pOriginBUf:传出参数,ITCAST_ANYBUF指针strOrigin:传出参数,待转换的字符串strOriginLen:传出参数,strOrigin的字符串长度
返回值:成功或失败int EncodeChar(char *pData, int dataLen, ITCAST_ANYBUF **outBuf);
函数说明:将char *类型数据进行编码
函数参数:pData:输入参数,指的是待编码的字符串dataLen:输入参数,指的是pData的长度outBuf:输出参数,ITCASTT_ANYBUF类型的数据,TLV格式
int DecodeChar(ITCAST_ANYBUF *inBuf, char **Data, int *pDataLen);ITCAST_INT DER_ItAsn1_WriteSequence(ITASN1_SEQUENCE *pSequence, ITCAST_ANYBUF **ppDerSequence);
函数说明:序列化链表,将链表序列化成字节流数据
函数参数:pSequence:输入参数,待序列化的数据ppDerSequence:输出参数,序列化之后的数据ITCAST_INT DER_ItAsn1_ReadSequence(ITCAST_ANYBUF *pDerSequence, ITASN1_SEQUENCE **ppSequence);
函数说明:反序列化
参数说明:pDerSequence:输入参数,开始需要将char *转换成ITCAST_ANYBUF类型ppSequence:输出参数,获得链表头节点ITCAST_INT DER_ITCAST_FreeQueue(ITCAST_ANYBUF *pAnyBuf);
释放内存

3 工厂模式

使用一个单独的类来做创建实例的过程。
使用工厂类的目的是让工厂类去创建类对象;好处:易于维护,易于扩充,且不用原有的代码进行修改。
使用步骤:

  1. 创建一个工厂类对象,父类的指针指向子类工厂对象;
  2. 使用父类指针指向的子类对象的createcode函数创建对象;
  3. 由第二步创建处理的对象由其父类指针指向;
  4. 调用编解码函数。

使用hash函数加密和解密:

#define _CRT_SECURE_NO_WARNINGS
#include <iostream>
#include <openssl/sha.h>
#include <stdio.h>
using namespace std;
int main() {char data[1024] = "xiaowu,hello world";int len = strlen(data);unsigned char md[SHA512_DIGEST_LENGTH] = {0};SHA512_CTX c;SHA512_Init(&c);SHA512_Update(&c,data,len);SHA512_Final(md,&c);char buf[SHA512_DIGEST_LENGTH*2+1] = {0};for (int i = 0;i < SHA512_DIGEST_LENGTH;i++) {sprintf(&buf[i*2],"%02x",md[i]);}cout << buf << endl;memset(md,0x00,sizeof(md));unsigned char data1[1024] = "xiaowu,hello world";SHA512(data1,len,md);memset(buf,0x00,sizeof(buf));for (int i = 0;i < SHA512_DIGEST_LENGTH;i++) {sprintf(&buf[i * 2], "%02x", md[i]);}cout << buf << endl;return 0;
}

4 知识点回顾

客户端的开发流程:

创建socket,得到一个通信的文件描述符cfdint socket(int domain, int type, int protocol);
绑定---不是必须的int bind(int sockfd, struct sockaddr *my_addr, socklen_t addrlen);
connect服务端int connect(int sockfd, const struct sockaddr *addr, socklen_t addrlen);注意:在给IP和端口赋值的时候需要进行主机字节序到网络字节序的转换--htonl htons
while(1){//发送数据ssize_t write(int fd, const void *buf, size_t count);int send(int s, const void *msg, size_t len, int flags);//接收数据ssize_t read(int fd, void *buf, size_t count);ssize_t recv(int sockfd, void *buf, size_t len, int flags);}
关闭连接int close(int fd);int shutdown(int sockfd, int how);

服务端开发流程:

创建socket,得到一个监听的文件描述符lfdint socket(int domain, int type, int protocol);
设置端口复用int setsockopt(int sockfd, int level, int optname, const void *optval, socklen_t optlen);
绑定--必须的int bind(int sockfd, struct sockaddr *my_addr, socklen_t addrlen);--INADDR_ANY: 本地任意可用IPIP+端口可以唯一确定网络中唯一一台主机的唯一一个服务
监听int listen(int s, int backlog);
调用accept函数接受新的客户端连接int accept(int s, struct sockaddr *addr, socklen_t *addrlen);--返回一个通信的文件描述符cfd--addr: 输出参数, 客户端的地址信息--addrlen: 输入输出参数, 输入: addr内存大小, 输出: 传输的地址大小
while(1){//接收数据ssize_t read(int fd, void *buf, size_t count);ssize_t recv(int sockfd, void *buf, size_t len, int flags);//发送数据ssize_t write(int fd, const void *buf, size_t count);int send(int s, const void *msg, size_t len, int flags);}
关闭套接字close(fd);

同步和异步:
同步:例如:客户端发送数据给服务端,发送完成之后,就read阻塞等待读数据,读数据不完成,则不进行后续操作
异步:例如:客户端发送数据给服务端,发送完成之后,没有等待read数据,而是处理后续操作。

阻塞和非阻塞:
阻塞:比如accept和read都是阻塞函数,条件不满足,就一直阻塞等待
非阻塞:将文件描述符设置为非阻塞,则read函数会立刻返回。

同步非阻塞:若客户端发送数据之后,read函数不阻塞(文件描述符设置为非阻塞)
同步阻塞:客户端发送数据之后,read数据,若对方不发送应答数据,就一直阻塞
异步阻塞:比如:select poll epoll,若没有事件发生,select或者epoll可以一直阻塞
异步非阻塞:比如:将epoll设置非阻塞,不管有没有事件发生都会立刻返回

长连接和短链接:
长连接:通常用于通信双方数据交换频繁的情况下 – 心跳包
短链接:通常用于通信双方数据交换完成后就断开连接

网络知识:
7层模型:物数网传会表应
TCP/IP四层模型
一个文件描述符对应两个内核缓冲区(读缓冲区和写缓冲区)
三次握手过程
四次挥手过程
TCP状态转换图:CLOSE SYN_SEND SYN_RCVD LISTEN ESTABLISHED FIN_WAIT_1 FIN_WAIT_2 TIME_WAIT CLOSE_WAIT LAST_WAIT
滑动窗口:主要作用是进行流量控制
TIME_WAIT处于主动关闭方

TCP服务端和客户端开发流程:
多进程版本的服务器
多线程版本的服务器
多路IO复用:select poll epoll
epoll反应堆、线程池(理解生产者和消费者模型)

通信效率:单位时间内客户端或者服务端接收或者发送数据的量

accept();while(1){read();}while(1){accept();read();}可以将文件描述符设置为非阻塞缺点: 处理起来比较麻烦改进: 使用多进程或者多线程多进程: 父进程负责accept接受新的客户端连接, 子进程负责收发数据.
多线程: 主线程负责accept接受新的客户端连接, 子线程父子处理通信.
比较: 最大的区别是多线程处理效率要比多进程快(多线程节省资源)
可以使用map来保存线程ID和文件描述符的对应关系.多路IO复用技术:在一个进程中让多个客户端同时请求服务.都是委托内核进行监控, 若有事件发生则内核会通知应用程序.select:void FD_CLR(int fd, fd_set *set);int  FD_ISSET(int fd, fd_set *set);void FD_SET(int fd, fd_set *set);void FD_ZERO(fd_set *set);int select(int nfds, fd_set *readfds, fd_set *writefds,fd_set *exceptfds, struct timeval *timeout);使用select开发服务端流程:1 先定义fd_set set;2 初始化set集合: FD_ZERO(&set);3 将lfd加入到set集合中: FD_SET(lfd, &set);4 while(1){nready = select();for(i=0; i<nfds+1; i++){//有新的客户端连接到来cfd = accept();FD_SET(cfd, readfds);//有客户端数据发来read();//若读失败或者对方关闭连接FD_CLR(cfd, readfds);}}
poll:int poll(struct pollfd *fds, nfds_t nfds, int timeout);与select比较, 没有本质的变化, 但是poll将输入和输出分离.
 epoll:使用步骤:1 创建一棵epoll树:int epoll_create(int size);2 将监听文件描述符上epoll树int epoll_ctl(int epfd, int op, int fd, struct epoll_event *event);event.data.fd = lfd;event.events = EPOLLIN;epoll_ctl(epfd, EPOLL_CTL_ADD, lfd, &event);3 while(1){//int epoll_wait(int epfd, struct epoll_event *events, int maxevents, int timeout);nready = epoll_wait(epfd, events, 1024, -1);if(nready<0 && errno==EINTR)//被信号打断{continue;}//有事件发生:1 有客户端连接到来  2 有客户端发送数据到来for(i=0; i<nready; i++){sockfd = event[i].data.fd;//1 有客户端连接到来if(sockfd==lfd && events.events==EPOLLIN){cfd = accept();event.data.fd = cfd;event.events = EPOLLIN;epoll_ctl(epfd, EPOLL_CTL_ADD, cfd, &event);}else//有客户端发送数据到来{n = read(sockfd, buf, sizeof(buf));if(n<=0){epoll_ctl(epfd, EPOLL_CTL_DEL, sockfd, NULL);}else{write(sockfd, buf, n);}}sleep(100);}}epoll和select比较:1 epoll可以突破1024的限制2 epoll精确的告诉应用程序有哪些文件描述符发生了变化, 而select需要进行判断.可能存在的问题: 若每一个连接上处理的时间比较长, 会导致后面的连接上发来的数据得不到及时处理.
解决方法: 可以让主线程处理epoll_wait, 让子线程处理每一个连接客户端:1 单进程--只处理一个连接while(1){write();//read();}2 使用多线程--多个线程使用同一个连接3 使用多线程---多个线程使用多个连接缺点: 如果频繁创建连接和销毁连接会有时间消耗.4 连接池+线程池连接池不能用于服务端, 因为连接是只有客户端发起连接请求之后才会有.连接池只用于客户端.for(i=0; i<num; i++){//创建num个连接fd[i] = socket();connect(fd, servaddr, sizeof(servaddr));//创建多个线程pthread_create();}连接池:1 有一个数据结构保存连接2 创建连接池操作---poolInit()3 获取连接的操作4 将连接放回的操作5 可以根据实际需要动态调整连接的数量6 销毁连接池

共享内存:共享内存实质是将内核的一块内存映射到进程中的内存,操作本地内存就相当于操作共享内存
创建步骤:

  1. 创建共享内存
  2. 关联共享内存
  3. 使用共享内存 – 读写共享内存
  4. 断开与共享内存的关联
  5. 删除共享内存
头文件:
#include <sys/ipc.h>
#include <sys/shm.h>int shmget(key_t key,size_t size,int shmflg);
函数说明:创建或打开一块共享内存区
参数说明:key:一个无符号整型值,唯一标识了一块共享内存size:创建共享内存大小shmflg:IPC_CREAT:创建共享内存 -创建文件并指定权限:IPC_CREAT|0664IPC_EXCL:只能和IPC_CREAT一起使用,若共享内存存在,则报错,errno=EEXITST
返回值:成功:返回共享内存的ID值失败:返回-1,并设置errno
用法:如果共享内存已经存在:key_t key = 0x1234;int shmID = shmget(key,0,0);如果共享内存不存在:int shmID = shmget(key,100,IPC_CREAT|IPC_EXCL|0755);if(shmID<0){if(errno == EEXIST){shmID = shmget(key,0,0);return 0;}}如果不知道到底存不存在:int shmID = shmget(key,100,IPC_CREAT|0755);void *shmat(int shmid,const void *shmaddr,int shmflg);
函数描述:连接共享内存
参数说明:shmid:shmget函数返回的共享内存的ID值shmaddr:传NULL,表示让内核分配地址shmflg:SHM_RDONLY:只能对共享内存进行读操作0:可读可写
返回值:成功:内存地址失败:(void *)-1int shmdt(const void *shmaddr);
函数描述:断开与共享内存的关联
参数说明:shmaddr :shmat返回的内存地址
返回值:成功:返回0失败:返回-1,并设置errnoint shmctl(int shmid,int cmd,struct shmid_ds *buf);
函数描述:设置或删除共享内存
参数说明:shmid:shmget函数返回的共享内存的ID值cmd:IPC_STAT:获得共享内存的状态信息IPC_SET:设置共享内存信息IPC_RMID:删除共享内存buf:若cmd为IPC_RMID则buf为NULL

共享内存代码:

shm_write.c
int main{//创建共享内存int shmid = shmget(x12345678,1024,IPC_CREATIPC_EXCL|0755);if(shmid<0){if(errno==EEXIST){shmid = shmget(0x12345678,0);}else{return -1;}}printf("shmget success shmid==[%d]n",shmid);//连接共享内存void *pAddr = shmat(shmid,NULL,0);if(pAddr==(void *)-1){return -1;}printf("pAddr==[%p]n",pAddr);//操作共享内存memcpy(pAddr,"hello world",strlen("hello world"));printf("按任意键n");getchar();//断开共享内存的关联shmdt(pAddr);printf("按任意键n");getchar();//删除共享内存shmctl(shmid,IPC_RMID,NULL);return 0;
}shm_read.c
int main(){//创建共享内存int shmid = shmget(0x123456781024,IPC_CREATJIPC_EXCL|0755);if(shmid<0){if(errno==EEXIST){shmid = shmget(0x12345678,,0);}else{return -1;}}printf("shmge tsuccess shmid==[%d]n",shmid);//连接共享内存void *pAddr = shmat(shmid,NULL,0);if(pAddr==(void *)-1){return -1;}printf("pAddr==[%p]n", pAddr);//操作共享内存char buf[64] = {0};memcpy(buf,pAddr,11);printf("buf==[%s]n", buf);//断开共享内存的关联printf("按任意键n");getchar();shmdt(pAddr);printf("按任意键n");getchar();//删除共享内存shmctl(shmid,IPC_RMID,NULL);return 0;
}当一个进程与共享内存进行关联之后, 关联引用计数会加1, 断开关联之后会减1.当删除共享内存的时候, 若共享内存的关联计数大于0,则不会被真正删除, 但是key值变成0了.

shm和mmap的区别:

  1. shm不需要磁盘文件,mmap需要磁盘文件
  2. shm效率高
  3. mmap操作的数据量比shm大
  4. shm内存位置在内核只有一块,mmap内存在用户区,每个进程都有各自的内存映射区
  5. shm和mmap的数据相比,mmap更安全,会通过映射的文件做备份
  6. 进程退出,共享内存依然存在,内存映射区就不存在了

ftok函数:

key_t ftok(const char *pathname,int proj_id);
函数说明:获取shmget所需的key值
参数说明:pathname是路径或文件名,必须存在,对文件的权限没有要求proj_id:只用到了一个字节,取值范围:0-255,也可以传递一字符 "a"pathname可以随便设置,对权限并没有要求,proj_id的取值范围是0-255.
若pathname指向的文件或目录被删除而且又重新创建,那么文件系统会赋予这个同名文件新的inode节点信息,这些进程调用ftok()都能正常返回,单键值key却不一定相同了

共享内存操作命令:

ipcs -a  打印当前系统中所有进程间通信方式的信息
ipcs -m  打印出使用共享内存进行进程间通信的信息ipcrm -M shmkey  移除用shmkey创建的共享内存段
ipcrm -m shmid   移除用shmid标识的共享内存段

生成随机字符串:

#include <iostream>
#include <string.h>
#include <stdlib.h>
#include <time.h>
using namespace std;
void getRandString(char *pRandBuf,int len){int flag;char buf[] = "~!@#$%^&*()+-=";for(int i=0;i<len-1;i++){flag = rand()%4;switch(flag){case 0:pRandBuf[i] = rand()%10 + '0';break;case 1:pRandBuf[i] = rand()%26 + 'A';break;case 2:pRandBuf[i] = rand()%26 + 'a';break;case 3:pRandBuf[i] = buf[rand()%strlen(buf)];break;default:break;}}
}int main(){srand(time(NULL));char sRandBuf[64];int len = sizeof(sRandBuf);while(1){getRandString(sRandBuf,len);cout<< sRandBuf<<endl;getchar();}return 0;
}

密钥协商系统处理流程
密钥协商整体流程:

  1. 客户端生成一个随机数r1,同时使用openssl中哈希函数对r1进行哈希运算,得到一个哈希值
  2. 将要发送的数据进行编码
  3. 发送数据给服务端
  4. 服务端收到请求数据之后,首先进行解码操作
  5. 服务端根据客户端ID和服务端ID查询数据库,校验客户端是否合法,如不合法直接拒绝服务
  6. 服务端校验r1消息认证码 – 首先使用与客户端相同的算法生成哈希值,然后将这个哈希值与接收到的哈希值作比较,如果不一样,则拒绝服务,如果一致则继续后续操作
  7. 服务端也生成随机数r2
  8. 服务端将r1和r2进行拼接,然后使用与客户端相同的哈希算法进行哈希运算,得到一个哈希值,这个哈希值就当作新的密钥seckey2
  9. 将密钥信息写入共享内存和数据库
  10. 服务端发送应答信息给客户端
  11. 客户端等待接收服务端的应答
  12. 对接收到的数据进行解码
  13. 判断rv的值,若rv为-1表示生成密钥失败
  14. 如果成功rv=0;获得服务端发来的随机字符串r2,将r2和r1进行拼接,然后进行哈希运算,得到一个新的密钥seckey1
  15. 客户端将密钥信息写入共享内存。

密钥校验:

  1. 客户端将密钥进行哈希运算,得到一个哈希值
  2. 将哈希值发送给服务端
  3. 服务端收到哈希值,并且自己也生成一个哈希值
  4. 将两个哈希值进行比较,相同则密钥协商成功,否则密钥协商失败

密钥注销:

  1. 将clientID,serverID,密钥ID发送给服务端
  2. 服务端收到请求之后,将共享内存中的密钥的状态修改为不可以状态,将数据库中的密钥的使用状态修改为不可以状态。

密钥协商客户端流程:

  1. 对请求结构体RequestMsg进行赋值,生成随机字符串r1,设置cmdType为0,clientID和serverID可以从配置文件或者是环境变量中获取;authCode是对r1进行哈希运算hmac算法得到一个值;
  2. 对请求结构体进行报文编码操作,得到一个字符串;
  3. 连接密钥协商服务器;
  4. 发送编码之后的字符串给密钥协商服务器;
  5. 等待接受服务端发来的应答数据inData;
  6. 解码服务端发来的应答数据到RespondMsg结构体变量rspMsg中;
  7. 根据rv的值判断密钥协商是否成功,为-1表示失败;
  8. 客户端使用r1和服务端发来的r2进行哈希运算,得到一个新的哈希值seckey1,这个值就当做生成的密钥;
  9. 将新的密钥信息写入共享内存;
    10.断开与服务端的网络连接。
//客户端流程
int ClientOperation::secKeyAgree()
{//准备请求数据 RequestMsg req;memset(&req, 0x00, sizeof(RequestMsg));//RequestCodec::NewOrUpdate枚举dType = RequestCodec::NewOrUpdate;//给clientID和serverID赋值strcpy(req.clientId, m_info.clinetID);strcpy(req.serverId, m_info.serverID);//生成随机字符串r1getRandString(sizeof(req.r1), req.r1);//使用hmac函数生成哈希值----消息认证码char key[64];unsigned int len;unsigned char md[SHA256_DIGEST_LENGTH];memset(key, 0x00, sizeof(key));sprintf(key, "@%s+%s@", req.serverId, req.clientId);HMAC(EVP_sha256(), key, strlen(key), (unsigned char *)req.r1, strlen(req.r1), md, &len);for (int i = 0; i < SHA256_DIGEST_LENGTH; i++){sprintf(&req.authCode[2 * i], "%02x", md[i]);}cout << "key:" << key << endl;cout << "r1:" << req.r1 << endl;cout << "authCode:" << req.authCode << endl;//将要发送的数据进行编码int dataLen;char *outData = NULL;CodecFactory *factory = new RequestFactory(&req);Codec *pCodec = factory->createCodec();pCodec->msgEncode(&outData, dataLen);delete factory;delete pCodec;//连接服务端tToHost(m_info.serverIP, m_info.serverPort);//发送请求数据给服务端m_socket.sendMsg(outData, dataLen);//等待接收服务端的应答char *inData;vMsg(&inData, dataLen);//将接收到的数据进行解码factory = new RespondFactory();pCodec = factory->createCodec();RespondMsg *pMsg = (RespondMsg *)pCodec->msgDecode(inData, dataLen);//判断服务端是否成功if (pMsg->rv == -1){cout << "秘钥协商失败" << endl;return -1;}else{cout << "秘钥协商成功" << endl;}//将服务端的r2和客户端的r1拼接生成秘钥char buf[1024];unsigned char md1[SHA_DIGEST_LENGTH];memset(md1, 0x00, sizeof(md1));char seckey[SHA_DIGEST_LENGTH*2+1];memset(buf, 0x00, sizeof(buf));memset(seckey, 0x00, sizeof(seckey));sprintf(buf, "%s%s", req.r1, pMsg->r2);SHA1((unsigned char *)buf, strlen((char *)buf), md1);for(int i=0; i<SHA_DIGEST_LENGTH; i++){ sprintf(&seckey[i*2], "%02x", md1[i]);}cout << "r1: " << req.r1 << endl;cout << "r2: " << pMsg->r2 << endl;cout << "r1和r2生成的秘钥: " << seckey << endl;//给秘钥结构体赋值NodeSHMInfo node;memset(&node, 0x00, sizeof(NodeSHMInfo));node.status = 0;strcpy(node.seckey, seckey);strcpy(node.clientID, m_info.clinetID);strcpy(node.serverID, m_info.serverID);node.seckeyID = pMsg->seckeyid;//将秘钥信息写入共享内存m_shm->shmWrite(&node);//关闭网络连接m_socket.disConnect();//释放资源delete factory;delete pCodec;return 0;
}
//服务端
//创建多线程进行监听
void ServerOperation::startWork()
{//连接,进行监听m_server.setListen(m_info.sPort);//创建线程pthread_t threadID;while (1) {if(m_stop==true){break;}//连接新的客户端m_client = m_server.acceptConn();pthread_create(&threadID, NULL, working, this);//设置子线程为分离属性pthread_detach(threadID);//std::map<pthread_t, TcpSocket*> m_listSocket;m_listSocket.insert(make_pair(threadID,m_client));}
}
void * working(void * arg)
{//接收数据//获得线程idpthread_t thread = pthread_self();ServerOperation* op = (ServerOperation *)arg;TcpSocket* socket = op->m_listSocket[thread]; //根据线程ID找clientchar* inData;int dataLen = -1;int recvMsg = socket->recvMsg(&inData,dataLen); //接收数据cout<<"recvMse:"<<recvMsg<<endl;//解码CodecFactory *factory = new RequestFactory();Codec* pCodec = factory->createCodec();RequestMsg* pMsg = (RequestMsg *)pCodec->msgDecode(inData,dataLen);delete factory;//判断clientID是否合法//判断客户端要请求什么服务char *outData;switch(pMsg->cmdType){//密钥协商case RequestCodec::NewOrUpdate:op->secKeyAgree(pMsg, &outData, dataLen);break;//密钥校验case RequestCodec::Check:op->secKeyCheck();break;//密钥注销case RequestCodec::Revoke:op->secKeyRevoke();break;//密钥查看case RequestCodec::View:op->secKeyView();break;default:break;}//发送数据给客户端socket->sendMsg(outData,dataLen);free(outData);socket->disConnect();delete socket;//pthread_t thread;//thread = pthread_self();auto it = op->m_listSocket.find(thread);op->ase(it);
}int ServerOperation::secKeyAgree(RequestMsg * reqMsg, char ** outData, int & outLen)
{//验证消息认证码char key[128];unsigned int len;unsigned char md[SHA256_DIGEST_LENGTH];char authCode[SHA256_DIGEST_LENGTH * 2 + 1] = { 0 };memset(key,0x00,sizeof(key));sprintf(key,"@%s+%s@",reqMsg->serverId,reqMsg->clientId);HMAC(EVP_sha256(),key,strlen(key),(unsigned char *)reqMsg->r1,strlen(reqMsg->r1),md,&len);//校验clientID的合法性bool bl = checkClientID(reqMsg->clientId);if(!bl){cout<<"客户端ID校验不通过"<<endl;return -1;}cout<<"客户端ID校验通过"<<endl;for (int i = 0;i < SHA256_DIGEST_LENGTH;i++) {sprintf(&authCode[2*i],"%02x",md[i]);}//将生成的消息认证码与客户端r1的消息认证码做对比if (strcmp(authCode,reqMsg->authCode)!=0) {cout << "消息认证码错误" << endl;return -1;}else{cout << "消息认证码成功" << endl;}//生成随机字符串RespondMsg rspMsg;memset(&rspMsg,0x00,sizeof(rspMsg));getRandString(sizeof(rspMsg.r2), rspMsg.r2);cout<<"r1:"<<reqMsg->r1<<endl;cout<<"r2:"<<rspMsg.r2<<endl;//将r2和r1拼接,生成密钥char buf[128];unsigned char md1[SHA_DIGEST_LENGTH];char seckey[SHA_DIGEST_LENGTH*2+1];memset(buf,0x00,sizeof(buf));memset(seckey, 0x00, sizeof(seckey));sprintf(buf,"%s%s",reqMsg->r1,rspMsg.r2);SHA1((unsigned char *)buf,strlen(buf),md1);for (int i = 0;i < SHA_DIGEST_LENGTH;i++) {sprintf(&seckey[i * 2], "%02x", md1[i]);}cout<<"r1和r2生成的密钥:"<<seckey<<endl;//给应答结构体赋值//获取密钥IDrspMsg.seckeyid = 1;rspMsg.rv = 0;strcpy(rspMsg.clientId,reqMsg->clientId);strcpy(rspMsg.serverId, m_info.serverID);//将要发送给客户端的应答结构体进行编码int dataLen;char* sendData = NULL;CodecFactory* factory = new RespondFactory(&rspMsg);Codec* pCodec = factory->createCodec();pCodec->msgEncode(&sendData,dataLen);cout<<"*******************"<<endl;//给输出参数赋值*outData = sendData;outLen = dataLen;//发送数据给客户端pthread_t thread = pthread_self();TcpSocket* socket = m_listSocket[thread];//写密钥信息到共享内存NodeSHMInfo node;memset(&node,0x00,sizeof(NodeSHMInfo));node.status = 0;strcpy(node.clientID,rspMsg.clientId);strcpy(node.serverID,m_info.serverID);strcpy(node.seckey,seckey);node.seckeyID = rspMsg.seckeyid;//将密钥写入共享内存m_shm->shmWrite(&node);return 0;
}

shell脚本:一些命令的集合,在脚本文件中可以有控制流程,如顺序,条件分支和循环等
脚本文件一般以.sh文件为拓展名,但不是必须的。用#做注释

执行shell脚本:首先要添加可执行权限chmod +x test.sh -> ./test.sh

shell脚本中的变量:
用户自定义变量:value="hello world"value1=123value1="123"value='123'使用""和''的区别:对于""中的变量会直接展开, 而对于''当做普通字符串对待例如: echo "$HOME"  和echo '$HOME'
环境变量:如HOME  PATH如何将普通变量设置为环境变量: export FILEPATH="xxxxx"在用户下设置了环境变量之后, 都可以在用户下的进程中通过调用getenv函数获得环境变量的值.位置变量:$0: 脚本文件的名字$1: 执行脚本的时候的第一个参数$2: 执行脚本的时候的第二个参数$3: 执行脚本的时候的第三个参数.....$#: 参数的个数$?: 命令的执行结果, 成功为0, 失败为非0值$$: shell脚本的PID$@: 表示全部的参数获取命令的值:date1=`date`date2=$(date)if语句if [ 判断语句 ]then处理语句处理语句elif [ 判断语句 ]then处理语句处理语句else处理语句处理语句fi循环控制:
for i in {1..100}
doxxxx
donefor file in `ls`
doecho $file
donefor file  b.
doecho $file
donei=0
sum=0
while [ i -le 10 ]
dosum=$[$sum+$i]i=$[$i+1]
doneawk用法:
ps -ef | grep itcast | awk '{print $2}'
使用awk查找出某个进程的PID:ps -ef | grep svrMain | grep -v grep | awk '{print $2}'

QT连接数据库:

int main(int argc, char *argv[])
{QCoreApplication a(argc, argv);//查看数据库驱动的名字//("QSQLITE", "QMYSQL", "QMYSQL3", "QODBC", "QODBC3", "QPSQL", "QPSQL7")qDebug() << QSqlDatabase::drivers();//加载驱动QSqlDatabase db = QSqlDatabase::addDatabase("QMYSQL");//连接数据库db.setHostName("localhost");db.setUserName("root");db.setPassword("000000");db.setDatabaseName("mysql");bool flag = db.open();qDebug()<<flag;if(!flag){qDebug()<<"连接失败";}else{qDebug()<<"连接成功";}//执行select查询QSqlQuery query;//第一种方法//("select * from dept");//第二种方法query.prepare("select * from dept");();QSqlRecord rec = d();qDebug()<<"查询结果字段总数:"<&unt();qDebug()<<"查询结果记录总数为:"<<query.size();()){qDebug()<<query.value("deptno").toInt()<<"  "<<query.value("dname").toString()<<"  "<<query.value("loc").toString()<<"  ";}//执行insert操作//第一种方法//("insert into dept values(11,'sale','shanxi')");//第二种方法//query.prepare("insert into dept values(21,'huanggua','jiangsu')");//();//第三种方法/*query.prepare("insert into dept values(?,?,?)");query.bindValue(0,31);query.bindValue(1,"xiaoj");query.bindValue(2,"dfg");();*///执行update操作//第一种方法//("update dept set dname='xxxxxx' where deptno=11");//第二种方法//query.prepare("update dept set dname='xxxxxx' where deptno=11");//();//第三种方法//query.prepare("update dept set dname='xxxxxx' where deptno=?");//query.bindValue(0,11);//();//执行delete操作//第一种方法//("delete from dept where deptno=11");//第二种方法//query.prepare("delete from dept where deptno=21");//();//第三种方法//query.prepare("delete from dept where deptno=?");//query.bindValue(0,11);//();//执行select查询//第一种方法//("select * from dept");//第二种方法query.prepare("select * from dept");();qDebug()<<"查询结果字段总数:"<&unt();qDebug()<<"查询结果记录总数为:"<<query.size();()){qDebug()<<query.value("deptno").toInt()<<"  "<<query.value("dname").toString()<<"  "<<query.value("loc").toString()<<"  ";}//事务处理//开启一个新的事务("start tranaction");//设置事务为手工提交("set autocommit=0");//插入一条记录("insert into dept values(11,'shitang','libu')");//回滚//("rollback");//提交("commit");db.close();();
}

json文件
QT操作json,json分为json对象和json数组

#include <QCoreApplication>
#include <QJsonDocument>
#include <QJsonArray>
#include <QJsonObject>
#include <QByteArray>
#include <QJsonValue>
#include <QFile>
#include <QDebug>
void writeJsonToFile_Arr(char *fileName){//将json数组写入磁盘文件//构造一个QJsonArray对象QJsonArray array;//给array对象添加值array.append(10);array.append("jinglong");array.append(true);//添加子数组到array中QJsonArray subArray;subArray.append("english");subArray.append("chinese");subArray.append("math");array.append(subArray);//添加对象到array中QJsonObject subObj;subObj.insert("mother","xiaoli");subObj.insert("father","xiaotong");subObj.insert("sister","damiao");array.append(subObj);//将QJsonObject转换为QJsonDocumentQJsonDocument jsonDoc(array);//将QJsonDocument对象转换为QByteArray对象QByteArray byteArray = Json();//文件操作 -- 将byteArray写入文件QFile file(fileName);//打开文件file.open(QIODevice::WriteOnly);//写文件file.write(byteArray);//关闭文件file.close();return;
}void writeJsonToFile_Obj(char *fileName){//将json对象写入磁盘文件//创建json对象QJsonObject json;//给json对象插入键值对//iterator insert(const QString &key,const QJsonValue &value)json.insert("name","xiaoqiu");json.insert("age",21);json.insert("sex","female");//插入子对象 "family":{"father":"longji",...}QJsonObject subJson;subJson.insert("father","longji");subJson.insert("mather","liwei");subJson.insert("sister","wangjin");json.insert("family",subJson);//插入json数组QJsonArray jsonArr;jsonArr.append("english");jsonArr.append("chinese");jsonArr.append("math");jsonArr.append("history");json.insert("course",jsonArr);//将QJsonObject转换为QJsonDocumentQJsonDocument jsonDoc(json);//将QJsonDocument对象转换为QByteArray对象QByteArray byteArray = Json();//文件操作 -- 将byteArray写入文件QFile file(fileName);//打开文件file.open(QIODevice::WriteOnly);//写文件file.write(byteArray);//关闭文件file.close();return;
}void readJsonFromFile(char *fileName){//构造QFile类对象QFile file;//设置要读的文件file.setFileName(fileName);//打开文件file.open(QIODevice::ReadOnly);//读文件QByteArray byteArray = adAll();//将QByteArray对象转换为QJsonDocument对象或数组QJsonDocument jsonDoc = QJsonDocument::fromJson(byteArray);//关闭文件file.close();//判断是数组还是对象if(jsonDoc.isObject()){QJsonObject jsonObj = jsonDoc.object();//获取对象中所有的key值QStringList keys = jsonObj.keys();for(int i=0;i<keys.size();i++){//获取每一个key值QString key = keys[i];//qDebug()<<key<<":";//根据key值获取value值QJsonValue jsonValue = jsonObj.value(key);//判断value值的类型if(jsonValue.isString()){qDebug()<<key<<":"<&String();}else if(jsonValue.isDouble()){qDebug()<<key<<":"<&Int();}else if(jsonValue.isBool()){qDebug()<<key<<":"<&Bool();}else if(jsonValue.isObject()){QJsonObject obj = Object();QStringList subKeys = obj.keys();qDebug()<<key<<":{";for(int k=0;k<subKeys.size();k++){QString subkey = subKeys[k];QJsonValue subJsonValue = obj.value(subkey);qDebug()<<"  "<&String();}qDebug()<<"}";}else if(jsonValue.isArray()){qDebug()<<key<<":[";QJsonArray arr = Array();for(int j = 0;j<arr.size();j++){QJsonValue va = arr[j];if(va.isString()){qDebug()<<"  "<&String();}}qDebug()<<"]";}}}else if(jsonDoc.isArray()){QJsonArray array = jsonDoc.array();for(int i=0;i<array.size();i++){QJsonValue value = array[i];if(value.isString()){qDebug()<&String();}else if(value.isDouble()){qDebug()<&Double();}else if(value.isBool()){qDebug()<&Bool();}else if(value.isArray()){qDebug()<<"[";QJsonArray subArray = Array();for(int j=0;j<subArray.size();j++){qDebug()<<"  "<<subArray[j].toString();}qDebug()<<"]";}else if(value.isObject()){qDebug()<<"{";QJsonObject subObj = Object();QStringList subKeys = subObj.keys();for(int k=0;k<subKeys.size();k++){QString subkey = subKeys[k];QJsonValue value = subObj[subkey];if(value.isString()){qDebug()<<"  "<&String();}}qDebug()<<"}";}}}
}int main(int argc, char *argv[])
{QCoreApplication a(argc, argv);readJsonFromFile("mytest.json");();
}

c++操作mysql数据库:

//头文件
#include <iostream>
#include <string>
#include <mysql/mysql.h>   
using namespace std;
//函数定义
// 执行sql语句, 包括增加、删除、更新数据
bool ExecuteSql(MYSQL m_mysql,const char * sql)
{//mysql_query与指定的连接标识符关联的服务器中的当前活动数据库发送一条查询if (mysql_query(&m_mysql, sql)){// 打错误log,这里直接显示到控制台cerr << "执行sql语句失败,错误信息为: " << mysql_error(&m_mysql) << endl;return false;}else{cout << "执行sql语句成功!" << endl;}return true;
}
//主函数中调用
int main()
{MYSQL mysql;    //一个数据库结构体MYSQL_RES* res; //一个结果集结构体MYSQL_ROW row;  //char** 二维数组,存放一条条记录//初始化数据库mysql_init(&mysql);//设置编码方式mysql_options(&mysql, MYSQL_SET_CHARSET_NAME, "gbk");//连接数据库//判断如果连接失败就输出连接失败。//注意你连接的账户名密码if (mysql_real_connect(&mysql, "localhost", "root", "000000", "qcx", 3306, NULL, 0) == NULL)printf("连接失败!n");//string st = "insert into dept values (15,'roujiamo','xian')";string del_st = "delete from dept where deptno=15";const char* sql1 = del_st.c_str();ExecuteSql(mysql, sql1);//查询数据mysql_query(&mysql, "select * from dept");//获取结果集res = mysql_store_result(&mysql);//显示数据//mysql_fetch_row检索结果集的下一行,当没有下一行时返回NULLwhile (row = mysql_fetch_row(res)){printf("%s", row[0]);                   printf("t%s", row[1]);printf("t%sn", row[2]);}//释放结果集mysql_free_result(res);//关闭数据库mysql_close(&mysql);  return 0;
}执行命令:g++ -o test test.cpp -Ldir_path -lmysqlclient

本文发布于:2024-02-02 22:48:37,感谢您对本站的认可!

本文链接:https://www.4u4v.net/it/170688531746958.html

版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系,我们将在24小时内删除。

标签:项目
留言与评论(共有 0 条评论)
   
验证码:

Copyright ©2019-2022 Comsenz Inc.Powered by ©

网站地图1 网站地图2 网站地图3 网站地图4 网站地图5 网站地图6 网站地图7 网站地图8 网站地图9 网站地图10 网站地图11 网站地图12 网站地图13 网站地图14 网站地图15 网站地图16 网站地图17 网站地图18 网站地图19 网站地图20 网站地图21 网站地图22/a> 网站地图23