安全传输平台项目——客户端代码移植-项目模块总结

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

10-安全传输平台项目-第11天(客户端代码移植-项目模块总结)

目录:
一、复习
二、安全传输平台项目——客户端代码移植-项目模块总结
1、图形客户端-密钥协商组件分析
2、图形客户端-物理组件集成
3、图形客户端-Linux源码对接错误调试
4、图形客户端-系统初始化
5、图形客户端-删除控件对应函数
6、图形客户端-密钥协商
7、图形客户端-密钥协商内存释放错误说明
8、总结
9、接口设计
10、文件加密原理
11、文件加密函数源码
12、数字证书、非对称加密

一、复习

1、复习-连接数据库
2、复习-网点信息管理

二、安全传输平台项目——客户端代码移植-项目模块总结

1、图形客户端-密钥协商组件分析

》SecMngClient协商客户端 界面设计及功能实现和SecMngServer配置管理 大体相同:

1)打开“VS”,新建Dialog,ID更改为:IDD_DIALOG_CLIENT,布局如下:

2)在“IDD_DIALOG_CLIENT”界面右键“添加类”,类名输入:CViewClient,基类选择:CFormView

3)使用类向导,为ListCtrl添加成员变量:

注意:自定义变量m_imageList不是通过类向导定义的,是通过代码定义的!

4)在MainFrm.cpp中OnOutbarNotify函数中打开case代码,增加调用(去掉return 0)

5)“类向导”中(ViewClient.cpp)重写OnInitialUpdate函数;和DlgNetInfo类似!

6)在“IDD_DIALOG_CLIENT”界面中为按钮设置ID并添加回调函数

回调函数的逻辑在Linux下已经做过:借助wind图形界面(客户端与服务器密钥协商、密钥校验、密钥注销)与linux客户端 文字界面(与 服务器密钥协商、密钥校验、密钥注销)逻辑一样,所以这块代码不用写了,把Linux下的代码移植过来。当然还需要更改!

对比分析:

》分析wind客户端和服务器 与 linux客户端和服务器 4大基础组件区别:
    1)统一报文编码解码    libmessagereal.so .h ---> .dll .lib .h
    2)统一通信组件socket    --- windows socket 通信        
    3)共享内存        --- windows shm 机制
    4)数据库访问    (客户端无需数据库)

》代码如下:

1)报文编解码_win

>messagereal.dll

>messagereal.lib

>keymng_msg.h

#ifndef _KEYMNG_MSG_H_
#define _KEYMNG_MSG_H_

#ifdef __cplusplus
extern "C" {
#endif

#define        KeyMng_ParamErr            200        //输入参数失败
#define        KeyMng_TypeErr            201        //输入类型失败
#define        KeyMng_MallocErr        202        //分配内存失败

#define        KeyMng_NEWorUPDATE        1        //1 密钥更新 
#define        KeyMng_Check            2        //2 密钥校验
#define        KeyMng_Revoke            3        //3 密钥注销
             ;     
#define  ID_MsgKey_Req  60

//密钥请求报文
typedef struct _MsgKey_Req
{
    //1 密钥更新      //2 密钥校验;     //3 密钥注销
    int                cmdType;        //报文命令码 
    char            clientId[12];    //客户端编号
    char            AuthCode[16];    //认证码
    char            serverId[12];    //服务器端I编号
    char            r1[64];        //客户端随机数
    
}MsgKey_Req;


//密钥应答报文
#define  ID_MsgKey_Res  61
typedef struct  _MsgKey_Res
{
    int                    rv;                //返回值
    char                clientId[12];    //客户端编号
    char                serverId[12];    //服务器编号
    unsigned char        r2[64];            //服务器端随机数
    int                    seckeyid;        //对称密钥编号 //modfy 2015.07.20
}MsgKey_Res;


/*
 pstruct :    输入的报文数据 ; (指向相应结构体的指针) 
 type :        输入的类型标识(函数内部通过type 得到 pstruct 所指向的报文类型)
 poutData:    输出的编码后的报文 ; 
 outlen :    输出的数据长度;
*/

int MsgEncode(
    void            *pStruct , /*in*/
    int                type,
    unsigned char    **outData, /*out*/
    int                *outLen );

/*
 inData        : 输入的编码后的数据;
 inLen        : 输入的数据长度 ;
 pstruct    : 输出的解码后的数据; (其空间是在内部开辟的,也需要用内部定义的free函数进行释放)
 type        : 结构的类型标识(返回类型标识,使得调用者通过flag进行判断,将pstruct 转换为相应的结构)
*/
int MsgDecode( 
    unsigned char *inData,/*in*/
    int           inLen,
    void          **pStruct /*out*/,
    int           *type /*out*/);


/*
释放 MsgEncode( )函数中的outData; 方法:MsgMemFree((void **)outData, 0); 
释放MsgDecode( )函数中的pstruct结构体,MsgMemFree((void **)outData, type);
type : 输入参数,便于函数判断调用哪个结构体的free函数
*/ 

int MsgMemFree(void **point,int type);

#ifdef __cplusplus
}
#endif

#endif
keymng_msg.h

2)共享内存_win_Linux

>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);


/***********************************************************************
  功能描述:    创建共享内存 通过种子文件
  参数说明:    shmname  [in]  是共享内存名,系统中唯一标志
                shmsize  [in]  是要创建的共享内存的大小;
                shmhdl   [out] 共享内存的句柄.
  返回值:      返回0函数执行成功;非0返回错误码
************************************************************************/
int IPC_CreatShmBySeedName(char *shmname, 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);


#ifdef __cplusplus
}
#endif
#endif
myipc_shm.h

>myipc_shm.cpp

//#define    _OS_LINUX_

#include "stdafx.h"

#define    _OS_WIN_ 1
#if defined _OS_WIN_
#include <conio.h>
#include <stdio.h>
#include <memory.h>
#include <string.h>
#include <windows.h>
#endif


#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

#include "myipc_shm.h"

int shmflag = 0;
int shmkey;


//创建共享内存 若共享内存不存在,则创建 若存在使用原来的
int IPC_CreatShm(int key, int shmsize, int *shmhdl)
{
    int        tmpshmhdl = 0;
    int     ret = 0;
#ifdef _OS_LINUX_
     //    创建共享内存 
     //    若共享内存不存在则创建 
     //    若共享内存已存在使用原来的
    tmpshmhdl = shmget(key, shmsize, IPC_CREAT|0666);
    if (tmpshmhdl == -1)            //创建失败
    {
        ret = MYIPC_ParamErr;
        printf("func shmget() err :%d ", ret);
        return ret;
    }
    *shmhdl = tmpshmhdl;
#endif

#ifdef _OS_WIN_
    char shmname[512] = {0};
    HANDLE m_hMapFile;

    sprintf(shmname, "%d", key);

     tmpshmhdl =(int)OpenFileMapping(FILE_MAP_WRITE, FALSE, shmname); 
     if (tmpshmhdl <= 0)
     {
        printf("共享内存不存在, 创建共享内存
");
        tmpshmhdl = (int)CreateFileMapping(    (HANDLE)0xFFFFFFFF,
            NULL,
            PAGE_READWRITE,
            0,
            shmsize,
            shmname);
        if ( tmpshmhdl == 0 )
        {
            return MYIPC_CreateErr;
        }
     }
     *shmhdl = tmpshmhdl;
#endif

    return ret;
}

//打开共享内存 若共享内存不存在,返回错误
//参数 无意义 可填写0
int IPC_OpenShm(int key, int shmsize, int *shmhdl)
{
    int        tmpshmhdl = 0;
    int     ret = 0;
#ifdef _OS_LINUX_
     //    创建共享内存 
     //    若共享内存不存在则创建 
     //    若共享内存已存在使用原来的
    tmpshmhdl = shmget(key, 0, 0);
    if (tmpshmhdl == -1)            //打开失败
    {
        ret = MYIPC_NotEXISTErr;
        //printf("func shmget() err :%d ", ret);
        return ret;
    }
    *shmhdl = tmpshmhdl;
#endif

#ifdef _OS_WIN_
    char shmname[512] = {0};
    sprintf(shmname, "%d", key);
    tmpshmhdl = (int)OpenFileMapping(FILE_MAP_WRITE, FALSE, shmname);  //modify (int)
    if (tmpshmhdl <= 0)
    {
        ret = MYIPC_NotEXISTErr;
        //printf("func shmget() err :%d ", ret);
        return ret;
    }
    *shmhdl = tmpshmhdl;
#endif
    return ret;
    
}

//   功能描述:    创建共享内存
//   参数说明:    shmname  [in]  是共享内存名,系统中唯一标志
//                 shmsize  [in]  是要创建的共享内存的大小;
//                 shmhdl   [out] 共享内存的句柄.
//   返回值:      返回0函数执行成功;非0返回错误码

int IPC_CreatShmBySeedName(char *shmseedfile, int shmsize, int *shmhdl)
{
    int        tmpshmhdl = 0;
    int     ret = 0;
#ifdef _OS_LINUX_
    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;
#endif


#ifdef _OS_WIN_
    tmpshmhdl = (int)CreateFileMapping(    (HANDLE)0xFFFFFFFF,
        NULL,
        PAGE_READWRITE,
        0,
        shmsize,
        shmseedfile);
    if ( tmpshmhdl == 0 )
    {
        return MYIPC_CreateErr;
    }
    *shmhdl = tmpshmhdl;
#endif
    return 0;

}

//   功能描述:    关联共享内存
//   参数说明:    shmhdl    [in]  共享的句柄
//                 mapaddr [out] 共享内存首地址
//   返回值:      返回0函数执行成功;非0返回错误码

int
IPC_MapShm(int  shmhdl, void  **mapaddr)
{
    void *tempptr = NULL;

#ifdef _OS_LINUX_
    //连接共享内存
    tempptr = (void *)shmat(shmhdl,0,SHM_RND);
    if ((int)tempptr == -1)        //共享内存连接失败
        return -1;
    *mapaddr = tempptr;            //导出共享内存首指针
#endif

#ifdef _OS_WIN_
    tempptr = MapViewOfFile((HANDLE)shmhdl,
        FILE_MAP_WRITE | FILE_MAP_READ, 
        0, 0, 0);
    if( tempptr == NULL )
        return -1;

    *mapaddr = tempptr;
#endif
    return 0;
}

//   功能描述:    取消共享内存关联
//   参数说明:    unmapaddr   [in] 共享内存首地址
//   返回值:      返回0函数执行成功;非0返回错误码

int IPC_UnMapShm(void *unmapaddr)
{
    int  ret = 0;
#ifdef _OS_LINUX_
    //取消连接共享内存 
    ret = shmdt((char *)unmapaddr);
    if (ret == -1)            //取消连接失败
        return -1;
#endif

#ifdef _OS_WIN_
    ret = UnmapViewOfFile(unmapaddr);
    if (ret == 0)
    {
        return -1;
    }
    else
    {
        return 0; //modify 
    }
        
#endif

    return ret;
}

//   功能描述:    删除共享内存
//   参数说明:    shmhdl    [in]  共享的句柄
//   返回值:      返回0函数执行成功;非0返回错误码

int IPC_DelShm(int shmhdl)
{
    int  ret;

#ifdef _OS_LINUX_
    //删除共享内存
    ret = shmctl(shmhdl,IPC_RMID,NULL);
    if(ret < 0)                //删除共享内存失败
        return -1;
#endif

#ifdef _OS_WIN_
    ret = CloseHandle((HANDLE)shmhdl);
    if (ret == 0)
        return -1;
#endif
    return ret;
}
myipc_shm.cpp

3)socket通信_win_Linux

