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

在学习安全传输平台项目总结了笔记,并分享出来。有问题请及时联系博主: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 \n");
            printf("服务器端检测到客户端有一条连接已关闭 \n");
            break;
        }
        else if (ret == Sck_ErrTimeOut)
        {
            printf("服务器端send超时\n");
            continue;
        }
        else if (ret != 0)
        {
            printf("服务器端 sckServer_send() err\n");
            break;
        }
    
        printf("out:%s \n", out);  //回射
        
        //服务器端发送报文
        ret =  sckServer_send(connfd, timeout, out, outlen);
        if (ret == Sck_ErrPeerClosed)
        {
            sck_FreeMem((void **)&out);
            printf("服务器端检测到客户端有一条连接已关闭\n");
            break;
        }
        else if (ret == Sck_ErrTimeOut)
        {
            sck_FreeMem((void **)&out);
            printf("服务器端send超时\n");
            continue;
        }
        else if (ret != 0)
        {
            sck_FreeMem((void **)&out);
            printf("服务器端 sckServer_send() err\n");
            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 \n", ret);
        return ret;
    }
    
    while (1)
    {
        ret = sckServer_accept(listenfd, timeout, &connfd);
        if (ret == Sck_ErrTimeOut)
        {
            printf("func sckServer_accept() Sck_ErrTimeOut\n");
            continue;
        }
        else if (ret != 0)
        {
            ret = 2;
            printf("fun sckServer_accept() err :%d \n", ret);
            break;
        }
    
        pthread_create(&pid, NULL,  mystart_routine, (void *)(connfd));

    }


    //服务器端环境释放 
    int sckServer_destroy();
    printf("hello....\n");    
}
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\n", ret);    
        return ret;
    }
    
    while (1) {
    
        //客户端 连接服务器
        ret = sckClient_connect(ip, port, time, &connfd);
         if (ret == Sck_ErrTimeOut) {
            printf("---客户端连接服务器 超时 \n");
            continue;
        } else if (ret != 0) {
            printf("客户端连接服务器 失败: errorNO:%d\n", ret);
            break;
        }
            
        //客户端 发送报文
        ret = sckClient_send(connfd, time, data, datalen);
        if (ret == Sck_ErrPeerClosed) {
            printf("---服务器关闭,客户端断开连接 \n");
            break;
        } else if (ret == Sck_ErrTimeOut) {
            printf("---服务器接收数据 超时 \n");
            continue;
        } else if (ret != 0) {
            printf("客户端发送数据失败:errorNO:%d\n", ret);
            break;
        }
        sleep(1);

        //客户端 接受报文
        ret = sckClient_rev(connfd, time, &out, &outlen); 
        if (ret == Sck_ErrPeerClosed) {
            printf("---服务器关闭,客户端断开连接 \n");
            break;
        } else if (ret == Sck_ErrTimeOut) {
            printf("---服务器发送数据 超时 \n");
            continue;
        } else if (ret != 0) {
            printf("客户端接收数据失败:errorNO:%d\n", 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\n", ret);
        return NULL;    
    }
    //可以增加发送数据的次数,flag=10,flag--
    while (1) {
        ret = sckCltPool_send(handle, connfd, data, datalen);
        if (ret == Sck_ErrPeerClosed) {
            printf("---服务器关闭,客户端断开连接 \n");
            break;
        } else if (ret == Sck_ErrTimeOut) {
            printf("---服务器接收数据 超时 \n");
            continue;
        } else if (ret != 0) {
            printf("客户端发送数据失败:errorNO:%d\n", ret);
            break;
        }
        
        ret = sckCltPool_rev(handle, connfd, &out, &outlen); 
        if (ret == Sck_ErrPeerClosed) {
            printf("---服务器关闭,客户端断开连接 \n");
            break;
        } else if (ret == Sck_ErrTimeOut) {
            printf("---服务器发送数据 超时 \n");
            continue;
        } else if (ret != 0) {
            printf("客户端接收数据失败:errorNO:%d\n", ret);
            break;
        }
        printf("------接收到 服务器回发数据:%s\n", 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\n", 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 \n");
            printf("服务器端检测到客户端有一条连接已关闭 \n");
            break;
        }
        else if (ret == Sck_ErrTimeOut)
        {
            printf("服务器端send超时\n");
            continue;
        }
        else if (ret != 0)
        {
            printf("服务器端 sckServer_send() err\n");
            break;
        }
    
        printf("out:%s \n", out);  //回射
        
        //服务器端发送报文
        ret =  sckServer_send(connfd, timeout, out, outlen);
        if (ret == Sck_ErrPeerClosed)
        {
            sck_FreeMem((void **)&out);
            printf("服务器端检测到客户端有一条连接已关闭\n");
            break;
        }
        else if (ret == Sck_ErrTimeOut)
        {
            sck_FreeMem((void **)&out);
            printf("服务器端send超时\n");
            continue;
        }
        else if (ret != 0)
        {
            sck_FreeMem((void **)&out);
            printf("服务器端 sckServer_send() err\n");
            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 \n", ret);
        return ret;
    }
    
    while (1)
    {
        ret = sckServer_accept(listenfd, timeout, &connfd);
        if (ret == Sck_ErrTimeOut)
        {
            printf("func sckServer_accept() Sck_ErrTimeOut\n");
            continue;
        }
        else if (ret != 0)
        {
            ret = 2;
            printf("fun sckServer_accept() err :%d \n", ret);
            break;
        }
    
        pthread_create(&pid, NULL,  mystart_routine, (void *)(connfd));

    }


    //服务器端环境释放 
    int sckServer_destroy();
    printf("hello....\n");    
}
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\n", 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 客户端检测到 服务器已经关闭 退出\n");
             break;    
         }
         else if (ret == Sck_ErrTimeOut)
         {
             printf(" sckCltPool_send timeout \n");
             break;
         }
         else if (ret != 0)
         {
             printf("fun sckServer_rev() err:%d \n", ret);
             break;
         }

        //客户端 socket池 接受数据
        ret =  sckCltPool_rev(handle, connfd, &out, &outlen); //1
        if (ret == Sck_ErrPeerClosed)
         { 
             printf("sckCltPool_rev 客户端检测到 服务器已经关闭 退出\n");
             break;    
         }
         else if (ret == Sck_ErrTimeOut)
         {
             printf(" sckCltPool_rev timeout \n");
             break;
         }
         else if (ret != 0)
         {
             printf("fun sckCltPool_rev() err:%d \n", ret);
             break;
         }
         printf("客户端 out:%s \n", 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("\n请输入线程的个数: ");
    scanf("%d", &iThreadNum);
    
    printf("\n请输入每个线程运行圈数: ");
    scanf("%d", &iLoop);
    
    if (iThreadNum >= 1024)
    {
        printf("iThreadNum大于1024\n");
        return 0;
    }

    //客户端 socket池初始化
    ret = sckCltPool_init(&handle,  &sckClitPoolParm);
    if (ret != 0)
    {
        printf("func  sckCltPool_init() err:%d \n ", 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....\n");
    
    
    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\n", 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 客户端检测到 服务器已经关闭 退出\n");
             break;    
         }
         else if (ret == Sck_ErrTimeOut)
         {
             printf(" sckCltPool_send timeout \n");
             break;
         }
         else if (ret != 0)
         {
             printf("fun sckServer_rev() err:%d \n", ret);
             break;
         }

        //客户端 socket池 接受数据
        ret =  sckCltPool_rev(handle, connfd, &out, &outlen); //1
        if (ret == Sck_ErrPeerClosed)
         { 
             printf("sckCltPool_rev 客户端检测到 服务器已经关闭 退出\n");
             break;    
         }
         else if (ret == Sck_ErrTimeOut)
         {
             printf(" sckCltPool_rev timeout \n");
             break;
         }
         else if (ret != 0)
         {
             printf("fun sckCltPool_rev() err:%d \n", ret);
             break;
         }
         printf("客户端 out:%s \n", 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("\n请输入线程的个数: ");
    scanf("%d", &iThreadNum);
    
    printf("\n请输入每个线程运行圈数: ");
    scanf("%d", &iLoop);
    
    if (iThreadNum >= 1024)
    {
        printf("iThreadNum大于1024\n");
        return 0;
    }

    //客户端 socket池初始化
    ret = sckCltPool_init(&handle,  &sckClitPoolParm);
    if (ret != 0)
    {
        printf("func  sckCltPool_init() err:%d \n ", 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....\n");
    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\n");
    
    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\n");
    
    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\n");
    
    buf = shmat(shmid, NULL, SHM_RND);//返回共享内存的地址
    if (buf == (void *)-1) {
        perror("shmat error:");
        return -1;        
    }
    
    memcpy(buf, "hello", 5);
    
    printf("buf:%s\n", buf);
    
    getchar();
        
    shmdt(buf);
    
    printf("finish---dt-----------\n");
    
    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\n");
    
    buf = shmat(shmid, NULL, SHM_RND);//返回共享内存的地址
    if (buf == (void *)-1) {
        perror("shmat error:");
        return -1;        
    }
    
    memcpy(buf, "hello", 5);
    
    printf("buf:%s\n", buf);
    
    getchar();
        
    shmdt(buf);
    
    printf("finish---dt-----------\n");
    
    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\n");
    
    buf = shmat(shmid, NULL, SHM_RND);//返回共享内存的地址
    if (buf == (void *)-1) {
        perror("shmat error:");
        return -1;        
    }
    
    memcpy(buf, "hello", 5);
/*
    printf("buf:%s\n", buf);
    
    getchar();
        
    shmdt(buf);
    
    printf("finish---dt-----------\n");
*/
    printf("----Enter 执行 shmctl 删除共享内存---\n");
    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,转载请注明出处。

posted on 2020-07-27 10:27  Alliswell_WP  阅读(447)  评论(0编辑  收藏  举报

导航