• 安全传输平台项目——统一通信组件-统一共享内存组件


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

    10-安全传输平台项目-第04天(统一通信组件-统一共享内存组件)

    目录:
    一、复习
    二、安全传输平台项目—统一通信组件
    1、客户端连接服务器
    2、客户端连接池连接服务器
    3、线程传参现象展示
    4、线程传参内存冗余法
    三、安全传输平台项目—统一共享内存组件
    1、常见IPC
    2、简单内存模型分析
    3、共享内存操作函数—shmget
    4、共享内存操作函数—shmat(shmdt了解)
    5、共享内存操作函数—shmctl
    6、Linux内核管理共享内存方法
    7、共享内存操作函数接口
    四、安全传输平台项目—密钥协商服务程序
    1、客户端服务器密钥协商预说明

    一、复习

    1、wind下制作动态库
    2、linux下制作动态库
    3、makefile复习
    4、统一通信组件—socket通信
    5、统一通信组件-服务器端实现

    >增加释放内存—free

    #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 <pthread.h>
    #include "poolsocket.h"
    
    
    void *mystart_routine(void *arg)
    {
        int         ret = 0;
        int         timeout = 3;
        int         connfd = (int)arg;
        
        unsigned char     *out = NULL;
        int             outlen = 0;
        
        while (1)
        {    
            //服务器端端接受报文
            ret =  sckServer_rev(connfd, timeout, &out, &outlen); //1
            if (ret == Sck_ErrPeerClosed)
            {
                //printf("aaaaa 
    ");
                printf("服务器端检测到客户端有一条连接已关闭 
    ");
                break;
            }
            else if (ret == Sck_ErrTimeOut)
            {
                printf("服务器端send超时
    ");
                continue;
            }
            else if (ret != 0)
            {
                printf("服务器端 sckServer_send() err
    ");
                break;
            }
        
            printf("out:%s 
    ", out);  //回射
            
            //服务器端发送报文
            ret =  sckServer_send(connfd, timeout, out, outlen);
            if (ret == Sck_ErrPeerClosed)
            {
                sck_FreeMem((void **)&out);
                printf("服务器端检测到客户端有一条连接已关闭
    ");
                break;
            }
            else if (ret == Sck_ErrTimeOut)
            {
                sck_FreeMem((void **)&out);
                printf("服务器端send超时
    ");
                continue;
            }
            else if (ret != 0)
            {
                sck_FreeMem((void **)&out);
                printf("服务器端 sckServer_send() err
    ");
                break;
            }
            sck_FreeMem((void **)&out);
        }
        
        sckServer_close(connfd);
        return NULL;
    }
    
    
    int main()
    {
        int         ret         = 0;
        int             port         = 8001;
        int         listenfd     = 0;
        int         timeout        = 3;
        int         connfd = 0;
        pthread_t     pid;
        
        
        //函数声明
        //服务器端初始化
        ret = sckServer_init(port, &listenfd);
        if (ret != 0)
        {
            printf("func sckServer_init() err:%d 
    ", ret);
            return ret;
        }
        
        while (1)
        {
            ret = sckServer_accept(listenfd, timeout, &connfd);
            if (ret == Sck_ErrTimeOut)
            {
                printf("func sckServer_accept() Sck_ErrTimeOut
    ");
                continue;
            }
            else if (ret != 0)
            {
                ret = 2;
                printf("fun sckServer_accept() err :%d 
    ", ret);
                break;
            }
        
            pthread_create(&pid, NULL,  mystart_routine, (void *)(connfd));
    
        }
    
    
        //服务器端环境释放 
        int sckServer_destroy();
        printf("hello....
    ");    
    }
    a_server.c

    二、安全传输平台项目—统一通信组件

    1、客户端连接服务器

    》编写client.c

    #include <stdio.h>
    #include <stdlib.h>
    #include <string.h>
    #include <unistd.h>
    #include <pthread.h>
     
    #include "poolsocket.h"  
    
    int main(void)
    {
        char *ip = "127.0.0.1"; 
        int port = 8080;
        int time = 3; 
        int connfd = -1;
        int ret = -1;
        
        unsigned char *data = "abcdefg";
        int datalen = 5;
        
        unsigned char *out = NULL;
        int outlen = -1;
        
        //客户端 初始化
        ret = sckClient_init();
        if (ret != 0) {
            printf("sckClient_init error %d
    ", ret);    
            return ret;
        }
        
        while (1) {
        
            //客户端 连接服务器
            ret = sckClient_connect(ip, port, time, &connfd);
             if (ret == Sck_ErrTimeOut) {
                printf("---客户端连接服务器 超时 
    ");
                continue;
            } else if (ret != 0) {
                printf("客户端连接服务器 失败: errorNO:%d
    ", ret);
                break;
            }
                
            //客户端 发送报文
            ret = sckClient_send(connfd, time, data, datalen);
            if (ret == Sck_ErrPeerClosed) {
                printf("---服务器关闭,客户端断开连接 
    ");
                break;
            } else if (ret == Sck_ErrTimeOut) {
                printf("---服务器接收数据 超时 
    ");
                continue;
            } else if (ret != 0) {
                printf("客户端发送数据失败:errorNO:%d
    ", ret);
                break;
            }
            sleep(1);
    
            //客户端 接受报文
            ret = sckClient_rev(connfd, time, &out, &outlen); 
            if (ret == Sck_ErrPeerClosed) {
                printf("---服务器关闭,客户端断开连接 
    ");
                break;
            } else if (ret == Sck_ErrTimeOut) {
                printf("---服务器发送数据 超时 
    ");
                continue;
            } else if (ret != 0) {
                printf("客户端接收数据失败:errorNO:%d
    ", ret);
                break;
            }    
        }
    
        //客户端 关闭和服务端的连接
        if (connfd != -1)
            sckClient_closeconn(connfd);
            
        //客户端 释放
        sckClient_destroy();
        
        return 0;    
    }
    client.c

    创建目录test,把makefile、libitcastsocket.so、poolsocket.h放入test目录下。

    >make

    >./server

    打开另一个终端,切换到test目录下,执行>./client,然后查看原终端的数据接收情况。

    注意中文乱码,需要转换:

    为什么会出现服务器检测到客户端发送数据超时?

    2、客户端连接池连接服务器

    》编写client2.c

    #include <stdio.h>
    #include <stdlib.h>
    #include <string.h>
    #include <unistd.h>
    #include <pthread.h>
     
    #include "poolsocket.h" 
    
    /*
    typedef struct _SCKClitPoolParam
    {
        char     serverip[64];
        int     serverport;
        int     bounds; //池容量
        int     connecttime;
        int     sendtime;
        int     revtime;
    }SCKClitPoolParam;
    */ 
    
    void *mystart_routin(void *arg) 
    {
        int ret = 0;
        int connfd = -1;
        void *handle = arg;
        
        unsigned char *data = "abcdefg";
        int datalen = 5;
        
        unsigned char *out = NULL;
        int outlen = -1;
            
        // 获取一条连接池中的链接:
        ret = sckCltPool_getConnet(handle, &connfd);
        if (ret != 0) {
            printf("从连接池 获取 连接失败:%d
    ", ret);
            return NULL;    
        }
        //可以增加发送数据的次数,flag=10,flag--
        while (1) {
            ret = sckCltPool_send(handle, connfd, data, datalen);
            if (ret == Sck_ErrPeerClosed) {
                printf("---服务器关闭,客户端断开连接 
    ");
                break;
            } else if (ret == Sck_ErrTimeOut) {
                printf("---服务器接收数据 超时 
    ");
                continue;
            } else if (ret != 0) {
                printf("客户端发送数据失败:errorNO:%d
    ", ret);
                break;
            }
            
            ret = sckCltPool_rev(handle, connfd, &out, &outlen); 
            if (ret == Sck_ErrPeerClosed) {
                printf("---服务器关闭,客户端断开连接 
    ");
                break;
            } else if (ret == Sck_ErrTimeOut) {
                printf("---服务器发送数据 超时 
    ");
                continue;
            } else if (ret != 0) {
                printf("客户端接收数据失败:errorNO:%d
    ", ret);
                break;
            }
            printf("------接收到 服务器回发数据:%s
    ", out);        
        }
        sckCltPool_putConnet(handle, connfd, 0); 
    
        
        return NULL;
    }
    
    
    int main(void)
    {
        int i = 0;
        int ret = 0;
        pthread_t pidArray[6] = {0};
        
        SCKClitPoolParam clientPoolparam;
        strcpy(clientPoolparam.serverip, "127.0.0.1");
        clientPoolparam.serverport = 8080;
        clientPoolparam.bounds = 10;
        clientPoolparam.connecttime = 3;
        clientPoolparam.sendtime = 3;
        clientPoolparam.revtime = 3;
        
        void *handle = NULL;
    
        //客户端 初始化
        ret = sckCltPool_init(&handle, &clientPoolparam);
        if (ret != 0) {
            printf("sckCltPool_init error %d
    ", ret);    
            return ret;
        }
        
        while (1) {
            
            for (i = 0; i < 6; i++) {
                pthread_create(&pidArray[i], NULL, mystart_routin, handle);    //需要判断返回值
            }
            
            for (i = 0; i< 6; i++) {
                pthread_join(pidArray[i], NULL);    
            }
        }
    
        //销毁连接池
        sckCltPool_destroy(handle);
    
        return 0;    
    }
    client2.c

    >make

    >./server

    打开另一个终端,切换到test目录下,执行>./client,然后查看原终端的数据接收情况。

    注意:pthread_create(&pidArray[i], NULL, myclient_startroutine, handle);在客户端初始化后,从连接池取连接时,最后一个参数采用参数handle,这是句柄。而单进程的server的pthread_create(&pid, NULL,  mystart_routine, (void *)(connfd));的最后一个参数connfd,是文件描述符。这是二者的区别。

    3、线程传参现象展示

    >vi a_server.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 <pthread.h>
    #include "poolsocket.h"
    
    
    void *mystart_routine(void *arg)
    {
        int         ret = 0;
        int         timeout = 3;
        int         connfd = (int)arg;
        
        unsigned char     *out = NULL;
        int             outlen = 0;
        
        while (1)
        {    
            //服务器端端接受报文
            ret =  sckServer_rev(connfd, timeout, &out, &outlen); //1
            if (ret == Sck_ErrPeerClosed)
            {
                //printf("aaaaa 
    ");
                printf("服务器端检测到客户端有一条连接已关闭 
    ");
                break;
            }
            else if (ret == Sck_ErrTimeOut)
            {
                printf("服务器端send超时
    ");
                continue;
            }
            else if (ret != 0)
            {
                printf("服务器端 sckServer_send() err
    ");
                break;
            }
        
            printf("out:%s 
    ", out);  //回射
            
            //服务器端发送报文
            ret =  sckServer_send(connfd, timeout, out, outlen);
            if (ret == Sck_ErrPeerClosed)
            {
                sck_FreeMem((void **)&out);
                printf("服务器端检测到客户端有一条连接已关闭
    ");
                break;
            }
            else if (ret == Sck_ErrTimeOut)
            {
                sck_FreeMem((void **)&out);
                printf("服务器端send超时
    ");
                continue;
            }
            else if (ret != 0)
            {
                sck_FreeMem((void **)&out);
                printf("服务器端 sckServer_send() err
    ");
                break;
            }
            sck_FreeMem((void **)&out);
        }
        
        sckServer_close(connfd);
        return NULL;
    }
    
    
    int main()
    {
        int         ret         = 0;
        int             port         = 8001;
        int         listenfd     = 0;
        int         timeout        = 3;
        int         connfd = 0;
        pthread_t     pid;
        
        
        //函数声明
        //服务器端初始化
        ret = sckServer_init(port, &listenfd);
        if (ret != 0)
        {
            printf("func sckServer_init() err:%d 
    ", ret);
            return ret;
        }
        
        while (1)
        {
            ret = sckServer_accept(listenfd, timeout, &connfd);
            if (ret == Sck_ErrTimeOut)
            {
                printf("func sckServer_accept() Sck_ErrTimeOut
    ");
                continue;
            }
            else if (ret != 0)
            {
                ret = 2;
                printf("fun sckServer_accept() err :%d 
    ", ret);
                break;
            }
        
            pthread_create(&pid, NULL,  mystart_routine, (void *)(connfd));
    
        }
    
    
        //服务器端环境释放 
        int sckServer_destroy();
        printf("hello....
    ");    
    }
    a_server.c

    注意:pthread_create(&pid, NULL,  mystart_routine, (void *)(connfd));最后一个参数强制转换的时候考虑32/64位的差异!

    >vi a_client3err.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 <pthread.h>
    #include "poolsocket.h"
    
    typedef struct _ThreadInfo
    {
        void     *handle;//线程池连接句柄
        int     iLoop;//一个线程被回调后会被执行多少次,跑多少圈,向服务器发送数据
        int     iArrayIndex;   //线程数组的下标,第几个被创建出来的线程
    }ThreadInfo;
    
    
    void* myclient_startroutine (void *arg)
    {
         int             i = 0, ret = 0;
         int             connfd = 0;
     
        char             data[64] = {0};
         int             datalen = 0;
         
        unsigned char  *out = NULL;
        int             outlen = 0;
        
        ThreadInfo         *pThreadInfo = (ThreadInfo *)arg;
        void             *handle = pThreadInfo->handle; //
    
        
        //客户端 socket池 获取一条连接 
        ret = sckCltPool_getConnet(handle, &connfd);
        if (ret != 0)
        {
            printf("func sckCltPool_getConnet() err:%d
    ", ret);
            return NULL;
        }
        
        for (i=0; i<pThreadInfo->iLoop; i++)
        {
            //客户端 socket池 发送数据 
            memset(data, 0, sizeof(data));
            sprintf(data, "第%d线程, 第%d圈", pThreadInfo->iArrayIndex, i+1);
            
            ret = sckCltPool_send(handle, connfd,  data, strlen(data));
            if (ret == Sck_ErrPeerClosed)
             { 
                 printf("sckCltPool_send 客户端检测到 服务器已经关闭 退出
    ");
                 break;    
             }
             else if (ret == Sck_ErrTimeOut)
             {
                 printf(" sckCltPool_send timeout 
    ");
                 break;
             }
             else if (ret != 0)
             {
                 printf("fun sckServer_rev() err:%d 
    ", ret);
                 break;
             }
    
            //客户端 socket池 接受数据
            ret =  sckCltPool_rev(handle, connfd, &out, &outlen); //1
            if (ret == Sck_ErrPeerClosed)
             { 
                 printf("sckCltPool_rev 客户端检测到 服务器已经关闭 退出
    ");
                 break;    
             }
             else if (ret == Sck_ErrTimeOut)
             {
                 printf(" sckCltPool_rev timeout 
    ");
                 break;
             }
             else if (ret != 0)
             {
                 printf("fun sckCltPool_rev() err:%d 
    ", ret);
                 break;
             }
             printf("客户端 out:%s 
    ", out);
             sck_FreeMem((void **)&out);
        }
        
        //客户端 socket池 把连接放回 socket池中 
        sckCltPool_putConnet(handle, connfd, 0); //0正常 1
        
         return NULL;
    }
     
    int main(void)
    {
        int         ret = 0, i = 0;
        char         *ip = "127.0.0.1"; 
        int         port = 8001;
        int         time = 3;
        int         connfd = 0;
        
        int            iLoop = 0; //圈数
        int            iThreadNum = 0 ; //线程数
        
        void         *handle = NULL;
        pthread_t     pidArray[1024]; 
        
        ThreadInfo             threadInfo;
        memset(&threadInfo, 0, sizeof(ThreadInfo));
        
        SCKClitPoolParam            sckClitPoolParm;
        memset(&sckClitPoolParm, 0, sizeof(SCKClitPoolParam));
        strcpy(sckClitPoolParm.serverip,  "127.0.0.1");
        sckClitPoolParm.serverport = 8001;
        sckClitPoolParm.bounds = 10;
        sckClitPoolParm.connecttime = 3;
        sckClitPoolParm.sendtime = 3;
        sckClitPoolParm.revtime = 3;
        
        
        printf("
    请输入线程的个数: ");
        scanf("%d", &iThreadNum);
        
        printf("
    请输入每个线程运行圈数: ");
        scanf("%d", &iLoop);
        
        if (iThreadNum >= 1024)
        {
            printf("iThreadNum大于1024
    ");
            return 0;
        }
    
        //客户端 socket池初始化
        ret = sckCltPool_init(&handle,  &sckClitPoolParm);
        if (ret != 0)
        {
            printf("func  sckCltPool_init() err:%d 
     ", ret);
            return ret;
        }
        
        //启动多线程 
        for (i=0; i<iThreadNum; i++)
        {
            threadInfo.handle = handle;              //alt + 鼠标键左键拖动 
            threadInfo.iLoop =  iLoop;
            threadInfo.iArrayIndex = i + 1;
        pthread_create(&pidArray[i], NULL, myclient_startroutine, (void *)&threadInfo);
        }
        
        
        //主进程 等待子线程 结束
        for (i=0; i<iThreadNum; i++)
        {
             pthread_join(pidArray[i], NULL);
        }
        
        
        //客户端 socket池 销毁连接
        sckCltPool_destroy(handle);
    
        printf("client hello....
    ");
        
        
        return 0;
    }
    a_client3err.c

    >make

    >./a_server

    打开另一个终端,切换到test目录下,执行>./a_client3err,然后查看原终端的数据接收情况。

    注意:重新定义了一个结构体:

    typedef struct _ThreadInfo
    {
        void     *handle;//线程池连接句柄
        int     iLoop;//一个线程被回调后会被执行多少次,跑多少圈,向服务器发送数据
        int     iArrayIndex;   //线程数组的下标,第几个被创建出来的线程
    }ThreadInfo;

    1)问题:pthread_create(&pidArray[i], NULL, myclient_startroutine, (void *)&threadInfo);中的最后一个参数需要加地址符号吗?

    由于threadInfo是一个结构体,从大小及值拷贝需要的空间复杂度考虑,所以须传入地址,不能传值。结论——结构体做参数,最好传地址!!!

    2)问题:每次都是最后一个线程被回调然后执行n次?跑n圈?

    分析:pthread_create传参最后一个参数threadInfo使用了地址传参。需要看4、线程传参内存冗余法

    4、线程传参内存冗余法

    》内存冗余分析图:

    注意:形参和局部变量地位等同,都位于栈上。

    原因:线程子函数栈空间调用了main函数栈空间的值,当线程几乎同时创建时,去读取main栈空间的值时,main栈空间的值i还在变化(自增)。所以需要为每个线程子函数创建属于自己的栈空间。

    >vi a_client4.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 <pthread.h>
    #include "poolsocket.h"
    
    typedef struct _ThreadInfo
    {
        void     *handle;
        int     iLoop;
        int     iArrayIndex;//线程数组的下标
    }ThreadInfo;
    
    
     void* myclient_startroutine (void *arg)
     {
         int             i = 0, ret = 0;
         int             connfd = 0;
     
         char             data[64] = {0};
          int             datalen = 0;
         
        unsigned char  *out = NULL;
        int             outlen = 0;
        
        ThreadInfo         *pThreadInfo = (ThreadInfo *)arg;
        void             *handle = pThreadInfo->handle; //
    
        
        //客户端 socket池 获取一条连接 
        ret = sckCltPool_getConnet(handle, &connfd);
        if (ret != 0)
        {
            printf("func sckCltPool_getConnet() err:%d
    ", ret);
            return NULL;
        }
        
        for (i=0; i<pThreadInfo->iLoop; i++)
        {
            //客户端 socket池 发送数据 
            memset(data, 0, sizeof(data));
            sprintf(data, "第%d线程, 第%d圈", pThreadInfo->iArrayIndex, i+1);
            ret = sckCltPool_send(handle, connfd,  data, strlen(data));
            if (ret == Sck_ErrPeerClosed)
             { 
                 printf("sckCltPool_send 客户端检测到 服务器已经关闭 退出
    ");
                 break;    
             }
             else if (ret == Sck_ErrTimeOut)
             {
                 printf(" sckCltPool_send timeout 
    ");
                 break;
             }
             else if (ret != 0)
             {
                 printf("fun sckServer_rev() err:%d 
    ", ret);
                 break;
             }
    
            //客户端 socket池 接受数据
            ret =  sckCltPool_rev(handle, connfd, &out, &outlen); //1
            if (ret == Sck_ErrPeerClosed)
             { 
                 printf("sckCltPool_rev 客户端检测到 服务器已经关闭 退出
    ");
                 break;    
             }
             else if (ret == Sck_ErrTimeOut)
             {
                 printf(" sckCltPool_rev timeout 
    ");
                 break;
             }
             else if (ret != 0)
             {
                 printf("fun sckCltPool_rev() err:%d 
    ", ret);
                 break;
             }
             printf("客户端 out:%s 
    ", out);
             sck_FreeMem((void **)&out);
        }
        
        //客户端 socket池 把连接放回 socket池中 
        sckCltPool_putConnet(handle, connfd, 0); //0正常 1
        
        if (arg != NULL) free(arg);
        
         return NULL;
     }
     
    int  main()
    {
        int         ret = 0, i = 0;
        char         *ip = "127.0.0.1"; 
        int         port = 8001;
        int         time = 3;
        int         connfd = 0;
        
        int            iLoop = 0; //圈数
        int            iThreadNum = 0 ; //线程数
        
        void         *handle = NULL;
        pthread_t         pidArray[1024]; 
        
        //ThreadInfo             threadInfo;
        //memset(&threadInfo, 0, sizeof(ThreadInfo));
        
        SCKClitPoolParam            sckClitPoolParm;
        memset(&sckClitPoolParm, 0, sizeof(SCKClitPoolParam));
        strcpy(sckClitPoolParm.serverip,  "127.0.0.1");
        sckClitPoolParm.serverport = 8001;
        sckClitPoolParm.bounds = 20;  //node: 客户端线程池的个数是10个
        sckClitPoolParm.connecttime = 3;
        sckClitPoolParm.sendtime = 3;
        sckClitPoolParm.revtime = 3;
        
        
        printf("
    请输入线程的个数: ");
        scanf("%d", &iThreadNum);
        
        printf("
    请输入每个线程运行圈数: ");
        scanf("%d", &iLoop);
        
        if (iThreadNum >= 1024)
        {
            printf("iThreadNum大于1024
    ");
            return 0;
        }
    
        //客户端 socket池初始化
        ret = sckCltPool_init(&handle,  &sckClitPoolParm);
        if (ret != 0)
        {
            printf("func  sckCltPool_init() err:%d 
     ", ret);
            return ret;
        }
        
        //启动多线程 
        for (i=0; i<iThreadNum; i++)
        {
            ThreadInfo *pInfo = (ThreadInfo *)malloc(sizeof(ThreadInfo));
            pInfo->handle = handle;              //alt + 鼠标键左键拖动 
            pInfo->iLoop =  iLoop;
            pInfo->iArrayIndex = i + 1;
            pthread_create(&pidArray[i], NULL, myclient_startroutine, (void *)pInfo);
        }
        
        
        //主进程 等待子线程 结束
        for (i=0; i<iThreadNum; i++)
        {
             pthread_join(pidArray[i], NULL);
        }
        
        
        //客户端 socket池 销毁连接
        sckCltPool_destroy(handle);
    
        printf("client hello....
    ");
        return 0;
    }
    a_client4.c

    >make

    >./a_server

    打开另一个终端,切换到test目录下,执行>./a_client4,然后查看原终端的数据接收情况。

    》总结(线程传参):
        传值:
            值传递。 传递的变量 是常量(不变)。
        传地址:
            传变量地址。    变量在回调函数内会被修改。
                    变量在回调函数内部使用时,会不断变化。
            解决---- 内存冗余法。

    三、安全传输平台项目—统一共享内存组件

    (1)共享内存的特征
    (2)共享内存常用的基础API
    (3)Linux内核管理共享内存的方法
    (4)函数接口封装

    1、常见IPC

    》共享内存的特征:
        IPC:    pipe:    简单。只能在有血缘关系的进程间通信。
            fifo:    在无血缘关系的进程间通信。    内部数据只能读一次。
            mmap:    在无血缘关系的进程间通信。    内部数据支持反复读取。
            信号:    开销小。 携带数据简单。
            本地 socket: 稳定性最好。 实现复杂。
            共享内存 shared mem : 类似于 mmap。  效率最高IPC。

    2、简单内存模型分析

    3、共享内存操作函数—shmget

    >man shmget

    int shmget(key_t key, size_t size, int shmflg);
        key_t key:16进制 非0数据。 0x0018  ----描述当前共享内存 状态。 shared --- private
        size_t size: 大小
        int shmflg:状态:读写执行权限 -- 8进制。 0644 。
            IPC_CREAT(用于创建) | IPC_EXCL(用于判断是否存在)
        返回值:成功 shmid, 失败-1, 设置errorno

    >测试函数shmget(0x0001, 256, 0644 | IPC_CREAT);

    #include <unistd.h>
    #include <stdio.h>
    #include <stdlib.h>
    #include <fcntl.h>
    #include <sys/stat.h>
    #include <sys/mman.h>
    #include <errno.h>
    #include <string.h>
    
    #include <sys/ipc.h>
    #include <sys/shm.h>
    
    int main(void)
    {
        int shmid = -1;
        
        char *buf = NULL;
        
        shmid = shmget(0x0001, 256, 0644 | IPC_CREAT);
        if (shmid == -1) {
            perror("shmget error");
            return -1;        
        }
        
        printf("-------------open shm ok
    ");
        
        return 0;
    }
    test_shm.c
    src = $(wildcard *.c)
    obj = $(patsubst %.c, %, $(src))
    
    ALL:$(obj)
    
    CFLAGS = -Wall -g 
    #LIBNAME = -litcastsocket
    #LIBFLAGS = -L$(HOME)/lib     #LD_LIBRARY_PAHT=$(HOME)/lib
    
    $(obj):%:%.c
        gcc $< -o $@ $(CFLAGS)  
        
    clean:
        -rm -rf $(obj)
        
    PHONY: ALL clean
        
    makefile

    基本操作:

    》查看共享内存
    >ipcs


    》操作共享内存
    [it01@localhost ~]$ ipcrm -m
    ipcrm:选项需要一个参数 -- m
    ipcrm: illegal option -- ?
    usage: ipcrm [ [-q msqid] [-m shmid] [-s semid]
                  [-Q msgkey] [-M shmkey] [-S semkey] ... ]

    >测试函数shmget(0x0018, 256, 0644 | IPC_CREAT | IPC_EXCL);

    #include <unistd.h>
    #include <stdio.h>
    #include <stdlib.h>
    #include <fcntl.h>
    #include <sys/stat.h>
    #include <sys/mman.h>
    #include <errno.h>
    #include <string.h>
    
    #include <sys/ipc.h>
    #include <sys/shm.h>
    
    int main(void)
    {
        int shmid = -1;
        
        char *buf = NULL;
        
        shmid = shmget(0x0018, 256, 0644 | IPC_CREAT  | IPC_EXCL);
        if (shmid == -1) {
            perror("shmget error");
            return -1;        
        }
        
        printf("-------------open shm ok
    ");
        
        return 0;
    }
    test_shm.c

    >ipcs

    >./test_shm(会报错)

    》如何清除某个用户下创建的共享内存?

    需要用到脚本cleanipc:

    #!/bin/bash
    
    #check parameters 
    if [ "$#" != "2" ]
    then
        echo "usage: $0 user shm|sem|smg|all"
        exit 1
    elif [ "$2" != "shm" -a "$2" != "sem" -a "$2" != "msg" -a "$2" != "all" ]
    then
        echo "usage: $0 user shm|sem|smg|all"
        exit 1
    fi
    
    #delete the shm
    if [ "$2" = "shm" -o "$2" = "all" ]
    then
        START=`ipcs|sed -n '/Shared/='`
        END=`ipcs|sed -n '/Semaphore/='`
        for i in `ipcs|sed -n ${START},${END}p|cut -d ' ' -f 2 `
            do
                ipcrm shm $i
                echo -e $i
            done
    fi
    
    #delete the sem
    if [ "$2" = "sem" -o "$2" = "all" ]
    then
        START=`ipcs|sed -n '/Semaphore/='`
        END=`ipcs|sed -n '/Message/='`
        for i in `ipcs|sed -n ${START},${END}p|grep $1|cut -d ' ' -f 2 `
            do
                ipcrm sem $i
                echo -e $i
            done
    fi
    
    #delete the msg
    if [ "$2" = "msg" -o "$2" = "all" ]
    then
        START=`ipcs|sed -n '/Message/='`
        for i in `ipcs|sed -n ${START},$$p|grep $1|cut -d ' ' -f 2 `
            do
                ipcrm msg $i
                echo -e $i
            done
    fi
    cleanipc

    >.cleanipc test04 all

    注意:只是清除了用户创建的共享内存,系统级的共享内存仍然存在!

    4、共享内存操作函数—shmat(shmdt了解)

    >man shmat

        void *shmat(int shmid, const void *shmaddr, int shmflg);  建立进程与共享内存的 关联关系。
            shmid: shmget 返回值
            shmaddr: 建议传入地址。默认NULL内核自动分配。
            shmflg: SHM_RND 读写。
                 SHM_RDONLY 只读。
            返回值:成功 映射内存首地址, 失败(void *)-1, 设置errorno

        int shmdt(const void *shmaddr);
            取消 进程与共享内存的 关联关系。
            返回值:成功 0, 失败-1, 设置errorno

    >测试shmat(shmid, NULL, SHM_RND);和shmdt(buf);

    #include <unistd.h>
    #include <stdio.h>
    #include <stdlib.h>
    #include <fcntl.h>
    #include <sys/stat.h>
    #include <sys/mman.h>
    #include <errno.h>
    #include <string.h>
    
    #include <sys/ipc.h>
    #include <sys/shm.h>
    
    int main(void)
    {
        int shmid = -1;
        
        char *buf = NULL;
        
        shmid = shmget(0x0018, 256, 0644 | IPC_CREAT);//返回共享内存的id
        if (shmid == -1) {
            perror("shmget error");
            return -1;        
        }
        
        printf("-------------open shm ok
    ");
        
        buf = shmat(shmid, NULL, SHM_RND);//返回共享内存的地址
        if (buf == (void *)-1) {
            perror("shmat error:");
            return -1;        
        }
        
        memcpy(buf, "hello", 5);
        
        printf("buf:%s
    ", buf);
        
        getchar();
            
        shmdt(buf);
        
        printf("finish---dt-----------
    ");
        
        return 0;
    }
    test_shm.c

    >make

    打开另一个终端,输入>ipcs查看该共享内存的使用情况:

    在原终端输入任意字符(如:回车)

    再次在第二个终端输入>ipcs查看该共享内存的使用情况:

    5、共享内存操作函数—shmctl

    >man chmctl

        int shmctl(int shmid, int cmd, struct shmid_ds *buf);   Mark标记共享内存,即将被删除
            shmid: shmget 返回值
            cmd:    IPC_RMID 删除 共享内存 (引用计数 变为 0)
                对应 参数3 传 NULL
            返回值:成功 0, 失败-1, 设置errorno

    >测试shmctl(shmid, IPC_RMID, NULL);

    #include <unistd.h>
    #include <stdio.h>
    #include <stdlib.h>
    #include <fcntl.h>
    #include <sys/stat.h>
    #include <sys/mman.h>
    #include <errno.h>
    #include <string.h>
    
    #include <sys/ipc.h>
    #include <sys/shm.h>
    
    int main(void)
    {
        int shmid = -1;
        
        char *buf = NULL;
        
        shmid = shmget(0x0018, 256, 0644 | IPC_CREAT);//返回共享内存的id
        if (shmid == -1) {
            perror("shmget error");
            return -1;        
        }
        
        printf("-------------open shm ok
    ");
        
        buf = shmat(shmid, NULL, SHM_RND);//返回共享内存的地址
        if (buf == (void *)-1) {
            perror("shmat error:");
            return -1;        
        }
        
        memcpy(buf, "hello", 5);
        
        printf("buf:%s
    ", buf);
        
        getchar();
            
        shmdt(buf);
        
        printf("finish---dt-----------
    ");
        
        getchar();
        
        int ret = shmctl(shmid, IPC_RMID, NULL);
        if (ret == -1) {
            perror("shmctl error:");
            return -1;    
        }
        
        return 0;
    }
    test_shm.c

    仍按照 4、共享内存操作函数—shmat(shmdt了解)  测试,当最后再次输入任意字符,如:换行,在另一个终端输入>ipcs 查看0x0018 的共享内存,发现已经没有了(计数为0)。

    6、Linux内核管理共享内存方法

    问题抛出:A、B、C、D四个进程共享同一块共享内存,如果一个进程执行了shmctl函数(相当于Mark标记了一下),其他进程会怎么样?

    #include <unistd.h>
    #include <stdio.h>
    #include <stdlib.h>
    #include <fcntl.h>
    #include <sys/stat.h>
    #include <sys/mman.h>
    #include <errno.h>
    #include <string.h>
    
    #include <sys/ipc.h>
    #include <sys/shm.h>
    
    int main(void)
    {
        int shmid = -1;
        
        char *buf = NULL;
        
        shmid = shmget(0x0018, 256, 0644 | IPC_CREAT);//返回共享内存的id
        if (shmid == -1) {
            perror("shmget error");
            return -1;        
        }
        
        printf("-------------open shm ok
    ");
        
        buf = shmat(shmid, NULL, SHM_RND);//返回共享内存的地址
        if (buf == (void *)-1) {
            perror("shmat error:");
            return -1;        
        }
        
        memcpy(buf, "hello", 5);
    /*
        printf("buf:%s
    ", buf);
        
        getchar();
            
        shmdt(buf);
        
        printf("finish---dt-----------
    ");
    */
        printf("----Enter 执行 shmctl 删除共享内存---
    ");
        getchar();
        
        int ret = shmctl(shmid, IPC_RMID, NULL);
        if (ret == -1) {
            perror("shmctl error:");
            return -1;    
        }
        
        return 0;
    }
    test_shm.c

    >make

    分别打开四个终端,切换到相应目录下,执行>./test_shm
    同时打开第5个终端,执行>ipcs 查看对比:

    任选一个终端,执行回车(----Enter 执行 shmctl 删除共享内存---):
    ------ Shared Memory Segments --------
    key        shmid      owner      perms      bytes      nattch     status 
    0x00000018 5734417    test04     644        256        4        
    -------------------------------------------------------------------------
    0x00000000 5734417    test04     644        256        3          dest  

    对比总结:
    key 0x0018 ---> 0x0000  PRIVATE 状态。——key值发生了变化

    (当1号进程创建了0x0018的共享内存后,2、3、4号进程创建时,直接加入后共享0x0018内存,(5、6进程也可以加入)当3号进程执行了shmctl(IPC_RMID)后,共享内存0x0018变为私有,1、2、4仍然共享内存,key变为0x0000,但是5、6等新进程不能加入进来共享这块共享内存了。)

    注意:private私有的意思是说针对新加入的进程5、6加入不进来了,而针对原来的1、2、4仍然是共享的。

    》总结:Linux内核管理共享内存的方法:
        key值 + 引用计数 技术。

    问题:当打开四个终端,切换到相应目录下,执行>./test_shm,A、B、C、D都异常终止(执行Ctrl+C),A、B、C、D进程结束了,第5个终端执行>ipcs key值和引用计数分别是几???

    总结:虽然A、B、C、D进程都结束了,但是共享内存仍然存在!

    7、共享内存操作函数接口

    >myipc_shm.h

    // myipc_shm.h
    #ifndef _WBM_MY_SHM_H_
    #define _WBM_MY_SHM_H_
    
    #include <stdio.h>
    #include <stdlib.h>
    #include <string.h>
    
    #ifdef __cplusplus 
    extern "C" {
    #endif
    
    //共享内存错误码
    #define        MYIPC_OK                0        //正确
    #define        MYIPC_ParamErr            301        //输入参数失败
    #define        MYIPC_NotEXISTErr        302        //共享内存不存在错误
    #define        MYIPC_CreateErr            303        //创建共享内存错误
    
    //创建共享内存 若共享内存不存在,则创建
    int IPC_CreatShm(int key, int shmsize, int *shmhdl);
    
    //打开共享内存 若共享内存不存在,返回错误
    int IPC_OpenShm(int key, int shmsize, int *shmhdl);
    
    /***********************************************************************
      功能描述:    关联共享内存
      参数说明:    shmhdl    [in]  共享的句柄
                    mapaddr [out] 共享内存首地址
      返回值:      返回0函数执行成功;非0返回错误码
    ************************************************************************/
    int IPC_MapShm(int shmhdl, void **mapaddr);
    
    /***********************************************************************
      功能描述:    取消共享内存关联
      参数说明:    unmapaddr   [in] 共享内存首地址
      返回值:      返回0函数执行成功;非0返回错误码
    ************************************************************************/
    int IPC_UnMapShm(void *unmapaddr);
    
    /***********************************************************************
      功能描述:    删除共享内存
      参数说明:    shmhdl    [in]  共享的句柄
      返回值:      返回0函数执行成功;非0返回错误码
    ************************************************************************/
    int IPC_DelShm(int shmhdl);
    
    /***********************************************************************
      功能描述:    创建共享内存 通过种子文件
      参数说明:    shmname  [in]  是共享内存名,系统中唯一标志
                    shmsize  [in]  是要创建的共享内存的大小;
                    shmhdl   [out] 共享内存的句柄.
      返回值:      返回0函数执行成功;非0返回错误码
    ************************************************************************/
    int IPC_CreatShmBySeedName(const char *shmname, int shmsize, int *shmhdl);
    
    #ifdef __cplusplus
    }
    #endif
    #endif
    myipc_shm.h

    >myipc_shm.c

    #define    _OS_LINUX_
    
    #if defined _OS_LINUX_
    #include <stdio.h>
    #include <errno.h>
    #include <unistd.h>
    #include <memory.h>
    #include <sys/ipc.h>
    #include <sys/shm.h>
    #include <sys/sem.h>
    #include <sys/msg.h>
    #include "myipc_shm.h" 
    
    #endif
    
    int shmflag = 0;
    int shmkey;
    
    
    //创建共享内存 若共享内存不存在,则创建 若存在使用原来的
    int IPC_CreatShm(int key, int shmsize, int *shmhdl)
    {
        int        tmpshmhdl = 0;
        int     ret = 0;
         //    创建共享内存 
         //    若共享内存不存在则创建 
         //    若共享内存已存在使用原来的
        tmpshmhdl = shmget(key, shmsize, IPC_CREAT|0666);
        if (tmpshmhdl == -1)            //创建失败
        {
            ret = MYIPC_ParamErr;
            printf("func shmget() err :%d ", ret);
            return ret;
        }
        *shmhdl = tmpshmhdl;
        return ret;
    }
    
    //打开共享内存 若共享内存不存在,返回错误
    //参数 无意义 可填写0
    int IPC_OpenShm(int key, int shmsize, int *shmhdl)
    {
        int        tmpshmhdl = 0;
        int     ret = 0;
         //    创建共享内存 
         //    若共享内存不存在则创建 
         //    若共享内存已存在使用原来的
        tmpshmhdl = shmget(key, 0, 0);
        if (tmpshmhdl == -1)            //打开失败
        {
            ret = MYIPC_NotEXISTErr;
            //printf("func shmget() err :%d ", ret);
            return ret;
        }
        *shmhdl = tmpshmhdl;
        return ret;
        
    }
    
    /***********************************************************************
      功能描述:    创建共享内存
      参数说明:    shmname  [in]  是共享内存名,系统中唯一标志
                    shmsize  [in]  是要创建的共享内存的大小;
                    shmhdl   [out] 共享内存的句柄.
      返回值:      返回0函数执行成功;非0返回错误码
    ************************************************************************/
    int IPC_CreatShmBySeedName(char *shmseedfile, int shmsize, int *shmhdl)
    {
        if(shmflag == 0)            //判断接口中共享内存key是否已经存在
        {
            shmkey = ftok(shmseedfile, 'c');
            if (shmkey == -1)
            {
                perror("ftok");
                return -1;
            }
                
            shmflag = 1;
        }
        
        //创建共享内存
        *shmhdl = shmget(shmkey,shmsize,IPC_CREAT|0666);
        if (*shmhdl == -1)            //创建失败
            return -2;
        return 0;
    
    }
    /***********************************************************************
      功能描述:    关联共享内存
      参数说明:    shmhdl    [in]  共享的句柄
                    mapaddr [out] 共享内存首地址
      返回值:      返回0函数执行成功;非0返回错误码
    ************************************************************************/
    int
    IPC_MapShm(int  shmhdl, void **mapaddr)
    {
        void *tempptr = NULL;
    
        //连接共享内存
        tempptr = (void *)shmat(shmhdl,0,SHM_RND);
        if ((int)tempptr == -1)        //共享内存连接失败
            return -1;
        *mapaddr = tempptr;            //导出共享内存首指针
    
        return 0;
    }
    /***********************************************************************
      功能描述:    取消共享内存关联
      参数说明:    unmapaddr   [in] 共享内存首地址
      返回值:      返回0函数执行成功;非0返回错误码
    ************************************************************************/
    int IPC_UnMapShm(void *unmapaddr)
    {
        int  rv;
        //取消连接共享内存 
        rv = shmdt((char *)unmapaddr);
        if (rv == -1)            //取消连接失败
            return -1;
    
        return 0;
    }
    /***********************************************************************
      功能描述:    删除共享内存
      参数说明:    shmhdl    [in]  共享的句柄
      返回值:      返回0函数执行成功;非0返回错误码
    ************************************************************************/
    int IPC_DelShm(int shmhdl)
    {
        int  rv;
        //删除共享内存
        rv = shmctl(shmhdl,IPC_RMID,NULL);
        if(rv < 0)                //删除共享内存失败
            return -1;
            
        return 0;
    }
    myipc_shm.c

    四、安全传输平台项目—密钥协商服务程序

    1、客户端服务器密钥协商预说明

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

  • 相关阅读:
    latch与DFF
    数字逻辑综合DC脚本示例及解释
    当DiscuzNT遇上了Loadrunner(下)
    [C#学习]在多线程中如何调用Winform
    并发性测试工具
    当DiscuzNT遇上了Loadrunner(上)
    大型网站(高访问、海量数据)技术架构
    Load Runner下载
    Invoke 和 BeginInvoke 的真正涵义
    当DiscuzNT遇上了Loadrunner(中)
  • 原文地址:https://www.cnblogs.com/Alliswell-WP/p/CPlusPlus_SecureTransmissionPlatform_Project04.html
Copyright © 2020-2023  润新知