>poolsocket.h

#ifndef _poolsocket_H_
#define _poolsocket_H_


//错误码定义  
#define Sck_Ok                 0
#define Sck_BaseErr           3000

#define Sck_ErrParam                    (Sck_BaseErr+1)
#define Sck_ErrTimeOut                    (Sck_BaseErr+2)
#define Sck_ErrPeerClosed               (Sck_BaseErr+3)
#define Sck_ErrMalloc                       (Sck_BaseErr+4)

#define Sck_Err_Pool_CreateConn                (Sck_BaseErr+20)  //创建连接池 (没有达到最大连接数)
#define Sck_Err_Pool_terminated                (Sck_BaseErr+21) //已终止
#define Sck_Err_Pool_GetConn_ValidIsZero    (Sck_BaseErr+22) //有效连接数是零
#define Sck_Err_Pool_HaveExist                (Sck_BaseErr+22) //连接已经在池中
#define Sck_Err_Pool_ValidBounds            (Sck_BaseErr+22) //有效连接数目超过了最大连接数


typedef struct _SCKClitPoolParam
{
    char     serverip[64];
    int     serverport;
    int     bounds; //池容量
    int     connecttime;
    int     sendtime;
    int     revtime;
}SCKClitPoolParam;


//客户端 初始化
int sckClient_init();

//客户端 连接服务器
int sckClient_connect(char *ip, int port, int connecttime, int *connfd);

//客户端 关闭和服务端的连接
int sckClient_closeconn(int connfd);

//客户端 发送报文
int sckClient_send(int connfd, int sendtime, unsigned char *data, int datalen);

//客户端 接受报文
int sckClient_rev(int connfd, int revtime, unsigned char **out, int *outlen); //1

//客户端 释放内存
int sck_FreeMem(void **buf);
//客户端 释放
int sckClient_destroy();


//客户端 socket池初始化
int sckCltPool_init(void **handle, SCKClitPoolParam *param);

//客户端 socket池 获取一条连接 
int sckCltPool_getConnet(void *handle, int *connfd);

//客户端 socket池 发送数据 
int sckCltPool_send(void *handle, int  connfd,  unsigned char *data, int datalen);

//客户端 socket池 接受数据
int sckCltPool_rev(void *handle, int  connfd, unsigned char **out, int *outlen); //1

//客户端 socket池 把连接放回 socket池中 
int sckCltPool_putConnet(void *handle, int connfd, int validFlag); //0正常 1

//客户端 socket池 销毁连接
int sckCltPool_destroy(void *handle);

//函数声明
//服务器端初始化
int sckServer_init(int port, int *listenfd);

int sckServer_accept(int listenfd, int timeout, int *connfd);
//服务器端发送报文
int sckServer_send(int connfd, int timeout, unsigned char *data, int datalen);
//服务器端端接受报文
int sckServer_rev(int  connfd, int timeout, unsigned char **out, int *outlen); //1

int sckServer_close(int connfd);

//服务器端环境释放 
int sckServer_destroy();

// #ifdef __cpluspluse
// }
// #endif
#endif
poolsocket.h

>poolsocket.cpp

// __declspec(dllexport)
/*
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <signal.h>
#include <sys/wait.h>

#include <fcntl.h>
#include <sys/time.h>
#include <sys/time.h>
*/
#include "stdafx.h"
#include "windows.h"
#include "winbase.h"
#include <process.h>


#include <stdlib.h>
#include <stdio.h>
#include <errno.h>
#include <string.h>
//#include <pthread.h>

#include "poolsocket.h"
#include "socketlog.h"

//Socket连接池结构
typedef struct _SockePoolHandle
{
    int *            fdArray;            //Socket连接池
    int    *            statusArray;        //每条连接的状态  eg: statusArray[0] =  1表示 链接有效 statusArray[0] =  0表示 链接无效
    int                valid;                //Socket有效连接数目 
    int                nvalid;                //Socket无效连接数目 
    int                bounds;                //Socket连接池的容量
    
    char             serverip[128];
    int             serverport;
    
    int             connecttime;
    int             sendtime;
    int             revtime;
    int                sTimeout; //没有连接时,等待之间
    //pthread_mutex_t foo_mutex ;
    LPCRITICAL_SECTION pCS ;  
    //判断连接池是否已经终止
    int                terminated;  //1已经终止 0没有终止
}SockePoolHandle;


