zoukankan      html  css  js  c++  java
  • 安全传输平台项目——密钥协商共享内存-守护进程-脚本

    在学习安全传输平台项目总结了笔记,并分享出来。有问题请及时联系博主:Alliswell_WP,转载请注明出处。

    10-安全传输平台项目-第06天(密钥协商共享内存-守护进程-脚本)

    目录:
    一、复习
    二、密钥协商共享内存-守护进程-脚本
    1、共享内存操作函数接口设计
    2、共享内存操作函数实现领读
    3、生成密钥-组织密钥结构体信息
    4、客户端写密钥信息到共享内存
    5、服务器内存释放
    6、共享内存补充说明
    7、服务器守护进程创建
    8、守护进程管理脚本
    9、借助信号管理守护进程
    10、密钥校验流程分析

    一、复习

    1、客户端:keymngclient.c—负责业务逻辑;keymngclientop.c—负责功能实现

    2、服务器:keymngserver.c—负责业务逻辑;keymngserverop.c—负责功能实现

    密钥协商业务逻辑图

    二、安全传输平台项目——密钥协商共享内存-守护进程-脚本

    1、共享内存操作函数接口设计

    ----共享内存    函数接口。
        分析网点密钥信息 元素            --- struct shmNodeInfo{}                   
            shmNodeInfo{ seckey, clientID, serverID, seckeyid, status, time }

        模块组织关系:
            客户端:
                协商密钥成功,写 密钥节点信息 到 共享内存。 密钥校验, 读 取共享内存密钥信息。

            服务器:
                协商密钥成功,写 密钥节点信息 到 共享内存。 密钥校验, 读 取共享内存密钥信息。

        存储:   
            服务器:struct shmNodeInfo     shmInfo[N];
              1
              :
              N
            客户端:struct shmNodeInfo     shmInfo;

        初始化/创建共享内存
            int shmInit(int shmkey, int size, int *shmhdle);   int -- map(shmat) --->void *

        写网点信息共享内存
            int shmNode_write(int shmhdle, struct shmNodeInfo *pShmNode, int maxnode);

        读网点信息共享内存
            int shmNode_Read(int shmhdle, struct shmNodeInfo **pShmNode, char *clientID,char *serverID);

    2、共享内存操作函数实现领读

    》函数封装:shmget()--> myipc_shm.c --> keymng_shmop.c  --- keymng_shmop.h

    分析了keymng_shmop.h和keymng_shmop.c

    >keymng_shmop.h

    // keymng_shmop.h
    
    #ifndef _KEYMNG_SHMOP_H_
    #define _KEYMNG_SHMOP_H_
    
    #include <stdio.h>
    #include <stdlib.h>
    #include <string.h>
    
    
    #ifdef __cplusplus 
    extern "C" {
    #endif
    
    //将网点密钥信息写共享内存, 网点共享内存结构体
    typedef struct _NodeSHMInfo
    {    
        int             status;            //密钥状态 0-有效 1无效
        char            clientId[12];    //客户端id
        char            serverId[12];    //服务器端id    
        int                seckeyid;        //对称密钥id
        unsigned char    seckey[128];    //对称密钥 //hash1 hash256 md5
    }NodeSHMInfo;
    
    
    //int KeyMng_ShmInit(int keyid, int keysize, void *shmid )
    //打开共享内存 共享内存存在则使用 不存在则创建(此函数并不是初始化)
    int KeyMng_ShmInit(int key, int maxnodenum, int *shmhdl);
    
    int KeyMng_ShmWrite(int shmhdl, int maxnodenum, NodeSHMInfo *pNodeInfo);//pNodeInfo是传入参数
    
    int KeyMng_ShmRead(int shmhdl, char *clientId, char *serverId,  int maxnodenum, NodeSHMInfo *pNodeInfo);//pNodeInfo是传出参数
    
    
    #ifdef __cplusplus
    }
    #endif
    #endif
    keymng_shmop.h

    >keymng_shmop.c

    #include <unistd.h>
    #include <sys/types.h>
    #include <signal.h>
    #include <sys/wait.h>
    
    #include <stdlib.h>
    #include <stdio.h>
    #include <errno.h>
    #include <string.h>
    
    #include "keymnglog.h"
    #include "keymng_shmop.h"
    #include "myipc_shm.h"
    #include "keymngclientop.h"
    
    //查看共享内存是否存在
    //若 存在使用旧 
    //若 不存在创建
    int KeyMng_ShmInit(int key, int maxnodenum, int *shmhdl)
    {
        int                ret = 0;
        
        //打开共享内存
        ret = IPC_OpenShm(key, maxnodenum*sizeof(NodeSHMInfo), shmhdl);
        if (ret == MYIPC_NotEXISTErr)
        {
            printf("keymng监测到共享内存不存在 正在创建共享内存...
    ");
            ret = IPC_CreatShm(key, maxnodenum*sizeof(NodeSHMInfo), shmhdl);
            if (ret != 0)
            {
                printf("keymng创建共享内存 err:%d 
    ", ret);
                return ret;
            }
            else
            {
                void     *mapaddr = NULL;
                printf("keymng创建共享内存 ok...
    ");
                
                ret = IPC_MapShm(*shmhdl, (void **) &mapaddr);
                if (ret != 0)
                {
                    printf("fun IPC_MapShm() err:%d 清空共享内存失败
    ", ret);
                    return ret;
                }
                memset(mapaddr, 0, maxnodenum*sizeof(NodeSHMInfo));
                IPC_UnMapShm(mapaddr);
                printf("keymng清空共享内存ok
    ");
            }
        }
        else if (ret == 0)
        {
            printf("keymng监测到共享内存存在 使用旧的共享内存...
    ");
        }
        else 
        {
            printf("fun IPC_OpenShm() err:%d
    ", ret);
        }
        
        return ret;    
    }
    
    //写网点密钥 
    //若存在 则修改 
    //若不存在 则找一个空的位置写入
    int KeyMng_ShmWrite(int shmhdl, int maxnodenum, NodeSHMInfo *pNodeInfo)
    {
        int                ret = 0, i = 0;
        NodeSHMInfo      tmpNodeInfo;         //空结点
        NodeSHMInfo        *pNode = NULL;
        
        void             *mapaddr = NULL;
        
        memset(&tmpNodeInfo, 0, sizeof(NodeSHMInfo));
        //连接共享内存
        ret = IPC_MapShm(shmhdl, (void **) &mapaddr);
        if (ret != 0)
        {
            KeyMng_Log(__FILE__, __LINE__,KeyMngLevel[4], ret,"func IPC_MapShm() err");
            goto End;
        }
        
        //判断传入的网点密钥 是否已经 存在
        for (i=0; i<maxnodenum; i++)
        {
            pNode = mapaddr + sizeof(NodeSHMInfo)*i;
            
            if (strcmp(pNode->clientId, pNodeInfo->clientId) == 0 &&
                strcmp(pNode->serverId, pNodeInfo->serverId) == 0 )
            {
                KeyMng_Log(__FILE__, __LINE__,KeyMngLevel[3], ret,"系统检测到 共享内存中已经存在网点信息cliented:%s serverid%s", pNode->clientId, pNode->serverId);
                memcpy(pNode, pNodeInfo, sizeof(NodeSHMInfo));
                goto End;
            } 
        }    
        
        //若不存在
        for (i=0; i<maxnodenum; i++)
        {
            pNode = mapaddr + sizeof(NodeSHMInfo)*i;
            if (memcmp(&tmpNodeInfo, pNode, sizeof(NodeSHMInfo)) == 0 )
            {
                KeyMng_Log(__FILE__, __LINE__,KeyMngLevel[3], ret,"系统检测到 有一个空的位置 ");
                memcpy(pNode, pNodeInfo, sizeof(NodeSHMInfo));
                goto End;
            }
        }
        
        if (i == maxnodenum)
        {
            ret = 1111;
            KeyMng_Log(__FILE__, __LINE__,KeyMngLevel[4], ret,"系统检测到共享内存已满 ");
            goto End;
        }
        
    End:
        IPC_UnMapShm(mapaddr);
        return ret;
    }
    
    //根据clientid和serverid 去读网点信息
    int KeyMng_ShmRead(int shmhdl, char *clientId, char *serverId,  int maxnodenum, NodeSHMInfo *pNodeInfo)
    {
        int            ret = 0, i = 0;
        NodeSHMInfo      tmpNodeInfo; //空结点
        NodeSHMInfo        *pNode = NULL;
        
        void             *mapaddr = NULL;
        
        memset(&tmpNodeInfo, 0, sizeof(NodeSHMInfo));
        //连接共享内存
        ret = IPC_MapShm(shmhdl, (void **) &mapaddr);
        if (ret != 0)
        {
            KeyMng_Log(__FILE__, __LINE__,KeyMngLevel[4], ret,"func IPC_MapShm() err");
            goto End;
        }
        
        //遍历网点信息
        for (i=0; i<maxnodenum; i++)
        {
            pNode = mapaddr + sizeof(NodeSHMInfo)*i;
            
            if ( strcmp(pNode->clientId, clientId) == 0 &&
                strcmp(pNode->serverId, serverId) == 0 )
            {
                KeyMng_Log(__FILE__, __LINE__,KeyMngLevel[3], ret,"系统检测到 有一个空的位置 ");
                memcpy(pNodeInfo, pNode, sizeof(NodeSHMInfo));
                goto End;
            }
        }
        
        if (i == maxnodenum)
        {
            ret = 1111;
            KeyMng_Log(__FILE__, __LINE__,KeyMngLevel[4], ret,"系统检测到共享内存已满 ");
            goto End;
        }
        
    End:
        IPC_UnMapShm(mapaddr);
        return ret;
        
    }
    keymng_shmop.c

    》网点密钥信息结构体            --- struct shmNodeInfo{}                   
            shmNodeInfo{ seckey, clientID, serverID, seckeyid, status, time }

           clientid        serverid    status        seckeyid    time        seckey
            1111        0001        1        100        2017/7/2    a1b2c3
            2222        0001        1        101        2017/7/4    abc123
            3333        0001        1        102        2017/7/1    ab12c3
            4444        0001        1        105        2017/7/3    a1bc23

    》存共享内存分析图:

    3、生成密钥-组织密钥结构体信息

    》生成密钥:
        1)借助开源算法  md5  sha1  sha256
        2)  自定义算法:
            123    abc    a1b2c3 
            abc      abc    aabbcc

    将keymng_shmop.h放入./secmng/inc/目录下,keymng_shmop.c放入./secmng/src/目录下。

    >vi keymngclientop.c

    1)将头文件"keymng_shmop.h"包含

    2)在MngClient_InitInfo中调用KeyMng_ShmInit

    3)在MngClient_Agree中组织密钥结构体信息

    #include <stdio.h>
    #include <stdlib.h>
    #include <unistd.h>
    #include <string.h>
    
    #include "keymnglog.h"
    #include "poolsocket.h"
    #include "keymng_msg.h"
    #include "keymngclientop.h"
    #include "keymng_shmop.h"
    
    int MngClient_InitInfo(MngClient_Info *pCltInfo)
    {
        int ret = 0;
         
        strcpy(pCltInfo->clientId, "1111");
        strcpy(pCltInfo->AuthCode, "1111");
        strcpy(pCltInfo->serverId, "0001");
        strcpy(pCltInfo->serverip, "127.0.0.1");
        pCltInfo->serverport = 8001;
        
        pCltInfo->maxnode = 1;
        pCltInfo->shmkey = 0x0011;
        pCltInfo->shmhdl = 0;    
        
        ret = KeyMng_ShmInit(pCltInfo->shmkey, pCltInfo->maxnode, &pCltInfo->shmhdl);
        if (ret != 0) {
            printf("---------客户端创建/打开 共享内存失败-----
    ");
            KeyMng_Log(__FILE__, __LINE__, KeyMngLevel[4], ret, "KeyMng_ShmInit() err:%d", ret);
            return 0;
        }
        
        return 0;
    }
    
    int MngClient_Agree(MngClient_Info *pCltInfo)
    {
        int i = 0; 
        int ret = 0; 
        int time = 3;
        
        int connfd = -1;
        
        // 存放编码 TLV 完成的 req
        unsigned char    *msgKey_Req_Data = NULL;   
        int             msgKey_Req_DataLen = 0;
        
        // 存放编码 TLV 完成的 res
        unsigned char    *msgKey_Res_Data = NULL;
        int             msgKey_Res_DataLen = 0;
        
        MsgKey_Res         *pStruct_Res = NULL;
        int             iType = 0;
    
        // 初始化密钥请求结构体
        MsgKey_Req msgKey_req;
        
        msgKey_req.cmdType = KeyMng_NEWorUPDATE;
        strcpy(msgKey_req.clientId, pCltInfo->clientId);
        strcpy(msgKey_req.AuthCode, pCltInfo->AuthCode);
        strcpy(msgKey_req.serverId, pCltInfo->serverId);    
        
        // 产生随机数         c: abcdefg    s: abcdefg        aabbccddeeffgg    
        for (i = 0; i < 64; i++) {
            msgKey_req.r1[i] = 'a' + i;    
        }
        
        // 编码密钥请求 结构体 req
        ret = MsgEncode(&msgKey_req, ID_MsgKey_Req, &msgKey_Req_Data, &msgKey_Req_DataLen);
        if (ret != 0) {        
            KeyMng_Log(__FILE__, __LINE__, KeyMngLevel[4], ret, "func MsgEncode() err:%d", ret);
            goto END;    
        }
        
        // 初始化建立连接函数
        ret = sckClient_init();
        if (ret != 0) {
            KeyMng_Log(__FILE__, __LINE__, KeyMngLevel[4], ret, "func sckClient_init() err:%d", ret);
            goto END;    
        }
            
        // 创建连接。
        ret = sckClient_connect(pCltInfo->serverip, pCltInfo->serverport, time, &connfd);
        if (ret != 0) {
            KeyMng_Log(__FILE__, __LINE__, KeyMngLevel[4], ret, "func sckClient_connect() err:%d", ret);
            goto END;    
        }
            
        // 发送数据  TLV
        ret = sckClient_send(connfd, time, msgKey_Req_Data, msgKey_Req_DataLen);
        if (ret != 0) {
            KeyMng_Log(__FILE__, __LINE__, KeyMngLevel[4], ret, "func sckClient_send() err:%d", ret);
            goto END;    
        }
            
        // ---- 等待服务器回发数据
        
        // 接收数据
        ret = sckClient_rev(connfd, time, &msgKey_Res_Data, &msgKey_Res_DataLen);
        if (ret != 0) {
            KeyMng_Log(__FILE__, __LINE__, KeyMngLevel[4], ret, "func sckClient_rev() err:%d", ret);
            goto END;    
        }    
        
        // 解码密钥应答 结构体 res ---> rv r2
        ret = MsgDecode(msgKey_Res_Data, msgKey_Res_DataLen, (void **)&pStruct_Res, &iType);
        if (ret != 0) {
            KeyMng_Log(__FILE__, __LINE__, KeyMngLevel[4], ret, "func MsgDecode() err:%d", ret);
            goto END;    
        }    
        
        if (pStruct_Res->rv != 0) {
            ret    = -1;
            goto END;
        } else if (pStruct_Res->rv == 0) {
            printf("---当前生成的密钥编号为:%d
    ", pStruct_Res->seckeyid);    
        }
        
        // --组织密钥信息结构体    
        NodeSHMInfo nodeSHMInfo;
        
        // --利用 r1 r2 生成密钥
        for (i = 0; i < 64; i++) {
            nodeSHMInfo.seckey[2*i] = msgKey_req.r1[i];
            nodeSHMInfo.seckey[2*i+1] = pStruct_Res->r2[i];
        }
        
        nodeSHMInfo.status = 0;                  //0-有效  1无效
        strcpy(nodeSHMInfo.clientId, msgKey_req.clientId);
        strcpy(nodeSHMInfo.serverId, msgKey_req.serverId);
        nodeSHMInfo.seckeyid = pStruct_Res->seckeyid;
    
        // --写入共享内存。
        
    END:
        if (msgKey_Req_Data != NULL) 
            MsgMemFree((void **)&msgKey_Req_Data, 0);
        if (msgKey_Res_Data != NULL) 
            MsgMemFree((void **)&msgKey_Res_Data, 0);
        if (pStruct_Res != NULL) 
            MsgMemFree((void **)&pStruct_Res, iType);
        
        return ret;    
    }
    keymngclientop.c

    4、客户端写密钥信息到共享内存

    >vi keymngclientop.c

    在MngClient_Agree中调用写入共享内存的函数KeyMng_ShmWrite

    #include <stdio.h>
    #include <stdlib.h>
    #include <unistd.h>
    #include <string.h>
    
    #include "keymnglog.h"
    #include "poolsocket.h"
    #include "keymng_msg.h"
    #include "keymngclientop.h"
    #include "keymng_shmop.h"
    
    int MngClient_InitInfo(MngClient_Info *pCltInfo)
    {
        int ret = 0;
         
        strcpy(pCltInfo->clientId, "1111");
        strcpy(pCltInfo->AuthCode, "1111");
        strcpy(pCltInfo->serverId, "0001");
        strcpy(pCltInfo->serverip, "127.0.0.1");
        pCltInfo->serverport = 8001;
        
        pCltInfo->maxnode = 1;
        pCltInfo->shmkey = 0x0011;
        pCltInfo->shmhdl = 0;    
        
        ret = KeyMng_ShmInit(pCltInfo->shmkey, pCltInfo->maxnode, &pCltInfo->shmhdl);
        if (ret != 0) {
            printf("---------客户端创建/打开 共享内存失败-----
    ");
            KeyMng_Log(__FILE__, __LINE__, KeyMngLevel[4], ret, "KeyMng_ShmInit() err:%d", ret);
            return 0;
        }
        
        return 0;
    }
    
    int MngClient_Agree(MngClient_Info *pCltInfo)
    {
        int i = 0; 
        int ret = 0; 
        int time = 3;
        
        int connfd = -1;
        
        // 存放编码 TLV 完成的 req
        unsigned char    *msgKey_Req_Data = NULL;   
        int             msgKey_Req_DataLen = 0;
        
        // 存放编码 TLV 完成的 res
        unsigned char    *msgKey_Res_Data = NULL;
        int             msgKey_Res_DataLen = 0;
        
        MsgKey_Res         *pStruct_Res = NULL;
        int             iType = 0;
    
        // 初始化密钥请求结构体
        MsgKey_Req msgKey_req;
        
        msgKey_req.cmdType = KeyMng_NEWorUPDATE;
        strcpy(msgKey_req.clientId, pCltInfo->clientId);
        strcpy(msgKey_req.AuthCode, pCltInfo->AuthCode);
        strcpy(msgKey_req.serverId, pCltInfo->serverId);    
        
        // 产生随机数         c: abcdefg    s: abcdefg        aabbccddeeffgg    
        for (i = 0; i < 64; i++) {
            msgKey_req.r1[i] = 'a' + i;    
        }
        
        // 编码密钥请求 结构体 req
        ret = MsgEncode(&msgKey_req, ID_MsgKey_Req, &msgKey_Req_Data, &msgKey_Req_DataLen);
        if (ret != 0) {        
            KeyMng_Log(__FILE__, __LINE__, KeyMngLevel[4], ret, "func MsgEncode() err:%d", ret);
            goto END;    
        }
        
        // 初始化建立连接函数
        ret = sckClient_init();
        if (ret != 0) {
            KeyMng_Log(__FILE__, __LINE__, KeyMngLevel[4], ret, "func sckClient_init() err:%d", ret);
            goto END;    
        }
            
        // 创建连接。
        ret = sckClient_connect(pCltInfo->serverip, pCltInfo->serverport, time, &connfd);
        if (ret != 0) {
            KeyMng_Log(__FILE__, __LINE__, KeyMngLevel[4], ret, "func sckClient_connect() err:%d", ret);
            goto END;    
        }
            
        // 发送数据  TLV
        ret = sckClient_send(connfd, time, msgKey_Req_Data, msgKey_Req_DataLen);
        if (ret != 0) {
            KeyMng_Log(__FILE__, __LINE__, KeyMngLevel[4], ret, "func sckClient_send() err:%d", ret);
            goto END;    
        }
            
        // ---- 等待服务器回发数据
        
        // 接收数据
        ret = sckClient_rev(connfd, time, &msgKey_Res_Data, &msgKey_Res_DataLen);
        if (ret != 0) {
            KeyMng_Log(__FILE__, __LINE__, KeyMngLevel[4], ret, "func sckClient_rev() err:%d", ret);
            goto END;    
        }    
        
        // 解码密钥应答 结构体 res ---> rv r2
        ret = MsgDecode(msgKey_Res_Data, msgKey_Res_DataLen, (void **)&pStruct_Res, &iType);
        if (ret != 0) {
            KeyMng_Log(__FILE__, __LINE__, KeyMngLevel[4], ret, "func MsgDecode() err:%d", ret);
            goto END;    
        }    
        
        if (pStruct_Res->rv != 0) {
            ret    = -1;
            goto END;
        } else if (pStruct_Res->rv == 0) {
            printf("---当前生成的密钥编号为:%d
    ", pStruct_Res->seckeyid);    
        }
        
        // --组织密钥信息结构体    
        NodeSHMInfo nodeSHMInfo;
        
        // --利用 r1 r2 生成密钥
        for (i = 0; i < 64; i++) {
            nodeSHMInfo.seckey[2*i] = msgKey_req.r1[i];
            nodeSHMInfo.seckey[2*i+1] = pStruct_Res->r2[i];
        }
        
        nodeSHMInfo.status = 0;                  //0-有效  1无效
        strcpy(nodeSHMInfo.clientId, msgKey_req.clientId);
        strcpy(nodeSHMInfo.serverId, msgKey_req.serverId);
        nodeSHMInfo.seckeyid = pStruct_Res->seckeyid;
    
        // --写入共享内存。
        ret = KeyMng_ShmWrite(pCltInfo->shmhdl, pCltInfo->maxnode, &nodeSHMInfo);
        if (ret != 0) {
            KeyMng_Log(__FILE__, __LINE__, KeyMngLevel[4], ret, "KeyMng_ShmWrite() err:%d", ret);
            goto END;    
        }
        printf("--------------写共享内存完成------
    ");
        
    END:
        if (msgKey_Req_Data != NULL) 
            MsgMemFree((void **)&msgKey_Req_Data, 0);
        if (msgKey_Res_Data != NULL) 
            MsgMemFree((void **)&msgKey_Res_Data, 0);
        if (pStruct_Res != NULL) 
            MsgMemFree((void **)&pStruct_Res, iType);
        
        return ret;    
    }
    keymng_shmop.c

     >vi makefile

    .PHONY:clean all
    
    WORKDIR=.
    VPATH = ./src
    
    CC=gcc
    CFLGS= -Wall -g -I$(WORKDIR)/inc/
    LIBFLAG = -L$(HOME)/lib
    
    
    BIN = keymngclient  keymngserver 
    
    
    all:$(BIN)
    
    keymngclient:keymngclient.o  keymnglog.o  keymngclientop.o  myipc_shm.o keymng_shmop.o
        $(CC) $(LIBFLAG) -lpthread -litcastsocket -lmessagereal $^ -o $@ 
    
    #myipc_shm.o  keymng_shmop.o keymng_dbop.o         -lclntsh  -licdbapi
    keymngserver:keymngserver.o  keymngserverop.o  keymnglog.o  
        $(CC) $(LIBFLAG) $^ -o $@ -lpthread -litcastsocket -lmessagereal  
     
    #testdbapi:testdbapi.o  
    #    $(CC) $(LIBFLAG) $^ -o $@ -lpthread  -lclntsh  -licdbapi
            
    %.o:%.c
        $(CC) $(CFLGS) -c $< -o $@    
    
    clean:
        rm -f *.o $(BIN)
        
        
        
    makefile

    >make

    >./keymngclient

    打开另一个终端,执行>./keymngserver,分别查看终端执行情况。

    客户端情况:(输入:1):

    “Ctrl+c”结束进程后,>ipcs 查看共享内存的创建情况

    服务器端情况:

    》同理,将服务器添加共享内存:

    >vi keymngserverop.c

    #include <stdio.h>
    #include <stdlib.h>
    #include <string.h>
    #include <unistd.h>
    #include "keymngserverop.h"
    #include "keymng_msg.h"
    #include "keymnglog.h" 
    #include "keymng_shmop.h"
    
    static int    seckeyid = 100;
    
    int MngServer_InitInfo(MngServer_Info *svrInfo)
    {
        int ret = 0;
        strcpy(svrInfo->serverId, "0001");
        strcpy(svrInfo->dbuse, "SECMNG");
        strcpy(svrInfo->dbpasswd, "SECMNG");
        strcpy(svrInfo->dbsid, "orcl");
        svrInfo->dbpoolnum = 8;    
        strcpy(svrInfo->serverip, "127.0.0.1");
        svrInfo->serverport = 8001;
        svrInfo->maxnode = 10;
        svrInfo->shmkey = 0x0001;
        svrInfo->shmhdl = 0;
        
        ret = KeyMng_ShmInit(svrInfo->shmkey, svrInfo->maxnode, &svrInfo->shmhdl);
        if (ret != 0) {
            printf("---------服务器创建/打开 共享内存失败-----
    ");
            KeyMng_Log(__FILE__, __LINE__, KeyMngLevel[4], ret, "服务器 KeyMng_ShmInit() err:%d", ret);
            return ret;
        }
        
        return 0;    
    }
    
    int MngServer_Agree(MngServer_Info *svrInfo, MsgKey_Req *msgkeyReq, unsigned char **outData, int *datalen)
    {
        int ret = 0;
        int i = 0;
        MsgKey_Res msgKey_Res;
        
        NodeSHMInfo nodeSHMInfo;
        
        // --结合 r1 r2 生成密钥  ---> 成功、失败 rv
        
        if (strcmp(svrInfo->serverId, msgkeyReq->serverId) != 0) {
            KeyMng_Log(__FILE__, __LINE__, KeyMngLevel[4], ret, "客户端访问了错误的服务器");
            return -1;    
        }
        
        // 组织 应答结构体 res : rv r2 clientId serverId  seckeyid
        msgKey_Res.rv = 0;     //0 成功 1 失败。
        strcpy(msgKey_Res.clientId, msgkeyReq->clientId); 
        strcpy(msgKey_Res.serverId, msgkeyReq->serverId); 
        
        // 生成随机数 r2
        for (i = 0; i < 64; i++) {
            msgKey_Res.r2[i] = 'a' + i;            
        }    
        msgKey_Res.seckeyid = seckeyid++;
        
        // 组织密钥节点信息结构体
        for (i = 0; i < 64; i++) {
            nodeSHMInfo.seckey[2*i] = msgkeyReq->r1[i];
            nodeSHMInfo.seckey[2*i+1] = msgKey_Res.r2[i];
        }
        nodeSHMInfo.status = 0;  //0-有效 1无效
        strcpy(nodeSHMInfo.clientId, msgkeyReq->clientId);
        strcpy(nodeSHMInfo.serverId, msgkeyReq->serverId);
        nodeSHMInfo.seckeyid = msgKey_Res.seckeyid;
    
        // --写入共享内存。
        ret = KeyMng_ShmWrite(svrInfo->shmhdl, svrInfo->maxnode, &nodeSHMInfo);
        if (ret != 0) {
            KeyMng_Log(__FILE__, __LINE__, KeyMngLevel[4], ret, "服务器 KeyMng_ShmWrite() err:%d", ret);
            return ret;    
        }
    
        // --写数据库
    
        // 编码应答报文  传出
        ret = MsgEncode(&msgKey_Res, ID_MsgKey_Res, outData, datalen);
        if (ret != 0) {
            KeyMng_Log(__FILE__, __LINE__, KeyMngLevel[4], ret, "serverAgree MsgEncode() err:%d", ret);    
            return ret;
        }
        
        return 0;    
    }
    
    
    int MngServer_Check(MngServer_Info *svrInfo, MsgKey_Req *msgkeyReq, unsigned char **outData, int *datalen)
    {
        
        
        return 0;    
    }
    keymngserverop.c

    >vi makefile

    .PHONY:clean all
    
    WORKDIR=.
    VPATH = ./src
    
    CC=gcc
    CFLGS= -Wall -g -I$(WORKDIR)/inc/
    LIBFLAG = -L$(HOME)/lib
    
    
    BIN = keymngclient  keymngserver 
    
    
    all:$(BIN)
    
    keymngclient:keymngclient.o  keymnglog.o  keymngclientop.o  myipc_shm.o keymng_shmop.o
        $(CC) $(LIBFLAG) -lpthread -litcastsocket -lmessagereal $^ -o $@ 
    
    # keymng_dbop.o         -lclntsh  -licdbapi
    keymngserver:keymngserver.o  keymngserverop.o  keymnglog.o  myipc_shm.o  keymng_shmop.o 
        $(CC) $(LIBFLAG) $^ -o $@ -lpthread -litcastsocket -lmessagereal  
     
    #testdbapi:testdbapi.o  
    #    $(CC) $(LIBFLAG) $^ -o $@ -lpthread  -lclntsh  -licdbapi
            
    %.o:%.c
        $(CC) $(CFLGS) -c $< -o $@    
    
    clean:
        rm -f *.o $(BIN)
        
        
        
    makefile

    5、服务器内存释放

    >free(在该释放内存的地方加入 相应创建的函数中寻找释放内存的函数

    #include <stdio.h>
    #include <stdlib.h>
    #include <string.h>
    #include <unistd.h>
    #include <pthread.h>
     
    #include "poolsocket.h"  
    #include "keymngserverop.h"
    #include "keymng_msg.h"
    #include "keymnglog.h"  
    
    MngServer_Info serverInfo;
    
     
    void *start_routine(void * arg)
    {
         int ret;
         int timeout = 3;
         int connfd = (int)arg;
         
         unsigned char *out = NULL;
         int outlen = 0;
         
         MsgKey_Req *pStruct_req = NULL;
         int iType = 0;
         
         unsigned char *res_outData = NULL;
         int res_outDataLen = 0;
         
         while (1) {
                     
            //服务器端端接受报文
            ret = sckServer_rev(connfd, timeout, &out, &outlen); 
            if (ret == Sck_ErrPeerClosed) {
                // 检测到 对端关闭,关闭本端。
                printf("----------------ErrPeerClosed 关闭服务器
    ");
                break;
            } else if (ret == Sck_ErrTimeOut) {
                if (out != NULL)  sck_FreeMem((void **)&out);
                continue;
            } else if (ret != 0) {
                printf("未知错误
    ");
                break;
            }
    
            // 解码客户端 密钥请求报文 ---> cmdType
            ret = MsgDecode(out, outlen, (void **)&pStruct_req, &iType);
            if (ret != 0) {
                KeyMng_Log(__FILE__, __LINE__, KeyMngLevel[4], ret, "MsgDecode() err:%d", ret);    
                break;    
            }
    
            switch(pStruct_req->cmdType) {
                case KeyMng_NEWorUPDATE:
                    ret = MngServer_Agree(&serverInfo, pStruct_req, &res_outData, &res_outDataLen);
                
                case KeyMng_Check:
                    MngServer_Check(&serverInfo, pStruct_req, &res_outData, &res_outDataLen);
                /*    
                case 密钥注销:
                    mngServer_Agree();
                    */
                default:
                    break;
            }
            if (ret != 0) {        
                KeyMng_Log(__FILE__, __LINE__, KeyMngLevel[4], ret, "MngServer_Agree() err:%d", ret);
                break;                
            }
    
             //服务器端发送报文
            ret = sckServer_send(connfd, timeout, res_outData, res_outDataLen);
             if (ret == Sck_ErrPeerClosed) {
                // 检测到 对端关闭,关闭本端。
                printf("---ErrPeerClosed 
    ");
                break;
            } else if (ret == Sck_ErrTimeOut) {
                printf("---服务器检测到本端发送数据 超时 
    ");
                if (out != NULL) sck_FreeMem((void **)&out);
                if (pStruct_req != NULL) MsgMemFree((void **)&pStruct_req, iType);
                if (res_outData != NULL) MsgMemFree((void **)&res_outData, 0);    
                continue;
            } else if (ret != 0) {
                printf("未知错误
    ");
                break;
            }
        }
        
        if (out != NULL) sck_FreeMem((void **)&out);
        if (pStruct_req != NULL) MsgMemFree((void **)&pStruct_req, iType);
        if (res_outData != NULL) MsgMemFree((void **)&res_outData, 0);    
        
        sckServer_close(connfd);
        
         return NULL;
    }
    
    
    int main(void)
    {
        int listenfd;
        int ret = 0;
        
        int timeout = 3;
        int connfd = -1;
        
        pthread_t pid;
        
        
        // 服务器信息初始化。
        ret = MngServer_InitInfo(&serverInfo);
        if (ret != 0) {
            KeyMng_Log(__FILE__, __LINE__, KeyMngLevel[4], ret, "MngServer_InitInfo() err:%d", ret);    
            return ret;
        }
        
        //服务器端初始化
        ret = sckServer_init(serverInfo.serverport, &listenfd);
        if (ret != 0) {
            KeyMng_Log(__FILE__, __LINE__, KeyMngLevel[4], ret, "sckServer_init() err:%d", ret);    
            return ret;
        }
        
        while (1) {    
            
            ret = sckServer_accept(listenfd, timeout, &connfd);
            if (ret == Sck_ErrTimeOut){
                KeyMng_Log(__FILE__, __LINE__, KeyMngLevel[2], ret, "---等待客户端连接超时---");
                continue;    
            } else if(ret != 0)  {
                KeyMng_Log(__FILE__, __LINE__, KeyMngLevel[4], ret, "sckServer_accept() err:%d", ret);    
                return ret;
            }
            
            ret = pthread_create(&pid, NULL, start_routine, (void *)connfd);                    
        }
         
         //服务器端环境释放 
        sckServer_destroy();
        
    
        return 0;    
    }
    keymngserver.c

    >make

    >./keymngclient

    打开另一个终端,执行>./keymngserver,分别查看终端执行情况。

    客户端情况:(输入:1):

    服务器端情况:

    打开第三个终端,输入>ipcs,查看共享内存:

    6、共享内存补充说明

    问题:密钥协商业务逻辑图中两块共享内存是一块吗?有什么关系吗?

    分析:服务器和客户端是两个PC,两个共享内存是分别在不同的PC的内核区创建的,图中上边的共享内存(服务器的共享内存)用于app1和keymngserver进行通信,图中下边的的共享内存(客户端的共享内存)用于app2和keymngclient进行通信。而这两个共享内存没有任何关系!

    7、服务器守护进程创建

    问题:当>./keymngserver 运行时,当前终端,不能输入shell指令,完全被服务器进程占据?

    解决:守护进程。

    》守护进程创建:
        守护进程:
            运行于操作系统后台的服务进程。周期性的执行某个任务,或者等待某些事件发生。
            不占用控制终端。不随用户的注销而结束、退出。  通常以 d 结尾命名。  daemon
            位于一个新会话中。新的进程组。 脱离控制终端。

        1. fork 子进程, 父进程exit。
        2. 子进程 调用 setsid()创建新会话。 成为会长。进程组组长。 子进程 pid。
        3. 修改工作目录位置。防止可执行占用可卸载磁盘空间。   chdir();
        4. 设定 创建文件的权限 掩码 umask
        5. 关闭/重定向(dup2) 0/1/2 --> /dev/null  ---- /dev/zero
        6. 守护进程 循环逻辑。
        7. 守护进程管理。———— 脚本

    >vi keymngserver.c(添加守护进程CREATE_DAEMON

    #include <stdio.h>
    #include <stdlib.h>
    #include <string.h>
    #include <unistd.h>
    #include <pthread.h>
     
    #include "poolsocket.h"  
    #include "keymngserverop.h"
    #include "keymng_msg.h"
    #include "keymnglog.h"  
    
    MngServer_Info serverInfo;
    
    //注意定义宏的时候最后是一个整体,不能有空格!
    #define CREATE_DAEMON if(fork()>0)exit(1);setsid();
     
    void *start_routine(void * arg)
    {
         int ret;
         int timeout = 3;
         int connfd = (int)arg;
         
         unsigned char *out = NULL;
         int outlen = 0;
         
         MsgKey_Req *pStruct_req = NULL;
         int iType = 0;
         
         unsigned char *res_outData = NULL;
         int res_outDataLen = 0;
         
         while (1) {
                     
            //服务器端端接受报文
            ret = sckServer_rev(connfd, timeout, &out, &outlen); 
            if (ret == Sck_ErrPeerClosed) {
                // 检测到 对端关闭,关闭本端。
                printf("----------------ErrPeerClosed 关闭服务器
    ");
                break;
            } else if (ret == Sck_ErrTimeOut) {
                if (out != NULL)  sck_FreeMem((void **)&out);
                continue;
            } else if (ret != 0) {
                printf("未知错误
    ");
                break;
            }
    
            // 解码客户端 密钥请求报文 ---> cmdType
            ret = MsgDecode(out, outlen, (void **)&pStruct_req, &iType);
            if (ret != 0) {
                KeyMng_Log(__FILE__, __LINE__, KeyMngLevel[4], ret, "MsgDecode() err:%d", ret);    
                break;    
            }
    
            switch(pStruct_req->cmdType) {
                case KeyMng_NEWorUPDATE:
                    ret = MngServer_Agree(&serverInfo, pStruct_req, &res_outData, &res_outDataLen);
                
                case KeyMng_Check:
                    MngServer_Check(&serverInfo, pStruct_req, &res_outData, &res_outDataLen);
                /*    
                case 密钥注销:
                    mngServer_Agree();
                    */
                default:
                    break;
            }
            if (ret != 0) {        
                KeyMng_Log(__FILE__, __LINE__, KeyMngLevel[4], ret, "MngServer_Agree() err:%d", ret);
                break;                
            }
    
             //服务器端发送报文
            ret = sckServer_send(connfd, timeout, res_outData, res_outDataLen);
             if (ret == Sck_ErrPeerClosed) {
                // 检测到 对端关闭,关闭本端。
                printf("---ErrPeerClosed 
    ");
                break;
            } else if (ret == Sck_ErrTimeOut) {
                printf("---服务器检测到本端发送数据 超时 
    ");
                if (out != NULL) sck_FreeMem((void **)&out);
                if (pStruct_req != NULL) MsgMemFree((void **)&pStruct_req, iType);
                if (res_outData != NULL) MsgMemFree((void **)&res_outData, 0);    
                continue;
            } else if (ret != 0) {
                printf("未知错误
    ");
                break;
            }
        }
        
        if (out != NULL) sck_FreeMem((void **)&out);
        if (pStruct_req != NULL) MsgMemFree((void **)&pStruct_req, iType);
        if (res_outData != NULL) MsgMemFree((void **)&res_outData, 0);    
        
        sckServer_close(connfd);
        
         return NULL;
    }
    
    
    int main(void)
    {
        int listenfd;
        int ret = 0;
        
        int timeout = 3;
        int connfd = -1;
        
        pthread_t pid;
        
        CREATE_DAEMON
        
        
        // 服务器信息初始化。
        ret = MngServer_InitInfo(&serverInfo);
        if (ret != 0) {
            KeyMng_Log(__FILE__, __LINE__, KeyMngLevel[4], ret, "MngServer_InitInfo() err:%d", ret);    
            return ret;
        }
        
        //服务器端初始化
        ret = sckServer_init(serverInfo.serverport, &listenfd);
        if (ret != 0) {
            KeyMng_Log(__FILE__, __LINE__, KeyMngLevel[4], ret, "sckServer_init() err:%d", ret);    
            return ret;
        }
        
        while (1) {    
            
            ret = sckServer_accept(listenfd, timeout, &connfd);
            if (ret == Sck_ErrTimeOut){
                KeyMng_Log(__FILE__, __LINE__, KeyMngLevel[2], ret, "---等待客户端连接超时---");
                continue;    
            } else if(ret != 0)  {
                KeyMng_Log(__FILE__, __LINE__, KeyMngLevel[4], ret, "sckServer_accept() err:%d", ret);    
                return ret;
            }
            
            ret = pthread_create(&pid, NULL, start_routine, (void *)connfd);                    
        }
         
         //服务器端环境释放 
        sckServer_destroy();
        
    
        return 0;    
    }
    keymngserver.c

    >make

    >./keymngserver

    8、守护进程管理脚本

    问题抛出:当服务器进程变为守护进程后,无法使用Ctrl+c 结束服务器进程,只能通过>ps aux | grep keymngserver 查到pid,然后 kill -9 pid。但是用户怎么办?用户无法结束服务器进程?

    所以,采用shell脚本。

    》shell脚本:
        集成 一系列 shell命令。组织到一个文件中。统一运行。
        第一行: 指定解析器。  #!/bin/bash   /bin/shell
        awk 处理列
        sed 处理行

    在UltraEdit中编辑文件:

    >touch myshell

    #! /bin/bash
    ls -l
    date
    echo $HOME

    >ls -l myshell

    >chmod a+x myshell

    但是运行脚本,会报错:

    分析:错误由于wind和Linux换行不一致造成的:
        windows                 
        ---
        Linux                  --> ^M    
    解决:
        vim        --> :set ff=unix(fileformat可以简写为ff)

    再次执行>./myshell

    但是,在myshell新增加行,设置就失效了,

    法一:把set ff=unix写到vim的配置文件vimrc中;

    法二:在Linux中写脚本

    如何提取pid?如何提取服务器进程?-u指定用户,awk指定列,$1指定第一列

    >ps -u test04 | grep keymngserver | awk '{print $1}'

    》获取 命令结果:(两种方式,常用第二种
        src=$(whoami)
        src=`whoami`  --> “反引号”

    >vi myshell

    #! /bin/bash
    
    userN=`whoami`
    
    mypid=`ps -u ${userN} | grep keymngserver | awk '{print $1}'`
    
    if [ -z ${mypid} ];then
        echo "The process is not started."
        exit 1;
    fi
    
    kill -9 ${mypid}
    echo "kill keymngserver successful"

    如果启动服务器>./keymngserver,执行>./myshell

    9、借助信号管理守护进程

    问题抛出:当服务器进程当运行在某处时,执行>./myshell,服务器进程终止,导致可能服务器正和客户端协商,终止了,导致共享内存,堆空间都消失了;服务器里边某些内存没释放,某些进程没有正常终止?

    查看kill -l 中这几个信号:

    >man sigaction(设置信号捕捉)

    >man signal

    》加入信号捕捉函数:signal(SIGUSR1, catchSignal);

    >vi keymngserver.c

    #include <stdio.h>
    #include <stdlib.h>
    #include <string.h>
    #include <unistd.h>
    #include <pthread.h>
    #include <signal.h>
     
    #include "poolsocket.h"  
    #include "keymngserverop.h"
    #include "keymng_msg.h"
    #include "keymnglog.h"  
    
    MngServer_Info serverInfo;
    
    int flg = 1;
    
    //注意定义宏的时候最后是一个整体,不能有空格!
    #define CREATE_DAEMON if(fork()>0)exit(1);setsid();
     
    void *start_routine(void * arg)
    {
         int ret;
         int timeout = 3;
         int connfd = (int)arg;
         
         unsigned char *out = NULL;
         int outlen = 0;
         
         MsgKey_Req *pStruct_req = NULL;
         int iType = 0;
         
         unsigned char *res_outData = NULL;
         int res_outDataLen = 0;
         
         while (1) {
                     
            if (flg == 0) 
                break;    
    
            //服务器端端接受报文
            ret = sckServer_rev(connfd, timeout, &out, &outlen); 
            if (ret == Sck_ErrPeerClosed) {
                // 检测到 对端关闭,关闭本端。
                printf("----------------ErrPeerClosed 关闭服务器
    ");
                break;
            } else if (ret == Sck_ErrTimeOut) {
                if (out != NULL)  sck_FreeMem((void **)&out);
                continue;
            } else if (ret != 0) {
                printf("未知错误
    ");
                break;
            }
    
            // 解码客户端 密钥请求报文 ---> cmdType
            ret = MsgDecode(out, outlen, (void **)&pStruct_req, &iType);
            if (ret != 0) {
                KeyMng_Log(__FILE__, __LINE__, KeyMngLevel[4], ret, "MsgDecode() err:%d", ret);    
                break;    
            }
    
            switch(pStruct_req->cmdType) {
                case KeyMng_NEWorUPDATE:
                    ret = MngServer_Agree(&serverInfo, pStruct_req, &res_outData, &res_outDataLen);
                
                case KeyMng_Check:
                    MngServer_Check(&serverInfo, pStruct_req, &res_outData, &res_outDataLen);
                /*    
                case 密钥注销:
                    mngServer_Agree();
                    */
                default:
                    break;
            }
            if (ret != 0) {        
                KeyMng_Log(__FILE__, __LINE__, KeyMngLevel[4], ret, "MngServer_Agree() err:%d", ret);
                break;                
            }
    
             //服务器端发送报文
            ret = sckServer_send(connfd, timeout, res_outData, res_outDataLen);
             if (ret == Sck_ErrPeerClosed) {
                // 检测到 对端关闭,关闭本端。
                printf("---ErrPeerClosed 
    ");
                break;
            } else if (ret == Sck_ErrTimeOut) {
                printf("---服务器检测到本端发送数据 超时 
    ");
                if (out != NULL) sck_FreeMem((void **)&out);
                if (pStruct_req != NULL) MsgMemFree((void **)&pStruct_req, iType);
                if (res_outData != NULL) MsgMemFree((void **)&res_outData, 0);    
                continue;
            } else if (ret != 0) {
                printf("未知错误
    ");
                break;
            }
        }
        
        if (out != NULL) sck_FreeMem((void **)&out);
        if (pStruct_req != NULL) MsgMemFree((void **)&pStruct_req, iType);
        if (res_outData != NULL) MsgMemFree((void **)&res_outData, 0);    
        
        sckServer_close(connfd);
        
         return NULL;
    }
    
    void catchSignal(int signum)
    {
        flg = 0;
        printf(" catch signal %d, process is going to die.
    ", signum);
        
        return ;
    }
    
    int main(void)
    {
        int listenfd;
        int ret = 0;
        
        int timeout = 3;
        int connfd = -1;
        
        pthread_t pid;
        
        CREATE_DAEMON
        
        signal(SIGUSR1, catchSignal);
        
        // 服务器信息初始化。
        ret = MngServer_InitInfo(&serverInfo);
        if (ret != 0) {
            KeyMng_Log(__FILE__, __LINE__, KeyMngLevel[4], ret, "MngServer_InitInfo() err:%d", ret);    
            return ret;
        }
        
        //服务器端初始化
        ret = sckServer_init(serverInfo.serverport, &listenfd);
        if (ret != 0) {
            KeyMng_Log(__FILE__, __LINE__, KeyMngLevel[4], ret, "sckServer_init() err:%d", ret);    
            return ret;
        }
        
        while (1) {    
            if (flg == 0) 
                break;
            
            ret = sckServer_accept(listenfd, timeout, &connfd);
            if (ret == Sck_ErrTimeOut){
                KeyMng_Log(__FILE__, __LINE__, KeyMngLevel[2], ret, "---等待客户端连接超时---");
                continue;    
            } else if(ret != 0)  {
                KeyMng_Log(__FILE__, __LINE__, KeyMngLevel[4], ret, "sckServer_accept() err:%d", ret);    
                return ret;
            }
            
            ret = pthread_create(&pid, NULL, start_routine, (void *)connfd);                    
        }
         
         //服务器端环境释放 
        sckServer_destroy();
        
        printf("服务器 优雅退出。
    ");
    
        return 0;    
    }
    keymngserver.c

    >vi myshell(更改为发送10号信号

    #! /bin/bash
    
    userN=`whoami`
    
    mypid=`ps -u ${userN} | grep keymngserver | awk '{print $1}'`
    
    if [ -z ${mypid} ];then
        echo "The process is not started."
        exit 1;
    fi
    
    kill -10 ${mypid}
    echo "kill keymngserver successful"
    myshell

    >make

    >./keymngserver

    打开另一个终端,执行>./myshell,

    查看服务器端:

    10、密钥校验流程分析

    密钥校验:

    》服务器:

        int MngServer_check()
        {
    
            //读出 密钥请求结构体中 r1[] (10字节的密钥数据)
    
            //依据 clientid、serverid 读共享内存 --> seckey 密钥 --> 提取前10字节 --> 比较
            
            //根据比较结果填充 res.rv
    
            //组织密钥应答结构体 res 其他成员变量
    
            //编码密钥应答结构体 --> 传出。
        }

    》客户端:

        校验方法:
            1. a1b2c3 --> 再加密(非对称加密)--> jqk678
            2. 片段校验法: 0-10 密钥信息一致  --> 一致。

        int MngServer_check()
        {
    
            //读共享内存 --> seckey 密钥 --> 提取前10字节 --> req.r1[];
    
            //组织密钥请求结构体(校验的事)
    
            //编码密钥请求结构体 req
    
            //初始化连接 --> listenfd
    
            //建立连接 --> connfd
    
            //发送请求报文 -->  TLV send
    
            //接收应答报文 --> fwq TLV res
    
            //解析应答报文 --> TLV --> Struct --> rv --> 0 一致 1 不一致。
    
            //给用户返回校验结果。    
        }

    在学习安全传输平台项目总结了笔记,并分享出来。有问题请及时联系博主:Alliswell_WP,转载请注明出处。

  • 相关阅读:
    Python字典为什么快?为什么是乱序?
    对Routers的理解
    对drf视图集的理解
    对drf视图的理解
    C++中的随机数函数 分类: C/C++ 2015-07-27 12:24 10人阅读 评论(0) 收藏
    windows bat脚本编写 2015-07-27 11:27 5人阅读 评论(0) 收藏
    windows下bat批处理文件语法 2015-07-27 11:25 8人阅读 评论(0) 收藏
    shell版俄罗斯方块二:界面绘制 分类: shell版俄罗斯方块 2015-07-27 09:14 116人阅读 评论(0) 收藏
    shell版俄罗斯方块一:方向键获取 分类: shell版俄罗斯方块 2015-07-27 09:00 114人阅读 评论(0) 收藏
    echo的高级用法-颜色输出与光标定位 分类: Ubuntu学习笔记 2015-07-26 22:54 17人阅读 评论(0) 收藏
  • 原文地址:https://www.cnblogs.com/Alliswell-WP/p/CPlusPlus_SecureTransmissionPlatform_Project06.html
Copyright © 2011-2022 走看看