//客户端 socket池初始化
int sckCltPool_init(void **handle, SCKClitPoolParam *param)
{
    int        ret =  0, i = 0;
    
    SockePoolHandle *hdl = NULL;
    
    //初始化 句柄
    hdl = (SockePoolHandle *)malloc(sizeof(SockePoolHandle));
    if (hdl == NULL)
    {
        ret = Sck_ErrMalloc;
        Socket_Log(__FILE__, __LINE__,SocketLevel[4], ret,"func sckCltPool_init(), check malloc err");
        return ret;
    }
    memset(hdl, 0, sizeof(hdl));
    
    strcpy(hdl->serverip, param->serverip);
    hdl->serverport = param->serverport;
    
    hdl->connecttime = param->connecttime;
    hdl->sendtime = param->sendtime;
    hdl->revtime = param->revtime;
    
    //处理连接数
    hdl->bounds  = param->bounds;
    hdl->valid = 0;
    hdl->nvalid = param->bounds;
    hdl->sTimeout = 1;

    hdl->pCS = (LPCRITICAL_SECTION)malloc(sizeof(CRITICAL_SECTION));
    if (hdl->pCS==NULL)
    {       
        ret = Sck_ErrMalloc;
        Socket_Log(__FILE__, __LINE__,SocketLevel[4], ret,"func sckCltPool_init(), check malloc err");
        return ret;
    }
    InitializeCriticalSection(hdl->pCS); //初始化
    EnterCriticalSection(hdl->pCS);
         
    //为连接句柄分配内存
    hdl->fdArray = (int *)malloc( hdl->bounds  * sizeof(int) );
    if (hdl->fdArray == NULL)
    {
        ret = Sck_ErrMalloc;
        Socket_Log(__FILE__, __LINE__,SocketLevel[4], ret,"func sckCltPool_init(), check malloc err");
        goto END;
    }
    hdl->statusArray = (int *)malloc( hdl->bounds  * sizeof(int) );
    if (hdl->statusArray == NULL)
    {
        ret = Sck_ErrMalloc;
        Socket_Log(__FILE__, __LINE__,SocketLevel[4], ret,"func sckCltPool_init(), check malloc err");
        goto END;
    }
    
    ret = sckClient_init();
    if (ret != 0)
    {
        printf("func sckClient_init() err:%d
", ret);
        goto END;
    }
    
    for (i=0; i<hdl->bounds; i++)
    {
        ret = sckClient_connect(hdl->serverip, hdl->serverport , hdl->connecttime , &(hdl->fdArray[i]) );
        if (ret != 0)
        {
            Socket_Log(__FILE__, __LINE__,SocketLevel[4], ret,"func sckClient_connect() err");
            break;
        }
        else
        {
            hdl->statusArray[i] = 1;
            hdl->valid ++;            //Socket有效连接数目
            hdl->nvalid --;            //Socket无效连接数目
        }
    }
    
    if (hdl->valid < hdl->bounds ) //若有效连接数 小于 总数
    {
        ret = Sck_Err_Pool_CreateConn;
        Socket_Log(__FILE__, __LINE__,SocketLevel[4], ret,"有效连接数 小于 总数");
        Socket_Log(__FILE__, __LINE__,SocketLevel[4], ret,"func sckClient_init() create connect num err:%d,  hdl->valid: %d , hdl->bounds:%d", ret, hdl->valid,   hdl->bounds);    
        for (i=0; i<hdl->bounds; i++)
        {
            if (hdl->statusArray[i] == 1)
            {
                sckClient_closeconn(hdl->fdArray[i]);
            }
        }
    }
    
END:    
    //pthread_mutex_unlock(& (hdl->foo_mutex) ); //解锁
    LeaveCriticalSection(hdl->pCS);
    if (ret != 0)
    {
        if (hdl->fdArray != NULL)         free(hdl->fdArray);
        if (hdl->statusArray != NULL)     free(hdl->statusArray);
        free(hdl);
        Socket_Log(__FILE__, __LINE__,SocketLevel[4], ret,"func pthread_mutex_unlock() err");
        return ret;
    }
    
    *handle = hdl; //间接赋值
    return ret;    
}

//客户端 socket池 获取一条连接 
int sckCltPool_getConnet(void *handle, int *connfd)
{
    int        ret =  0;
    
    SockePoolHandle *hdl = NULL;
    
    if (handle == NULL || connfd==NULL)
    {
        ret = Sck_ErrParam;
        Socket_Log(__FILE__, __LINE__,SocketLevel[4], ret,"func sckCltPool_getConnet() (handle == NULL || connfd==NULL) err");
        return ret;
    }
    
    hdl = (SockePoolHandle *)handle;
    //pthread_mutex_lock( &(hdl->foo_mutex) ); //流程加锁 pthread_mutex_unlock(& (hdl->foo_mutex) ); //解锁
    EnterCriticalSection(hdl->pCS);
    
    
    //若 已终止
    if (hdl->terminated == 1)
    {
        ret = Sck_Err_Pool_terminated;
        Socket_Log(__FILE__, __LINE__,SocketLevel[4], ret,"func sckCltPool_getConnet() (terminated == 1)");
        goto END;
    }
    
    //若 有效连数 = 0
    if (hdl->valid == 0)
    {
        //usleep(hdl->sTimeout); //等上几微妙
        Sleep(hdl->sTimeout);
        
        if (hdl->valid == 0)
        {
            ret = Sck_Err_Pool_GetConn_ValidIsZero;
            Socket_Log(__FILE__, __LINE__,SocketLevel[4], ret,"func sckCltPool_getConnet() Sck_Err_Pool_GetConn_ValidIsZero err");
            goto END;
        }
        
        //若 已终止
        if (hdl->terminated == 1)
        {
            ret = Sck_Err_Pool_terminated;
            Socket_Log(__FILE__, __LINE__,SocketLevel[4], ret,"func sckCltPool_getConnet() (terminated == 1)");
            goto END;
        }
    }
        
    //判断现有连接的状态
    if (hdl->statusArray[hdl->valid-1] == 0 )
    {
        //首先断开坏掉的连接
        if (hdl->fdArray[hdl->valid-1] == 0)
        {
            ret = sckClient_closeconn(hdl->fdArray[hdl->valid-1]);
            if (ret != 0)
            {
                Socket_Log(__FILE__, __LINE__,SocketLevel[4], ret,"funcsckClient_closeconn()err");
                hdl->fdArray[hdl->valid-1] = 0;
                //出错不做错误处理
            }
        }
    
        //断链修复 重新连接 1次
        ret = sckClient_connect(hdl->serverip, hdl->serverport, hdl->connecttime, &(hdl->fdArray[hdl->valid-1]) );
        if (ret != 0)
        {
            Socket_Log(__FILE__, __LINE__,SocketLevel[4], ret," func sckClient_connect() err 断链修复 重新连接失败");
            hdl->fdArray[hdl->valid-1] = 0;
            goto END;
        }
    }
    
END:
    if (ret == 0)
    {
        *connfd = hdl->fdArray[ --(hdl->valid) ]; //注 有效连接数 减1
    }
    
    //pthread_mutex_unlock(& (hdl->foo_mutex) ); //解锁
    //printf("valid=%d;nvalid=%d;bounds=%d 
", hdl->valid, hdl->nvalid, hdl->bounds);
    LeaveCriticalSection(hdl->pCS);
    return ret;    
}

//客户端 socket池 发送数据 
int sckCltPool_send(void *handle, int  connfd,  unsigned char *data, int datalen)
{
    int        ret =  0;
    SockePoolHandle *hdl = NULL;
    
    if (handle==NULL || connfd<0 || data==NULL || datalen<=0 )
    {
        ret = Sck_ErrParam;
        Socket_Log(__FILE__, __LINE__,SocketLevel[4], ret," func sckCltPool_send() err (handle==NULL || connfd<0 || data==NULL || datalen<=0) ");
        return ret;
    }
    hdl = (SockePoolHandle *)handle;
    
    //客户端 发送报文
    ret = sckClient_send(connfd, hdl->sendtime , data, datalen);
    if (ret != 0)
    {
        Socket_Log(__FILE__, __LINE__,SocketLevel[4], ret," func sckClient_send() err");
        return ret;
    }
    
    return ret;    
}

//客户端 socket池 接受数据
int sckCltPool_rev(void *handle, int  connfd, unsigned char **out, int *outlen)
{
    int        ret =  0;
    SockePoolHandle *hdl = NULL;
    
    if (handle==NULL || connfd<0 || out==NULL || outlen==NULL )
    {
        ret = Sck_ErrParam;
        Socket_Log(__FILE__, __LINE__,SocketLevel[4], ret," func sckCltPool_rev() err, check (handle==NULL || connfd<0 || out==NULL || outlen==NULL )");
        return ret;
    }
    hdl = (SockePoolHandle *)handle;
    
    //客户端 接受报文
    ret = sckClient_rev(connfd, hdl->revtime,  out,  outlen); //1
    if (ret != 0)
    {
        Socket_Log(__FILE__, __LINE__,SocketLevel[4], ret," func sckClient_rev() err");
        return ret;
    }
    
    return ret;    
}

//客户端 socket池 把连接放回 socket池中 
int sckCltPool_putConnet(void *handle, int connfd, int validFlag)
{
    int        ret =  0, i = 0;

    SockePoolHandle *hdl = NULL;
    
    if (handle == NULL || connfd<0)
    {
        ret = Sck_ErrParam;
        Socket_Log(__FILE__, __LINE__,SocketLevel[4], ret," func sckCltPool_putConnet() err, check (handle == NULL || connfd==NULL)");
        goto END;
    }
    
    hdl = (SockePoolHandle *)handle;
    //pthread_mutex_lock( &(hdl->foo_mutex) ); //流程加锁 
    EnterCriticalSection(hdl->pCS);
    
    //若 已终止
    if (hdl->terminated == 1)
    {
        ret = Sck_Err_Pool_terminated;
        Socket_Log(__FILE__, __LINE__,SocketLevel[4], ret," func sckCltPool_putConnet() err, check (func sckCltPool_putConnet() (terminated == 1))");
        hdl->fdArray[hdl->valid] = connfd;
        hdl->valid++;
        goto END;
    }
    
    //判断连接是否已经被 放进来         //判断该连接是否已经被释放
    for(i=0; i<hdl->valid; i++)
    {
        if (hdl->fdArray[i] == connfd)
        {
            ret = Sck_Err_Pool_HaveExist;
            Socket_Log(__FILE__, __LINE__,SocketLevel[4], ret," func sckCltPool_putConnet() err, check Sck_Err_Pool_HaveExist ");
            goto END;
        }
    }
    
    //判断有效连接数是否已经到达最大值
    if (hdl->valid >= hdl->bounds)
    {
        ret = Sck_Err_Pool_ValidBounds ; 
        Socket_Log(__FILE__, __LINE__,SocketLevel[4], ret," func sckCltPool_putConnet() err, check (hdl->valid >= hdl->bounds) ");
        goto END;
    }
    
    //判断释放的连接是否有效
    if (validFlag == 1)
    {
        hdl->fdArray[hdl->valid] = connfd;
        hdl->statusArray[hdl->valid] = 1; //连接有效
        hdl->valid++;  //
    }
    else
    {
        int tmpconnectfd = 0;
        //首先断开坏掉的连接
        ret = sckClient_closeconn(connfd);
        if (ret != 0)
        {
            Socket_Log(__FILE__, __LINE__,SocketLevel[4], ret," func sckClient_closeconn() err, check (hdl->valid >= hdl->bounds) ");
            //失败不处理
        }
        
        //断链修复 重新连接 1次 若重新连接成功则再加入连接池中;若重新连接失败,则不需要加入到连接池中
        ret = sckClient_connect(hdl->serverip, hdl->serverport, hdl->connecttime, &tmpconnectfd );
        if (ret != 0)
        {
            Socket_Log(__FILE__, __LINE__,SocketLevel[4], ret," func sckClient_connect() err, 断链修复 重新连接失败");
        }
        else    
        {
            //有效连接数加1
            hdl->fdArray[hdl->valid] = tmpconnectfd;
            hdl->statusArray[hdl->valid] = 1; //连接有效
            hdl->valid++;  //
        }
    }

END:
    
    //pthread_mutex_unlock(& (hdl->foo_mutex) ); //解锁
    LeaveCriticalSection(hdl->pCS);
    //printf("valid=%d;nvalid=%d;bounds=%d 
", hdl->valid, hdl->nvalid, hdl->bounds);

    return ret;    
}

//客户端 socket池 销毁连接
int sckCltPool_destroy(void *handle)
{
    int        ret =  0, i = 0;

    SockePoolHandle *hdl = NULL;
    
    if (handle == NULL )
    {
        ret = Sck_ErrParam;
        Socket_Log(__FILE__, __LINE__,SocketLevel[4], ret," func sckCltPool_destroy() err, check (handle == NULL)");
        return ret;
    }
    
    hdl = (SockePoolHandle *)handle;
    //pthread_mutex_lock( &(hdl->foo_mutex) ); //流程加锁 
    EnterCriticalSection(hdl->pCS);
    
    //若 已终止
    hdl->terminated = 1; //连接池设置成终止 状态
    
    
    for (i=0; i<hdl->bounds; i++)
    {
        if (hdl->fdArray[i] != 0)
        {
            sckClient_closeconn(hdl->fdArray[i]);
        }
    }
    
    if (hdl->fdArray) 
    {
        free(hdl->fdArray); hdl->fdArray = NULL;
    }
    
    if (hdl->statusArray)
    {
        free(hdl->statusArray); hdl->statusArray = NULL;
    }

    sckClient_destroy();
    //pthread_mutex_unlock(& (hdl->foo_mutex) ); //解锁
    LeaveCriticalSection(hdl->pCS);
    free(hdl->pCS);
    free(hdl);

    //printf("valid=%d;nvalid=%d;bounds=%d 
", hdl->valid, hdl->nvalid, hdl->bounds);
    
    return ret;    
}
poolsocket.cpp

>socketlog.h

//socketlog.h 日志头文件

#ifndef _SOCKET_LOG_H_
#define _SOCKET_LOG_H_

/*
#define IC_NO_LOG_LEVEL            0
#define IC_DEBUG_LEVEL            1
#define IC_INFO_LEVEL            2
#define IC_WARNING_LEVEL        3
#define IC_ERROR_LEVEL            4;
*/

/************************************************************************/
/* 
const char *file:文件名称
int line:文件行号
int level:错误级别
        0 -- 没有日志
        1 -- debug级别
        2 -- info级别
        3 -- warning级别
        4 -- err级别
int status:错误码
const char *fmt:可变参数
*/
/************************************************************************/
//实际使用的Level
extern int  SocketLevel[5];
void Socket_Log(const char *file, int line, int level, int status, const char *fmt, ...);

#endif
socketlog.h

>socketlog.cpp

#include "stdafx.h"
#define  _CRT_SECURE_NO_WARNINGS 

#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdarg.h>
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#include <time.h>

#include "socketlog.h"

#define ITCAST_DEBUG_FILE_    "socketlib.log"
#define ITCAST_MAX_STRING_LEN         10240
//#define WIN32

//Level类别
#define IC_NO_LOG_LEVEL            0
#define IC_DEBUG_LEVEL            1
#define IC_INFO_LEVEL            2
#define IC_WARNING_LEVEL        3
#define IC_ERROR_LEVEL            4

int  SocketLevel[5] = {IC_NO_LOG_LEVEL, IC_DEBUG_LEVEL, IC_INFO_LEVEL, IC_WARNING_LEVEL, IC_ERROR_LEVEL};

//Level的名称
char ICLevelName[5][10] = {"NOLOG", "DEBUG", "INFO", "WARNING", "ERROR"};

static int ITCAST_Error_GetCurTime(char* strTime)
{
    struct tm*        tmTime = NULL;
    size_t            timeLen = 0;
    time_t            tTime = 0;    
    
    tTime = time(NULL);
    tmTime = localtime(&tTime);
    //timeLen = strftime(strTime, 33, "%Y(Y)%m(M)%d(D)%H(H)%M(M)%S(S)", tmTime);
    timeLen = strftime(strTime, 33, "%Y.%m.%d %H:%M:%S", tmTime);
    
    return timeLen;
}

static int ITCAST_Error_OpenFile(int* pf)
{
    char    fileName[1024];
    
    memset(fileName, 0, sizeof(fileName));
#ifdef WIN32
    sprintf(fileName, "c:\itcast\%s",ITCAST_DEBUG_FILE_);
#else
    sprintf(fileName, "%s/log/%s", getenv("HOME"), ITCAST_DEBUG_FILE_);
#endif
    
    /*
    *pf = open(fileName, O_WRONLY|O_CREAT|O_APPEND, 0666);
    if(*pf < 0)
    {
        return -1;
    }
    */
    *pf = (int)fopen(fileName, "w+");
    if (*pf < 0)
    {
        return -1;
    }
    
    return 0;
}

static void ITCAST_Error_Core(const char *file, int line, int level, int status, const char *fmt, va_list args)
{
    char str[ITCAST_MAX_STRING_LEN];
    int     strLen = 0;
    char tmpStr[64];
    int     tmpStrLen = 0;
    int  pf = 0;
    
    //初始化
    memset(str, 0, ITCAST_MAX_STRING_LEN);
    memset(tmpStr, 0, 64);
    
    //加入LOG时间
    tmpStrLen = ITCAST_Error_GetCurTime(tmpStr);
    tmpStrLen = sprintf(str, "[%s] ", tmpStr);
    strLen = tmpStrLen;

    //加入LOG等级
    tmpStrLen = sprintf(str+strLen, "[%s] ", ICLevelName[level]);
    strLen += tmpStrLen;
    
    //加入LOG状态
    if (status != 0) 
    {
        tmpStrLen = sprintf(str+strLen, "[ERRNO is %d] ", status);
    }
    else
    {
        tmpStrLen = sprintf(str+strLen, "[SUCCESS] ");
    }
    strLen += tmpStrLen;

    //加入LOG信息
    tmpStrLen = vsprintf(str+strLen, fmt, args);
    strLen += tmpStrLen;

    //加入LOG发生文件
    tmpStrLen = sprintf(str+strLen, " [%s]", file);
    strLen += tmpStrLen;

    //加入LOG发生行数
    tmpStrLen = sprintf(str+strLen, " [%d]
", line);
    strLen += tmpStrLen;
    
    //打开LOG文件
    if(ITCAST_Error_OpenFile(&pf))
    {
        return ;
    }
    
    //写入LOG文件
   // write(pf, str, strLen);
    fwrite(str, 1, strLen, (FILE *)pf);
    //IC_Log_Error_WriteFile(str);
    
    //关闭文件
    fclose((FILE *)pf);
    
    return ;
}


void Socket_Log(const char *file, int line, int level, int status, const char *fmt, ...)
{
    va_list args;
    
    //判断是否需要写LOG
//    if(level!=IC_DEBUG_LEVEL && level!=IC_INFO_LEVEL && level!=IC_WARNING_LEVEL && level!=IC_ERROR_LEVEL)
    if(level == IC_NO_LOG_LEVEL)
    {
        return ;
    }
    
    //调用核心的写LOG函数
    va_start(args, fmt);
    ITCAST_Error_Core(file, line, level, status, fmt, args);
    va_end(args);
    
    return ;
}
socketlog.cpp

>socketutil.h

// socketutil.h

#ifndef _socketutil_H_
#define _socketutil_H_

#ifdef __cplusplus
extern 'C'
{
#endif

#include <stdio.h>
#include <stdlib.h>

void activate_nonblock(int fd);
void deactivate_nonblock(int fd);

int read_timeout(int fd, unsigned int wait_seconds);
int write_timeout(int fd, unsigned int wait_seconds);
int accept_timeout(int fd, struct sockaddr_in *addr, unsigned int wait_seconds);
int connect_timeout(int fd, struct sockaddr_in *addr, unsigned int wait_seconds);


int readn(int fd, void *buf, size_t count);
int writen(int fd, const void *buf, size_t count);
int recv_peek(int sockfd, void *buf, size_t len);
int readline(int sockfd, void *buf, size_t maxline);


#ifdef __cpluspluse
}
#endif


#endif /* _SYS_UTIL_H_ */
socketutil.h

>socketutil.cpp

#include "stdafx.h"
#include <stdlib.h>
#include <stdio.h>


#include <winsock2.h>
#include <stdio.h>
#include <string.h>
#include <time.h>
#include <errno.h>


#include "poolsocket.h"
#include "socketlog.h"

#define ssize_t int 
#define socklen_t int 

//
//readn - 读取固定字节数
//@fd: 文件描述符
//@buf: 接收缓冲区
//@count: 要读取的字节数
//成功返回count,失败返回-1,读到EOF返回<count
//
int readn(int fd, void *buf, size_t count)
{
    size_t nleft = count;
    int nread;
    char *bufp = (char*)buf;

    while (nleft > 0)
    {
        //if ((nread = read(fd, bufp, nleft)) < 0)
        if ((nread = recv(fd, bufp, nleft, 0)) < 0)
        {
            if (errno == EINTR)
                continue;
            return -1;
        }
        else if (nread == 0)
            return count - nleft;

        bufp += nread;
        nleft -= nread;
    }

    return count;
}

//
//writen - 发送固定字节数
//@fd: 文件描述符
//@buf: 发送缓冲区
//@count: 要读取的字节数
//成功返回count,失败返回-1
//
int writen(int fd, const void *buf, size_t count)
{
    size_t nleft = count;
    ssize_t nwritten;
    char *bufp = (char*)buf;

    while (nleft > 0)
    {
        //if ((nwritten = write(fd, bufp, nleft)) < 0)
        if ((nwritten = send(fd, bufp, nleft, 0)) < 0)
        {
            if (errno == EINTR)
                continue;
            return -1;
        }
        else if (nwritten == 0)
            continue;

        bufp += nwritten;
        nleft -= nwritten;
    }

    return count;
}

//
//recv_peek - 仅仅查看套接字缓冲区数据,但不移除数据
//@sockfd: 套接字
//@buf: 接收缓冲区
//@len: 长度
//成功返回>=0,失败返回-1
//
int recv_peek(int sockfd, void *buf, size_t len)
{
    while (1)
    {
        int ret = recv(sockfd, (char *)buf, len, MSG_PEEK);
        if (ret == -1 && errno == EINTR)
            continue;
        return ret;
    }
}

//
//activate_noblock - 设置I/O为非阻塞模式
//@fd: 文件描符符
//
int activate_nonblock(int fd)
{
    int ret = 0;
    /*
    int flags = fcntl(fd, F_GETFL);
    if (flags == -1)
    {
        ret = flags;
        Socket_Log(__FILE__, __LINE__,SocketLevel[4], ret,"func activate_nonblock() err");
        return ret;
    }
        
    flags |= O_NONBLOCK;
    ret = fcntl(fd, F_SETFL, flags);
    if (ret == -1)
    {
        Socket_Log(__FILE__, __LINE__,SocketLevel[4], ret,"func activate_nonblock() err");
        return ret;
    }
    */

    
    int flags = 1;
    if (ioctlsocket(fd, FIONBIO, (u_long *)&flags))
    {
        ret = -1;
        Socket_Log(__FILE__, __LINE__,SocketLevel[4], ret,"func ioctlsocket() err 设置为非阻塞模式失败");
        return ret;
    }
    return ret;
}

//
//deactivate_nonblock - 设置I/O为阻塞模式
//@fd: 文件描符符
//
int deactivate_nonblock(int fd)
{
    int ret = 0;
    /*
    int flags = fcntl(fd, F_GETFL);
    if (flags == -1)
    {
        ret = flags;
        Socket_Log(__FILE__, __LINE__,SocketLevel[4], ret,"func deactivate_nonblock() err");
        return ret;
    }

    flags &= ~O_NONBLOCK;
    ret = fcntl(fd, F_SETFL, flags);
    if (ret == -1)
    {
        Socket_Log(__FILE__, __LINE__,SocketLevel[4], ret,"func deactivate_nonblock() err");
        return ret;
    }*/

    int flags = 0;
    if(ioctlsocket(fd, FIONBIO, (u_long *)&flags))
    {
        ret = -1;
        Socket_Log(__FILE__, __LINE__,SocketLevel[4], ret, "func ioctlsocket() err 设置为阻塞模式失败 ");
        return ret;
    }
    
    
    return ret;
}

//
//connect_timeout - connect
//@fd: 套接字
//@addr: 要连接的对方地址
//@wait_seconds: 等待超时秒数,如果为0表示正常模式
//成功(未超时)返回0,失败返回-1,超时返回-1并且errno = ETIMEDOUT
//
static int connect_timeout(int fd, struct sockaddr_in *addr, unsigned int wait_seconds)
{
    int ret;
    socklen_t addrlen = sizeof(struct sockaddr_in);

    if (wait_seconds > 0)
        activate_nonblock(fd);

    ret = connect(fd, (struct sockaddr*)addr, addrlen);
    if (ret < 0 && errno == EINPROGRESS  ||
        ret<0 && WSAGetLastError()==WSAEWOULDBLOCK ) //win和linux下
    {
        //printf("11111111111111111111
");
        fd_set connect_fdset;
        struct timeval timeout;
        FD_ZERO(&connect_fdset);
        FD_SET(fd, &connect_fdset);
        timeout.tv_sec = wait_seconds;
        timeout.tv_usec = 0;
        do
        {
            // 一但连接建立,则套接字就可写  所以connect_fdset放在了写集合中
            ret = select(fd + 1, NULL, &connect_fdset, NULL, &timeout);
        } while (ret < 0 && errno == EINTR);
        if (ret == 0)
        {
            ret = -1;
            errno = ETIMEDOUT;
        }
        else if (ret < 0)
            return -1;
        else if (ret == 1)
        {
            //printf("22222222222222222
");
            // ret返回为1(表示套接字可写),可能有两种情况,一种是连接建立成功,一种是套接字产生错误,
            // 此时错误信息不会保存至errno变量中,因此,需要调用getsockopt来获取。 
            int err;
            socklen_t socklen = sizeof(err);
            int sockoptret = getsockopt(fd, SOL_SOCKET, SO_ERROR, (char *)&err, &socklen);
            if (sockoptret == -1)
            {
                return -1;
            }
            if (err == 0)
            {
                //printf("3333333333333
");
                ret = 0;
            }
            else
            {
                //printf("4444444444444444:%d
", err);
                errno = err;
                ret = -1;
            }
        }
    }
    
    
    if (wait_seconds > 0)
    {
        deactivate_nonblock(fd);
    }
    return ret;
}


//
//write_timeout - 写超时检测函数,不含写操作
//@fd: 文件描述符
//@wait_seconds: 等待超时秒数,如果为0表示不检测超时
//成功(未超时)返回0,失败返回-1,超时返回-1并且errno = ETIMEDOUT
//
int write_timeout(int fd, unsigned int wait_seconds)
{
    int ret = 0;
    if (wait_seconds > 0)
    {
        fd_set write_fdset;
        struct timeval timeout;

        FD_ZERO(&write_fdset);
        FD_SET(fd, &write_fdset);

        timeout.tv_sec = wait_seconds;
        timeout.tv_usec = 0;
        do
        {
            ret = select(fd + 1, NULL, &write_fdset, NULL, &timeout);
        } while (ret < 0 && errno == EINTR);

        if (ret == 0)
        {
            ret = -1;
            errno = ETIMEDOUT;
        }
        else if (ret == 1)
            ret = 0;
    }

    return ret;
}


//
//read_timeout - 读超时检测函数,不含读操作
//@fd: 文件描述符
//@wait_seconds: 等待超时秒数,如果为0表示不检测超时
//成功(未超时)返回0,失败返回-1,超时返回-1并且errno = ETIMEDOUT
//
int read_timeout(int fd, unsigned int wait_seconds)
{
    int ret = 0;
    if (wait_seconds > 0)
    {
        fd_set read_fdset;
        struct timeval timeout;

        FD_ZERO(&read_fdset);
        FD_SET(fd, &read_fdset);

        timeout.tv_sec = wait_seconds;
        timeout.tv_usec = 0;
        
        //select返回值三态
        //1 若timeout时间到(超时),没有检测到读事件 ret返回=0
        //2 若ret返回<0 &&  errno == EINTR 说明select的过程中被别的信号中断(可中断睡眠原理)
        //2-1 若返回-1,select出错
        //3 若ret返回值>0 表示有read事件发生,返回事件发生的个数
        
        do
        {
            ret = select(fd + 1, &read_fdset, NULL, NULL, &timeout);
        } while (ret < 0 && errno == EINTR); 

        if (ret == 0)
        {
            ret = -1;
            errno = ETIMEDOUT;
        }
        else if (ret == 1)
            ret = 0;
    }

    return ret;
}



//函数声明
//客户端环境初始化
int sckClient_init()
{
    int        ret = 0;
    WSADATA wsaData;

    struct sockaddr_in servaddr;

    ret = WSAStartup(MAKEWORD(2, 2), &wsaData);
    if (ret != NO_ERROR)
    {
        Socket_Log(__FILE__, __LINE__,SocketLevel[4], ret,"func WSAStartup() err");
        return ret;
    }
    return 0; 
}


int sckClient_connect(char *ip, int port, int connecttime, int *connfd)
{
    
    int                    ret = 0;
    int                    sockfd;
    struct sockaddr_in    servaddr;

    if (ip==NULL || connfd==NULL || port<=0 || port>65537 || connecttime < 0)
    {
        ret = Sck_ErrParam;
        Socket_Log(__FILE__, __LINE__,SocketLevel[4], ret,"func sckClient_connect() err, check  (ip==NULL || connfd==NULL || port<=0 || port>65537 || connecttime < 0)");
        return ret;
    }
    
    //
    
    sockfd = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP);
    if (sockfd < 0)
    {
        ret = errno;
        printf("func socket() err:  %d
", ret);
        return ret;
    }
    
    
    memset(&servaddr, 0, sizeof(servaddr));
    servaddr.sin_family = AF_INET;
    servaddr.sin_port = htons(port);
    servaddr.sin_addr.s_addr = inet_addr(ip);
    
    ret = connect_timeout(sockfd, (struct sockaddr_in*) (&servaddr), (unsigned int )connecttime);
    if (ret < 0)
    {
        if (ret==-1 && errno == ETIMEDOUT)
        {
            ret = Sck_ErrTimeOut;
            return ret;
        }
        else
        {
            printf("func connect_timeout() err:  %d
", ret);
            Socket_Log(__FILE__, __LINE__,SocketLevel[4], ret,"func connect_timeout() err");
        }
        return ret;
    }
    
    *connfd = sockfd;
   
       return ret;
    
}


//客户端发送报文
int sckClient_send(int connfd, int sendtime, unsigned char *data, int datalen)
{
    int     ret = 0;
    
    if (data == NULL || datalen <= 0)
    {
        ret = Sck_ErrParam;
        Socket_Log(__FILE__, __LINE__,SocketLevel[4], ret,"func sckClient_send() err, check (data == NULL || datalen <= 0)");
        return ret;
    } 
    
    ret = write_timeout(connfd, sendtime);
    if (ret == 0)
    {
        int writed = 0;
        int netlen = 0;
        unsigned char *netdata = ( unsigned char *)malloc(datalen + 4);
        if ( netdata == NULL)
        {
            ret = Sck_ErrMalloc;
            printf("func sckClient_send() mlloc Err:%d
 ", ret);
            return ret;
        }
        netlen = htonl(datalen);
        memcpy(netdata, &netlen, 4);
        memcpy(netdata+4, data, datalen);
        
        //writed = writen(connfd, netdata, datalen + 4);
        writed = send(connfd, (const char *)netdata, datalen + 4, 0);
        if (writed < (datalen + 4) )
        {
            if (netdata != NULL) 
            {
                free(netdata);
                netdata = NULL;
            }
            return writed;
        }
        
        if (netdata != NULL)  //wangbaoming 20150630 modify bug
        {
            free(netdata);
            netdata = NULL;
        }  
    }
    
    if (ret < 0)
    {
        //失败返回-1,超时返回-1并且errno = ETIMEDOUT
        if (ret == -1 && errno == ETIMEDOUT)
        {
            ret = Sck_ErrTimeOut;
            printf("func sckClient_send() mlloc Err:%d
 ", ret);
            return ret;
        }
        return ret;
    }
    
    return ret;
}


//客户端端接受报文
int sckClient_rev(int connfd, int revtime, unsigned char **out, int *outlen)
{
    
    int        ret = 0;
    unsigned char *tmpBuf = NULL;

    int netdatalen = 0;
    int n;
    
    if (out==NULL || outlen==NULL)
    {
        ret = Sck_ErrParam;
        printf("func sckClient_rev() timeout , err:%d 
", Sck_ErrTimeOut);
        return ret;
    }
    
    ret =  read_timeout(connfd, revtime ); //bugs modify bombing
    if (ret != 0)
    {
        if (ret==-1 || errno == ETIMEDOUT)
        {
            ret = Sck_ErrTimeOut;
            Socket_Log(__FILE__, __LINE__,SocketLevel[4], ret,"func read_timeout() timeout");
            return ret;
        }
        else
        {
            Socket_Log(__FILE__, __LINE__,SocketLevel[4], ret,"func read_timeout() err");
            return ret;
        }    
    }
    
    
    ret = readn(connfd, &netdatalen,  4); //读包头 4个字节
    if (ret == -1)
    {
        //printf("func readn() err:%d 
", ret);
        Socket_Log(__FILE__, __LINE__,SocketLevel[4], ret,"func readn() err");
        return ret;
    }
    else if (ret < 4)
    {
        ret = Sck_ErrPeerClosed;
        //printf("func readn() err peer closed:%d 
", ret);
        Socket_Log(__FILE__, __LINE__,SocketLevel[4], ret,"func readn() err, peer closed");
        return ret;
    }
    
    
    n = ntohl(netdatalen);
    tmpBuf = (unsigned char *)malloc(n+1);
    if (tmpBuf == NULL)
    {
        ret = Sck_ErrMalloc;
        Socket_Log(__FILE__, __LINE__,SocketLevel[4], ret,"malloc() err");
        return ret;    
    }
    
    
    ret = readn(connfd, tmpBuf, n); //根据长度读数据
    if (ret == -1)
    {
        //printf("func readn() err:%d 
", ret);
        Socket_Log(__FILE__, __LINE__,SocketLevel[4], ret,"readn() err");
        return ret;
    }
    else if (ret < n)
    {
        ret = Sck_ErrPeerClosed;
        //printf("func readn() err peer closed:%d 
", ret);
        Socket_Log(__FILE__, __LINE__,SocketLevel[4], ret,"func readn() err,  peer closed");
        return ret;
    }
    
    *out = tmpBuf;
    *outlen = n;
    tmpBuf[n] = ''; //多分配一个字节内容,兼容可见字符串 字符串的真实长度仍然为n
    return 0;
}


//客户端 关闭与服务端的连接
int sckClient_closeconn(int connfd)
{
    if (connfd > 0 )
    {
        //close(connfd); 
        closesocket(connfd); 
    }
    return 0;
}

//客户端 释放内存
int sck_FreeMem(void **buf)
{
    if (buf == NULL)
    {
        return 0;
    }    
    if (*buf != NULL)
    {
        free (*buf);
    }
    *buf = NULL; //简介修改实参的值
    return 0;
}

// 客户端环境释放 
int sckClient_destroy()
{
    WSACleanup();
    return 0;
}


#pragma comment(lib,"ws2_32.lib") 

/////////////////////////////////////////////////////////////////////////////////////
//函数声明
//服务器端初始化
int sckServer_init(int port, int *listenfd)
{
    int     ret = 0;
    int        mylistenfd;
    int        on = 1;
    WSADATA wsaData;

    struct sockaddr_in servaddr;

    ret = WSAStartup(MAKEWORD(2, 2), &wsaData);
    if (ret != NO_ERROR)
    {
        Socket_Log(__FILE__, __LINE__,SocketLevel[4], ret,"func socket() err");
        return ret;
    }

    memset(&servaddr, 0, sizeof(servaddr));
    servaddr.sin_family = AF_INET;
    servaddr.sin_port = htons(port);
    servaddr.sin_addr.s_addr = htonl(INADDR_ANY);
    
        
    mylistenfd = socket(PF_INET, SOCK_STREAM, 0);
    if (mylistenfd < 0)
    {
        ret = errno ; 
        WSACleanup();
        Socket_Log(__FILE__, __LINE__,SocketLevel[4], ret,"func socket() err");
        return ret;
    }
    
    ret = setsockopt(mylistenfd, SOL_SOCKET, SO_REUSEADDR, (const char *)&on, sizeof(on) );
    if (ret < 0)
    {
        ret = errno ;
        WSACleanup();
        Socket_Log(__FILE__, __LINE__,SocketLevel[4], ret,"func setsockopt() err");
        return ret;
    }
    

    ret = bind(mylistenfd, (struct sockaddr*)&servaddr, sizeof(servaddr));
    if (ret < 0)
    {
        ret = errno ;
        WSACleanup();
        Socket_Log(__FILE__, __LINE__,SocketLevel[4], ret,"func bind() err");
        return ret;
    }
        
    ret = listen(mylistenfd, SOMAXCONN);
    if (ret < 0)
    {
        ret = errno ;
        WSACleanup();
        Socket_Log(__FILE__, __LINE__,SocketLevel[4], ret,"func listen() err");
        return ret;
    }
        
    *listenfd = mylistenfd;

    return 0;
}


//
//accept_timeout - 带超时的accept
//@fd: 套接字
//@addr: 输出参数,返回对方地址
//@wait_seconds: 等待超时秒数,如果为0表示正常模式
//成功(未超时)返回已连接套接字,超时返回-1并且errno = ETIMEDOUT
//
int accept_timeout(int fd, struct sockaddr_in *addr, unsigned int wait_seconds)
{
    int ret;
    //socklen_t addrlen = sizeof(struct sockaddr_in);
    int addrlen = sizeof(struct sockaddr_in);

    if (wait_seconds > 0)
    {
        fd_set accept_fdset;
        struct timeval timeout;
        FD_ZERO(&accept_fdset);
        FD_SET(fd, &accept_fdset);
        timeout.tv_sec = wait_seconds;
        timeout.tv_usec = 0;
        do
        {
            ret = select(fd + 1, &accept_fdset, NULL, NULL, &timeout);
        } while (ret < 0 && errno == EINTR);
        if (ret == -1)
            return -1;
        else if (ret == 0)
        {
            errno = ETIMEDOUT;
            return -1;
        }
    }

    //一但检测出 有select事件发生,表示对等方完成了三次握手,客户端有新连接建立
    //此时再调用accept将不会堵塞
    if (addr != NULL)
        ret = accept(fd, (struct sockaddr*)addr, &addrlen); //返回已连接套接字
    else
        ret = accept(fd, NULL, NULL);
        if (ret == -1)
        {
            ret = errno;
            Socket_Log(__FILE__, __LINE__,SocketLevel[4], ret,"func accept() err");
            return ret;
        }
    

    return ret;
}

int sckServer_accept(int listenfd,  int timeout, int *connfd)
{
    int    ret = 0;
    
    ret = accept_timeout(listenfd, NULL, (unsigned int) timeout);
    if (ret < 0)
    {
        if (ret == -1 && errno == ETIMEDOUT)
        {
            ret = Sck_ErrTimeOut;
            //printf("func accept_timeout() timeout err:%d 
", ret);
            return ret;
        }
        else
        {
            ret = errno;
            Socket_Log(__FILE__, __LINE__,SocketLevel[4], ret,"func accept_timeout() err");
            return ret;
        }
    }
    
    *connfd = ret;
    return 0;
}
//服务器端发送报文
int sckServer_send(int connfd, int timeout, unsigned char *data, int datalen)
{
    int     ret = 0;
    
    ret = write_timeout(connfd, timeout);
    if (ret == 0)
    {
        int writed = 0;
        int netlen = 0;
        unsigned char *netdata = ( unsigned char *)malloc(datalen + 4);
        if ( netdata == NULL)
        {
            ret = Sck_ErrMalloc;
            Socket_Log(__FILE__, __LINE__,SocketLevel[4], ret,"func malloc() err");
            return ret;
        }

        netlen = htonl(datalen);
        memcpy(netdata, &netlen, 4);
        memcpy(netdata+4, data, datalen);
        
        //writed = writen(connfd, netdata, datalen + 4);
        writed = send(connfd, (const char *)netdata, datalen + 4, 0);
        if (writed < (datalen + 4) )
        {
            if (netdata != NULL) 
            {
                free(netdata);
                netdata = NULL;
            }
            return writed;
        }
          
    }
    
    if (ret < 0)
    {
        //失败返回-1,超时返回-1并且errno = ETIMEDOUT
        if (ret == -1 && errno == ETIMEDOUT)
        {
            ret = Sck_ErrTimeOut;
            Socket_Log(__FILE__, __LINE__,SocketLevel[4], ret,"func sckServer_send() err");
            return ret;
        }
        return ret;
    }
    
    return ret;
}

//服务器端端接受报文
int sckServer_rev(int  connfd, int timeout, unsigned char **out, int *outlen)
{
        
    int        ret = 0;
    unsigned char *tmpBuf = NULL;
    int netdatalen = 0;
    int n;
    
    if (out==NULL || outlen==NULL)
    {
        ret = Sck_ErrParam;
        Socket_Log(__FILE__, __LINE__,SocketLevel[4], ret,"func sckServer_rev() err, check (out==NULL || outlen==NULL)");
        return ret;
    }
    
    ret =  read_timeout(connfd, timeout); //bugs modify bombing
    if (ret != 0)
    {
        if (ret==-1 || errno == ETIMEDOUT)
        {
            ret = Sck_ErrTimeOut;
            Socket_Log(__FILE__, __LINE__,SocketLevel[4], ret,"func read_timeout() err");
            return ret;
        }
        else
        {
            Socket_Log(__FILE__, __LINE__,SocketLevel[4], ret,"func read_timeout() err");
            return ret;
        }    
    }
    
    
    ret = readn(connfd, &netdatalen,  4); //读包头 4个字节
    if (ret == -1)
    {
        Socket_Log(__FILE__, __LINE__,SocketLevel[4], ret,"func readn() err");
        return ret;
    }
    else if (ret < 4)
    {
        ret = Sck_ErrPeerClosed;
        Socket_Log(__FILE__, __LINE__,SocketLevel[4], ret,"func readn() err, peer closed");
        return ret;
    }
    

    n = ntohl(netdatalen);
    
    tmpBuf = (unsigned char *)malloc(n+1);
    if (tmpBuf == NULL)
    {
        ret = Sck_ErrMalloc;
        Socket_Log(__FILE__, __LINE__,SocketLevel[4], ret,"func malloc() err");
        return ret;    
    }
    
    ret = readn(connfd, tmpBuf, n); // 服务端 根据长度读数据
    if (ret == -1)
    {
        Socket_Log(__FILE__, __LINE__,SocketLevel[4], ret,"func readn() err");
        return ret;
    }
    else if (ret < n)
    {
        ret = Sck_ErrPeerClosed;
        Socket_Log(__FILE__, __LINE__,SocketLevel[4], ret,"func readn() err. peer closed");
        return ret;
    }
    
    *out = tmpBuf; //间接赋值
    *outlen = n;
    tmpBuf[n] = '';
    
    return 0;
}

//服务器端 关闭连接
int sckServer_close(int connfd)
{
    //close(connfd);    
    closesocket(connfd);
    return 0;
}

//服务器端环境释放 
int sckServer_destroy()
{
    return 0;
}
socketutil.cpp

2、图形客户端-物理组件集成

》3大物理组件集成:

(1)拷贝“报文编解码_win”目录下的(messagereal.dll、messagereal.lib、keymng_msg.h)3个文件到项目所在的目录,然后剪切messagereal.dll到项目目录的上一级目录的Debug目录下;然后在项目“MyAdmin”右键选择“添加现有项”添加keymng_msg.h文件;

(2)在“资源视图”中项目“MyAdmin”右键选择“属性”,在左侧选择“链接器”的“输入”,然后在右侧“附加依赖项”的下拉框中编辑,添加:messagereal.lib,然后点击“确定”;

(3)拷贝“共享内存_win_Linux”目录下的(myipc_shm.h、myipc_shm.cpp)2个文件到项目所在的目录;然后在项目“MyAdmin”右键选择“添加现有项”添加这2个文件;

(4)拷贝“socket通信_win_Linux”目录下的(poolsocket.h、poolsocket.cpp、socketlog.h、socketlog.cpp、socketutil.h、socketutil.cpp)6个文件到项目所在的目录;然后在项目“MyAdmin”右键选择“添加现有项”添加这6个文件;

》将 Linux 业务代码移植到win下:

(1)wind新建文件夹“Linux-src”添加与客户端相关的源码:keymng_shmop.h、keymng_shmop.c、keymngclientop.h、keymngclientop.c、keymnglog.h、keymnglog.c

(2)为支持VS的MFC中的cpp格式,所以把*.c改为*.cpp(如:keymng_shmop.c改为keymng_shmop.cpp);

(3)拷贝“Linux-src”目录下的(keymng_shmop.h、keymng_shmop.cpp、keymngclientop.h、keymngclientop.cpp、keymnglog.h、keymnglog.cpp)6个文件到项目所在的目录;然后在项目“MyAdmin”右键选择“添加现有项”添加这6个文件;

3、图形客户端-Linux源码对接错误调试

(1)直接生成,项目报错如下:

解决: 去除*.cpp文件 #include 中 Linux 专用 头文件;添加:

#define  _CRT_SECURE_NO_WARNINGS
#include "stdafx.h"

>keymng_shmop.cpp:

>keymngclientop.cpp:

>keymnglog.cpp:


(2)报错:未定义标识符:

1)未定义标识符“open”:

解决:keymnglog.cpp中open(fileName, O_WRONLY|O_CREAT|O_APPEND, 066);  →   (int) fopen(fileName,"w+");

 

2)未定义标识符“write”:

先注释掉,待解决;

3)未定义标识符“write”:

解决:keymnglog.cpp中close(fp);   →  fclose((int)pf);

(3)报错:表达式必须是指向完整对象类型的指针,无法从“void”转换为“NodeSHMInfo”:

解决:keymng_shmop.cpp中pNode = mapaddr + i*sizeof(NodeSHMInfo);   →    pNode = (NodeSHMInfo *)mapaddr + i*sizeof(NodeSHMInfo);

keymng_shmop.cpp中90行、104行、146行共需要修改3处。

(4)报错:ICLevelName已经在keymnglog.obj中定义,找到一个或多个多重定义的符号;

解决:keymnglog.cpp中把ICLevelName修改为ICKeyMngLogName

keymnglog.cpp中33行、88行共需要修改2处。

4、图形客户端-系统初始化

(1)修改“IDD_DIALOG_CLIENT”中 密钥协商按钮ID为:IDC_BUTTON_AGREE;密钥校验按钮ID为:IDC_BUTTON_CHECK;密钥注销按钮ID为:IDC_BUTTON_REVOKE;系统初始化按钮ID为:IDC_BUTTON_SYSINIT;

(2)双击按钮,设置系统初始化的回调函数void CViewClient::OnBnClickedButtonSysinit()

》注意:1)在ViewClient.cpp添加头文件#include "keymngclientop.h"

2)定义全局的MngClient_Info pCltInfo;

3)更改keymngclientop.cpp中ip:strcpy(pCltInfo->serverip, "192.168.22.251");

附:密钥协商、密钥校验、密钥注销、系统初始化 回调函数

>ViewClient.h

#pragma once
#include "afxcmn.h"

// CViewClient 窗体视图

class CViewClient : public CFormView
{
    DECLARE_DYNCREATE(CViewClient)

protected:
    CViewClient();           // 动态创建所使用的受保护的构造函数
    virtual ~CViewClient();

public:
    enum { IDD = IDD_DIALOG_CLIENT };
#ifdef _DEBUG
    virtual void AssertValid() const;
#ifndef _WIN32_WCE
    virtual void Dump(CDumpContext& dc) const;
#endif
#endif

protected:
    virtual void DoDataExchange(CDataExchange* pDX);    // DDX/DDV 支持

    DECLARE_MESSAGE_MAP()
public:
    virtual void OnInitialUpdate();
    CListCtrl m_listSecNode;

public:
    CImageList        m_imageList;
    int CViewClient::DbInitListSecNode(CString &clientid, CString &serverid, int keyid, int state, CTime &time);
    afx_msg void OnBnClickedButtonAgree();
    afx_msg void OnBnClickedButtonSysinit();
    afx_msg void OnBnClickedButtonCheck();
};
ViewClient.h

>ViewClient.cpp

// ViewClient.cpp : 实现文件
//

#include "stdafx.h"
#include "MyAdmin.h"
#include "ViewClient.h"
#include "keymngclientop.h"
#include "keymng_shmop.h"


// CViewClient

IMPLEMENT_DYNCREATE(CViewClient, CFormView)

CViewClient::CViewClient()
    : CFormView(CViewClient::IDD)
{

}

CViewClient::~CViewClient()
{
}

void CViewClient::DoDataExchange(CDataExchange* pDX)
{
    CFormView::DoDataExchange(pDX);
    DDX_Control(pDX, IDC_LIST_SECNODE, m_listSecNode);
}

BEGIN_MESSAGE_MAP(CViewClient, CFormView)

    ON_BN_CLICKED(IDC_BUTTON_AGREE, &CViewClient::OnBnClickedButtonAgree)
    ON_BN_CLICKED(IDC_BUTTON_SYSINIT, &CViewClient::OnBnClickedButtonSysinit)
    ON_BN_CLICKED(IDC_BUTTON_CHECK, &CViewClient::OnBnClickedButtonCheck)
END_MESSAGE_MAP()


// CViewClient 诊断

#ifdef _DEBUG
void CViewClient::AssertValid() const
{
    CFormView::AssertValid();
}

#ifndef _WIN32_WCE
void CViewClient::Dump(CDumpContext& dc) const
{
    CFormView::Dump(dc);
}
#endif
#endif //_DEBUG

// CViewClient 消息处理程序

int CViewClient::DbInitListSecNode(CString &clientid, CString &serverid, int keyid, int state, CTime &time)
{
    // TODO:  在此添加控件通知处理程序代
    LVITEM   lvi;
    lvi.mask = LVIF_IMAGE | LVIF_TEXT;
    lvi.iItem = 0;    //在第几行上插入数据 始终头插法
    lvi.iImage = 4;

    //插入第0列数据
    lvi.iSubItem = 0;        // Set subitem 0
    lvi.pszText = (LPTSTR)(LPCTSTR)clientid;
    m_listSecNode.InsertItem(&lvi);

    ////插入第1列数据
    lvi.iSubItem = 1;        // Set subitem 1
    lvi.pszText = (LPTSTR)(LPCTSTR)serverid;
    m_listSecNode.SetItem(&lvi);

    char buf[128] = { 0 };
    sprintf(buf, "%d", keyid);
    ////插入第2列数据
    lvi.iSubItem = 2;        // Set subitem 1
    lvi.pszText = (LPTSTR)(LPCTSTR)buf;
    m_listSecNode.SetItem(&lvi);

    //插入第3列数据
    lvi.iSubItem = 3;        // Set subitem 3
    if (state == 1)
    {
        lvi.pszText = "禁用";

    }
    else {
        lvi.pszText = "正常";
    }
    m_listSecNode.SetItem(&lvi);


    //插入第4列数据
    CString strTime = time.Format("%Y-%m-%d %H:%M:%S");
    lvi.iSubItem = 4;        // Set subitem 4
    //CString strAuthcode(authcode) ;
    lvi.pszText = (LPTSTR)(LPCTSTR)strTime;
    m_listSecNode.SetItem(&lvi);
    return 0;
}


void CViewClient::OnInitialUpdate()
{
    CFormView::OnInitialUpdate();
    HIMAGELIST hList = ImageList_Create(16, 16, ILC_COLOR8 | ILC_MASK, 8, 1);
    m_imageList.Attach(hList);

    CBitmap cBmp;
    cBmp.LoadBitmap(IDB_BITMAP_SECNODE);
    m_imageList.Add(&cBmp, RGB(255, 0, 255));
    cBmp.DeleteObject();

    m_listSecNode.SetImageList(&m_imageList, LVSIL_SMALL);


    DWORD dwExStyle = ListView_GetExtendedListViewStyle(m_listSecNode.m_hWnd);
    dwExStyle |= LVS_EX_FULLROWSELECT | LVS_EX_GRIDLINES;
    ListView_SetExtendedListViewStyle(m_listSecNode.m_hWnd, dwExStyle);


    CRect rect; //msdn
    m_listSecNode.GetClientRect(&rect);
    int nColInterval = rect.Width() / 5;

    m_listSecNode.SetRedraw(FALSE);
    m_listSecNode.InsertColumn(0, "Client编号", LVCFMT_LEFT, nColInterval);
    m_listSecNode.InsertColumn(1, "Server编号", LVCFMT_LEFT, nColInterval);
    m_listSecNode.InsertColumn(2, "当前密钥KeyId", LVCFMT_LEFT, nColInterval);
    m_listSecNode.InsertColumn(3, "密钥状态", LVCFMT_LEFT, nColInterval);
    m_listSecNode.InsertColumn(4, "请求时间", LVCFMT_LEFT, rect.Width() - 4 * nColInterval);
    m_listSecNode.SetRedraw(TRUE);

    CString clientid = "1111";
    CString serverid = "0001";
    int keyid = 105;
    int state = 0;
    CTime time = COleDateTime::GetCurrentTime();

    DbInitListSecNode(clientid, serverid, keyid, state, time);

}

MngClient_Info pCltInfo;

void CViewClient::OnBnClickedButtonAgree()
{
    // TODO: 在此添加控件通知处理程序代码
    int ret = 0;
    ret = MngClient_Agree(&pCltInfo);
    if (ret != 0)
    {
        AfxMessageBox("客户端密钥协商失败");
    }
    else
    {
        AfxMessageBox("客户端密钥协商成功");
    }
}

void CViewClient::OnBnClickedButtonSysinit()
{
    // TODO: 在此添加控件通知处理程序代码
    int ret = 0;

    ret = MngClient_InitInfo(&pCltInfo);
    if (ret != 0)
    {
        AfxMessageBox("客户端系统初始化失败");
    }
    else
    {
        AfxMessageBox("客户端系统初始化成功");
    }
}

void CViewClient::OnBnClickedButtonCheck()
{
    // TODO: 在此添加控件通知处理程序代码
    int ret = 0;

    ret = MngClient_Check(&pCltInfo);
    if (ret != 0)
    {
        AfxMessageBox("客户端密钥校验失败");
    }
    else
    {
        AfxMessageBox("客户端密钥校验成功");
    }

}
ViewClient.cpp

5、图形客户端-删除控件对应函数

问题抛出:如果添加按钮控件,未更改ID时,不小心双击了,就会生成OnBnClickedButton1()这样的回调函数,如何删除呢?以“IDD_DIALOG_CLIENT”界面添加按钮为例。

解决:(1)切换到“类视图”,然后找到“CViewClient”类,然后右键打开“类向导”,弹出对话框,点击“方法”,选中需要删除的“OnBnClickedButton1”,在左侧点击“删除方法”:

(2)在ViewClient.h中删除声明:

(3)在ViewClient.cpp中删除消息映射和回调函数:

6、图形客户端-密钥协商

(1)双击按钮,设置密钥协商的回调函数void CViewClient::OnBnClickedButtonAgree()

(2)测试:

1)在Linux启动服务器(和Oracle数据库)

注意:一定要启动服务器(和Oracle数据库)!

2)在“VS”点击运行,弹出MyAdmin项目窗口,然后左侧点击“SecMngClient协商客户端”,点击“SecMngClient协商”,然后在右侧先点击“系统初始化”,再点击“密钥协商”

报错:

点击“继续”:

点击“继续”,弹出“密钥协商ok”对话框:

查看数据库的SECKYEINFO表:(数据插入成功)

分析:由点击“继续”时项目定位的位置,知道这是内存释放错误的原因!

7、图形客户端-密钥协商内存释放错误说明

》内存释放:
    原则:谁开辟,谁释放。       
    创建库时,如有函数开辟了内存,必须提供对应的内存释放函数给用户。

原keymngclientop.cpp中MngClient_Agree密钥协商函数的内存释放函数为:

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);

检查后,更改为:

END:
    if (msgKey_Req_Data != NULL) 
        MsgMemFree((void **)&msgKey_Req_Data, 0);
        //sck_FreeMem((void**)&msgKey_Req_Data);
    if (msgKey_Res_Data != NULL) 
        //MsgMemFree((void **)&msgKey_Res_Data, 0);
        sck_FreeMem((void**)&msgKey_Res_Data);
    if (pStruct_Res != NULL) 
        MsgMemFree((void **)&pStruct_Res, iType);

再次按照流程测试,成功了!

可知:Linux释放内存程序检查的不够严格(虽然底层都是free实现),到wind的“VS”中检查比较严格,所以报错!

8、总结
》4大基础组件:
    1. 统一报文编码解码    libmessagereal.so .h ---> .dll .lib .h
    2. 统一通信组件socket    --- windows socket 通信        
    3. 共享内存        --- windows shm 机制
    4. 数据库访问    (客户端无需数据库)

》物理组件集成:

    统一报文编解码组件: 
        messagereal.lib 、messagereal.dll 、 keymng_msg.h
        集成动态库到项目中。 属性 → 配置属性 → 连接器 → 输入 → 附加依赖项 → 编辑 → messagereal.lib
        messagereal.dll 放置到 .exe 所在目录位置。
        messagereal.lib 放置到 .cpp 所在目录位置。

    共享内存组件:
        myipc_shm.cpp 、myipc_shm.h
        集成源码到项目中。 属性 → 配置属性 → C/C++ → 常规 → SDL检查 → “否(/sdl-)”

    Socket通信组件:
        poolsocket.cpp 、poolsocket.h 、 socketlog.cpp 、 socketlog.h 、 socketutil.cpp 、 socketutil.h
        集成源码到项目中。

-----将 Linux 业务代码移植到win下---------------------------------------------------
    添加与客户端相关的源码:
        keymng_shmop.c        → keymng_shmop.cpp
        keymngclient.c        是在Linux 下组织文字界面的,不需要。
        keymngclientop.c    → keymngclientop.cpp   
        keymnglog.c        → keymnglog.cpp       
        keymng_shmop.h
        keymngclientop.h
        keymnglog.h
        去除 cpp文件 #include 中 Linux 专用 头文件。
        添加    #define  _CRT_SECURE_NO_WARNINGS
            #include "stdafx.h"

    修改 源码对接平台差异错误:
        open(fileName,     O_WRONLY|O_CREAT|O_APPEND, 066)   →   (int) fopen(fileName,"w+")
        pNode = mapaddr + i*sizeof(NodeSHMInfo);   →    pNode = (NodeSHMInfo *)mapaddr + i*sizeof(NodeSHMInfo);
        ICLevelName    →    ICKeyMngLogName;

----- 整合图形客户端业务--------------------------------------------------------

    实现“系统初始化”Button 功能 :  ( initUpdate() )
        引入头文件:keymngclientop.h → ViewClient.cpp
        定义全局变量:MngClient_Info pCltInfo; → ViewClient.cpp  ---- 只读。
        调用:MngClient_InitInfo(&mngClientInfo); 初始化。    AfxMessage().显示错误信息。
        【注意】:修改 MngClient_InitInfo 中,服务器IP地址。
    实现“密钥协商”Button 功能:   
        调用:MngClient_Agree(&mngClientInfo); 协商密钥。AfxMessage().显示错误信息。
        【注意】:内存释放 MFC 中验证严格, ===== 把握:谁开辟,谁释放 原则。
        msgKey_Req_Data:是在报文编码过程中 使用 MsgEncode() 函数创建的内存。
                 应使用 MsgMemFree() 释放。 如:
                 MsgMemFree((void **)&msgKey_Req_Data, 0);
        msgKey_Res_Data:是在数据通信过程中 使用 sckClient_rev() 函数创建的内存。
                 应使用 sck_FreeMem() 释放。 如:
                 sck_FreeMem((void **)&msgKey_Res_Data);   

    实现“密钥校验”Button 功能:   
        调用:MngClient_Check(&mngClientInfo); 协商密钥。AfxMessage().显示错误信息。    

    实现“密钥注销”Button功能:   
        调用:MngClient_Revoke(&mngClientInfo); 协商密钥。AfxMessage().显示错误信息。   

》内存释放:
    原则:谁开辟,谁释放。       
    创建库时,如有函数开辟了内存,必须提供对应的内存释放函数给用户。       
    int poolget(int **cache(传出));      int free(int *cache(传入));

9、接口设计

》项目框架再回顾与分析:

所以,针对,app1和app2 需要提供两套接口(函数)!

》接口(函数)设计分析:

--------外联接口设计思想------------------------------------------------------
    .so/.dll 和 .h
    项目中外联接口主要有两个作用:    1. 读共享内存。    2. 加解密数据。

    函数接口:
        int DataEnc(char *clientid, char *serverid, unsigned char *indata, int indatalen, unsigned char *oudata, int *outdatalen);
        int DataDec(char *clientid, char * serverid, unsigned char *indata, int indatalen, unsigned char *outdata, int *outdatalen);
        int tag = 0 / 1; 加密, 解密。
        int DataEncAndDec(char *clientid, char *serverid, unsigned char *indata, int indatalen, unsigned char *outdata, int outdatalen);

    操作共享内存时, 需要使用到keyid。它可以以两种方式传入到函数接口中:
        1. 直接作为函数接口,传递到函数中。
            int DataEncAndDec(char *clientid, char *serverid, unsigned char *indata, int indatalen,
                      unsigned char *outdata, int outdatalen, int keyid, int maxnode);
        2. 接口读配置文件获取。
            配置文件应存放在: $(HOME)/etc/1.ini   e.g. KEYID=1111 、 MAXNODE=10
            这样,直接调接口效率会比较低。所以应封装初始化函数和结束函数给用户。
            int DataEncAndDec_init(char *filename);        //读配置文件,获取数据存入全局变量中。
            int DataEncAndDec(char *clientid, char *serverid, unsigned char *indata, int indatalen,
                      unsigned char *outdata, int outdatalen);
            int DataEncAndDec_finish();
    项目开发中,不同的用户需求,选用不同的接口封装方式,效率几近相同。没有孰优孰劣之分。

10、文件加密原理

=======对称加密:   加密秘钥和解密密钥一致==========================

    加密三要素:
        y = ax + b   明文、密文, 密钥, 算法。

   常见对称加密体系:
        AES    DES    3DES    SM3/n 
    分组加密原理:   
        采用分组加密的方式:对 不满足 8 字节部分,打padding。 但,从补丁明文恢复到原始明文时,有问题。无法区分原始明文和补丁明文。
        缺几补几。
        补丁长度 = 分组数据长度 - 数据长度 % 8   (缺8补8)    0-7   打补丁
            只对一整块数据,只做一次 padding 操作,        缺几补几。 

            大数据(大文件)要分块传输、存储。无论多大的数据,只对最后一个分组进行 padding 操作。在中间不能进行该操作。

            一般加密供应商给用户应提供两个API函数: 一个打padding的函数、一个不打padding的API函数。 

           大多数用户不了解加密过程,通常对其隐藏实现细节。只提供加密、解密接口。

》加密原理分析图:

》大文件 划分加密图:

11、文件加密函数源码

分析了DES源码(数据加密和解密)——cryptproj-文件加密项目(des.c、des.h、tmain.c)

12、数字证书

把DES源码封装,最后提供两套接口给用户:(appinterface/)

    //本地 加密
    ret = Appcryptapi(0, clientid, serverid, indata, indatalen, outdata, &outdatalen, cfg_shm_keyid, cfg_shm_maxnodenum);
    
    //本地 解密
    ret =  AppCryptApi(1, clientid, serverid, outdata, outdatalen, plain2, &plainlen2, cfg_shm_keyid, cfg_shm_maxnodenum);

 >appcryptapi.h

#ifndef _APP_CRYPT_API_H_
#define _APP_CRYPT_API_H_

#ifdef __cplusplus
extern "C" {
#endif


//crypttag = 0   加密
//crypttag = 1   解密
int AppCryptApi(int crypttag, char *clientid, char *serverid, unsigned char *indata, int indatalen, 
    unsigned char *outdata, int *outdatalen, int cfg_shm_keyid, int cfg_shm_maxnodenum);
    
#ifdef __cplusplus
}
#endif

#endif
appcryptapi.h

>appcryptapitest.c

#define _CRT_SECURE_NO_WARNINGS

#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include "appcryptapi.h"

int main(void)
{
    //加密报文
    int                ret            = 0;
    int                crypttag    = 0;
    char            *clientid    = "1111";
    char            *serverid    = "0001";

    unsigned char    *indata = (char *)"aaaaaaaa";
    int                indatalen = strlen((char *)indata);

    unsigned char    outdata[4096];
    int                outdatalen = 4096;

    unsigned char    plain2[4096];
    int                plainlen2 = 4096;

    int cfg_shm_keyid = 0x0001;
    int cfg_shm_maxnodenum = 20;

    
    //本地 加密
    ret = Appcryptapi(0, clientid, serverid, indata, indatalen, outdata, &outdatalen, cfg_shm_keyid, cfg_shm_maxnodenum);
    if (ret != 0)
    {
        printf("func AppCryptApi() err:%d 
", ret);
        goto End;
    }
    
    //本地 解密
    ret =  AppCryptApi(1, clientid, serverid, outdata, outdatalen, plain2, &plainlen2, cfg_shm_keyid, cfg_shm_maxnodenum);
    if (ret != 0)
    {
        printf("func AppCryptApi() err:%d 
", ret);
        goto End;
    }

    if (plainlen2 != indatalen)
    {
        printf("解密以后明文大小不一样
");
        goto End;
    }
    else
    {
        printf(" 解密以后明文大小 一样
");
    }
    if ( memcmp(plain2,indata, indatalen ) != 0)
    {
        printf("校验解密后 明文不一样
");
        goto End;
    }
    else
    {
        printf("校验解密后 明文一样
");
    }

}
appcryptapitest.c

然后结合 cryptproj-文件加密项目 进行编译

新建目录appinterfaceso,然后进入该目录,新建incl目录(des.h、myipc_shm.h)、src目录(appcryptapi.c、des.c、myipc_shm.c)和makefile。

>makefile

# Makefile Module For Develop Team

.SUFFIXES:
.SUFFIXES:  .c .o

WORKDIR=.
INCLDIR=$(WORKDIR)/incl
LIBDIR=$(HOME)/lib
BINDIR=$(HOME)/bin

CC=gcc

INCLFLG= -I$(INCLDIR)
LIBFLG = -L$(LIBDIR)
CFLAG= -c -g $(INCLFLG) 
LIBS = 

VPATH = $(WORKDIR)/src

OBJ1 = appcryptapi.o  des.o  myipc_shm.o


libappinterface.so: $(OBJ1) 
    $(CC) -shared -fPIC $^  -o $@ 
    @cp $@ $(LIBDIR)

.c.o:
    $(CC) -shared -fPIC $(CFLAG) $< -D_ITCAST_OS_LINUX

#gcc       -shared -fPIC -c -g -I./incl   *.c--->*.o
    
.PHONY : clean
clean :
    rm  -f *.o
    rm  -f *.s
makefile

》补充:

=======非对称加密=================================================

    特征:加密密钥 和 解密密钥,不一样。 密钥对(公钥、私钥)。   

    银行开户:
        1)提交身份证
        2)柜员 审核 ---> 人为管理
            1. 调用银行内部密钥生成系统,得到密钥对(公钥、私钥)。
            2. 将私钥灌入 KEY 中。(网银key、U盾)
            3. 将 公钥 + 个人身份信息 ---> 数字证书。     (包含“公钥”和“个人身份信息”两部分内容)

    数字证书:
        简单理解成“网络身份证”。解决了虚拟世界(网络世界)中,两端之间身份识别问题。

    示例:
        360 → 菜单 → “选项” → “高级设置” → “管理证书”
        MicroSoft edge → 菜单 → “使用 Internet Explorer 打开” → “工具” → “Internet 选项” → “内容”标签页 → “证书”
        可导出证书,选择.BER格式 或者 base64编码格式 (将DER编码后得到的可见字符格式)。

    查看证书:
        详细信息中,包含公钥和使用者相关身份信息。

    公钥的作用:
        1)验证身份                B → 验证 → A
            1. A 产生随机数 r1, 用私钥加密 r1 → r1S                        (签名)
            2. A 将 r1和r1S 给 B。 则B有了明文:r1 和 密文:r1S
            3. B 从公共网点上下载 A 的证书。 若 B 无法下载,A也可以将自己的证书一起给B。
            4. B 用 A的证书中的“公钥”,解密 密文:r1S → r2 。 校验 r1 和 r2 是否相等。         (验证签名)
            5. 若相等,则能确定,数据加密动作,是 A 完成的。
            签名,类似公司加盖公章。具有法律效力(电子签名法)。   
        2)数据加密:                C 、 D
            1. C 用 D 的公钥加密,将加密数据给D
            2. D 用自己的私钥解密。

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

原文地址:https://www.cnblogs.com/Alliswell-WP/p/CPlusPlus_SecureTransmissionPlatform_Project11.html