1 需求
一般的企业信息系统都有成熟的框架。软件框架一般不发生变化,能自由的集成第三方厂商的产品。假如要在企业信息系统框架中集成第三方厂商的Socket通信产品和第三方厂商加密产品,其中Socket通信产品用于完成两点间的通信,加密产品用于数据发送时加密,以及数据接收时的解密。
下面是Socket通信产品的框架(文件名CSocketProtocol.h):
#pragma once
#include <iostream>
using namespace std;
class CSocketProtocol //该类定义了两个点消息收发的接口
{
public:
CSocketProtocol()
{
;
}
virtual ~CSocketProtocol() //虚析构函数的细节
{
;
}
//客户端环境的初始化
virtual int cltSocketInit() = 0;
//客户端发报文
virtual int cltSocketSend(unsigned char *buf /*in*/, int buflen /*in*/) = 0;
//客户端收报文
virtual int cltSocketRev(unsigned char *buf /*in*/, int *buflen /*in out*/) = 0;
//客户端释放资源
virtual int cltSocketDestory() = 0;
};
这是一个抽象类,里面的函数都声明为了纯虚函数,析构函数之所以声明为抽象函数,是因为在多态的时候,可以通过父类指针或引用释放子类对象。
加密产品的框架如下(CEncDesProtocol.h):
#pragma once
class CEncDesProtocol //该类定义了加密接口
{
public:
CEncDesProtocol()
{
}
virtual ~CEncDesProtocol()
{
}
virtual int EncData(unsigned char *plain, int plainlen, unsigned char *cryptdata, int *cryptlen) = 0; //加密
virtual int DecData(unsigned char *cryptdata, int cryptlen, unsigned char *plain, int *plainlen) = 0; //解密
};
CSocketProtocol
和CEncDesProtocol
中的纯虚函数,由第三方厂商写的子类实现。
2 第三方厂商实现Socket通信产品
假如有两家公司入围了,他么的名字分别是Factory1和Factory2。
(1)Factory1
类的声明如下(文件名 CSckFactoryImp1.h):
#pragma once
#include <iostream>
using namespace std;
#include "CSocketProtocol.h"
//厂商1定义的类,该类实现了抽象类的接口
class CSckFactoryImp1 : public CSocketProtocol
{
public:
//客户端初始化
virtual int cltSocketInit( /*out*/);
//客户端发报文
virtual int cltSocketSend( unsigned char *buf /*in*/, int buflen /*in*/);
//客户端收报文
virtual int cltSocketRev( unsigned char *buf /*in*/, int *buflen /*in out*/);
//客户端释放资源
virtual int cltSocketDestory();
private:
unsigned char *p;
int len ;
};
类中有两个私有的成员变量p
和len
,其中p
用来存储报文内容首地址,len
用来存储报文长度。
当指针作为函数的参数时,往往还需要一个表示长度的参数,用于防止数组越界。
类的实现如下(文件名 CSckFactoryImp1.cpp):
#include <iostream>
using namespace std;
#include "CSckFactoryImp1.h"
//客户端初始化
int CSckFactoryImp1::cltSocketInit( /*out*/) //实现虚函数的时候,可以不加virtual
{
p = NULL;
len = 0;
return 0;
}
//客户端发报文
int CSckFactoryImp1::cltSocketSend(unsigned char* buf /*in*/, int buflen /*in*/)
{
p = (unsigned char*)malloc(sizeof(unsigned char) * buflen);
if (p == NULL)
{
return -1;
}
memcpy(p, buf, buflen);
len = buflen;
return 0;
}
//客户端收报文
int CSckFactoryImp1::cltSocketRev(unsigned char* buf /*in*/, int* buflen /*in out*/)
{
if (buf == NULL || buflen == NULL)
{
return -1;
}
*buflen = this->len;
memcpy(buf, this->p, this->len);
return 0;
}
//客户端释放资源
int CSckFactoryImp1::cltSocketDestory()
{
if (p != NULL)
{
free(p);
p = NULL;
len = 0;
}
return 0;
}
这里解释一下各个成员函数
cltSocketInit()
函数实现客户端环境的初始化,这主要是对CSckFactoryImp1
类中的两个成员变量进行赋值,有点类似于构造函数,但构造函数只能在创建对象时调用,这里的初始化函数可以在创建对象后调用,某些时候可以实现“重置”的功能
cltSocketSend(unsigned char* buf /*in*/, int buflen /*in*/)
用于发送报文,这里的in表示数据传递方向,参数除了可以从函数外向函数传递数据(in),还可以向外传递数据(out),例如参数为指针的时候。这里参数buf是报文字符串的首地址, buflen是要发送的报文长度,这个函数的作用是:开辟一块空间,并把报文复制到这段内存空间中。这个函数执行后,类CSckFactoryImp1的成员变量p将指向报文所在空间,len则是报文的长度。
cltSocketRev(unsigned char* buf /*in*/, int* buflen /*in out*/)
用于客户端接收报文,实际就是把类中的两个成员变量的值给发送出去,通过函数参数向外传递数据。
cltSocketDestory()
客户端释放资源,因为CSckFactoryImp1类中存在指针类型的成员变量,这里需要释放资源。
(2)Factory2
这里只是为了演示多个厂商入围的情形,所以Factory2提供的类,仅仅是类名与Factory1不一致,成员函数和成员变量与Factory1一致
类的声明如下(文件名CSckFactoryImp2.h)
#pragma once
#include <iostream>
using namespace std;
#include "CSocketProtocol.h"
class CSckFactoryImp2 : public CSocketProtocol
{
public:
//客户端初始化 获取handle上下
virtual int cltSocketInit( /*out*/);
//客户端发报文
virtual int cltSocketSend(unsigned char* buf /*in*/, int buflen /*in*/);
//客户端收报文
virtual int cltSocketRev(unsigned char* buf /*in*/, int* buflen /*in out*/);
//客户端释放资源
virtual int cltSocketDestory();
private:
unsigned char* p;
int len;
};
类的实现如下(文件名 CSckFactoryImp2.cpp):
#include <iostream>
using namespace std;
#include "CSckFactoryImp2.h"
//客户端初始化 获取handle上下
int CSckFactoryImp2::cltSocketInit( /*out*/)
{
p = NULL;
len = 0;
return 0;
}
//客户端发报文
int CSckFactoryImp2::cltSocketSend(unsigned char* buf /*in*/, int buflen /*in*/)
{
p = (unsigned char*)malloc(sizeof(unsigned char) * buflen);
if (p == NULL)
{
return -1;
}
memcpy(p, buf, buflen);
len = buflen;
return 0;
}
//客户端收报文
int CSckFactoryImp2::cltSocketRev(unsigned char* buf /*in*/, int* buflen /*in out*/)
{
if (buf == NULL || buflen == NULL)
{
return -1;
}
*buflen = this->len;
memcpy(buf, this->p, this->len);
return 0;
}
//客户端释放资源
int CSckFactoryImp2::cltSocketDestory()
{
if (p != NULL)
{
free(p);
p = NULL;
len = 0;
}
return 0;
}
(3)测试
可以写一个测试文件(文件名socket_test.cpp),来对两个厂商提交的产品进行测试:
#define _CRT_SECURE_NO_WARNINGS
#include <iostream>
using namespace std;
#include "CSocketProtocol.h"
#include "CSckFactoryImp1.h"
#include "CSckFactoryImp2.h"
//面向抽象类编程,框架实现完毕
//用于收发报文
int SckSendAndRec01(CSocketProtocol* sp, unsigned char* in, int inlen, unsigned char* out, int* outlen)
{
int ret = 0;
ret = sp->cltSocketInit(); //客户端初始化
if (ret != 0)
{
goto End;
}
ret = sp->cltSocketSend(in, inlen); //客户端发送报文
if (ret != 0)
{
goto End;
}
ret = sp->cltSocketRev(out, outlen); //客户端发送报文
if (ret != 0)
{
goto End;
}
End:
ret = sp->cltSocketDestory(); //客户端释放资源
return 0;
}
//写一个框架
int main()
{
int ret = 0;
unsigned char in[4096];
int inlen;
unsigned char out[4096];
int outlen = 0;
strcpy((char*)in, "aadddddddddddaaaaaaaaaaa");
inlen = 9;
CSocketProtocol* sp = NULL;
//sp = new CSckFactoryImp1 //使用Factory1提供的类
sp = new CSckFactoryImp2; //使用Factory2提供的类
ret = SckSendAndRec01(sp, in, inlen, out, &outlen);
if (ret != 0)
{
printf("func SckSendAndRec() err:%d \n", ret);
return ret;
}
delete sp; //想通过父类指针 释放所有的子类对象的资源 ..
cout << "hello..." << endl;
system("pause");
return ret;
}
3 第三方厂商实现加密产品
这里为了简便,假设加密产品只有一家公司入围(假设这家公司的名称为Hw)
(1)实现需求
他们写的类声明如下(文件名HwEncDec.h):
#pragma once
#include <iostream>
using namespace std;
#include "CEncDesProtocol.h"
class HwEncDec : public CEncDesProtocol
{
public:
virtual int EncData(unsigned char* plain, int plainlen, unsigned char* cryptdata, int* cryptlen);
virtual int DecData(unsigned char* cryptdata, int cryptlen, unsigned char* plain, int* plainlen);
};
类的定义如下:
#include <iostream>
using namespace std;
#include "HwEncDec.h"
#include "des.h"
int HwEncDec::EncData(unsigned char* plain, int plainlen, unsigned char* cryptdata, int* cryptlen)
{
int ret = 0;
//用户使用的函数
ret = DesEnc(plain, plainlen, cryptdata, cryptlen);
if (ret != 0)
{
printf("func DesEnc() err:%d \n ", ret);
return ret;
}
return ret;
}
int HwEncDec::DecData(unsigned char* cryptdata, int cryptlen, unsigned char* plain, int* plainlen)
{
int ret = 0;
//用户使用函数des解密
ret = DesDec(cryptdata, cryptlen, plain, plainlen);
if (ret != 0)
{
printf("func DesDec() err:%d \n ", ret);
return ret;
}
return ret;
}
这里导入了一个名为des.h的文件,它是对所用加密、解密算法的声明,也就是说,类在实现的时候使用了之前写过的算法。des.h代码如下:
/*********************************************************
* des.h
* 用户使用des算法头文件
*
*********************************************************/
#ifndef _OPENDESS_H_
#define _OPENDESS_H_
#ifdef __cplusplus
extern "C" {
#endif
//ab\0defg
//用户使用的函数
int DesEnc(
unsigned char* pInData,
int nInDataLen,
unsigned char* pOutData,
int* pOutDataLen);
//用户使用函数des解密
int DesDec(
unsigned char* pInData,
int nInDataLen,
unsigned char* pOutData,
int* pOutDataLen);
#ifdef __cplusplus
}
#endif
#endif
这里#ifdef __cplusplus
表示:判断当前的环境是否为C++编译环境
extern "C" {...}
表示:花括号中的内容按C语言的语法进行编译和链接
也就是说,这里调用了C语言编写的函数。
加密解密算法写在了des.cpp中(代码太长,有800多行,这里略过),虽然后缀是cpp,但是因为C++兼容C,所以还是可以正常使用
(2)测试
需求实现后,就是测试了,测试代码如下(socket_DecEnc_test.cpp):
#define _CRT_SECURE_NO_WARNINGS
#include <iostream>
using namespace std;
#include "CSocketProtocol.h"
#include "CSckFactoryImp1.h"
#include "CSckFactoryImp2.h"
#include "CEncDesProtocol.h"
#include "HwEncDec.h"
//面向抽象类编程,框架实现完毕
//c函数
int SckSendAndRec_EncDec(CSocketProtocol* sp, CEncDesProtocol* ed, unsigned char* in, int inlen, unsigned char* out, int* outlen)
{
int ret = 0;
unsigned char data[4096];
int datalen = 0;
ret = sp->cltSocketInit();
if (ret != 0)
{
goto End;
}
ret = ed->EncData(in, inlen, data, &datalen);
if (ret != 0)
{
goto End;
}
ret = sp->cltSocketSend(data, datalen); //发送数据之前对数据加密 ..
if (ret != 0)
{
goto End;
}
ret = sp->cltSocketRev(data, &datalen); //收到的数据是密文,需要进行解密
if (ret != 0)
{
goto End;
}
ret = ed->DecData(data, datalen, out, outlen);
if (ret != 0)
{
goto End;
}
End:
ret = sp->cltSocketDestory();
return 0;
}
//写一个框架
int main()
{
int ret = 0;
unsigned char in[4096];
int inlen;
unsigned char out[4096];
int outlen = 0;
strcpy((char*)in, "aadddddddddddaaaaaaaaaaa");
inlen = 9;
CSocketProtocol* sp = NULL;
CEncDesProtocol* ed = NULL;
//sp = new CSckFactoryImp1
sp = new CSckFactoryImp2; //
ed = new HwEncDec;
ret = SckSendAndRec_EncDec(sp, ed, in, inlen, out, &outlen);
if (ret != 0)
{
printf("func SckSendAndRec() err:%d \n", ret);
return ret;
}
delete sp; //想通过父类指针 释放所有的子类对象的资源 ..
cout << "hello..." << endl;
system("pause");
return ret;
}
至此,加密产品也通过了测试
4 测试框架类
我们前面的测试,框架都是函数,这是C语言的思维,真正的面向对象思维,框架应该是一个类,我们来实现一下这个类。
#define _CRT_SECURE_NO_WARNINGS
#include <iostream>
using namespace std;
#include "CSocketProtocol.h"
#include "CSckFactoryImp1.h"
#include "CSckFactoryImp2.h"
#include "CEncDesProtocol.h"
#include "HwEncDec.h"
class MainOp
{
public:
MainOp()
{
this->sp = NULL;
this->ed = NULL;
}
MainOp(CSocketProtocol* sp, CEncDesProtocol* ed)
{
this->sp = sp;
this->ed = ed;
}
void setSp(CSocketProtocol* sp)
{
this->sp = sp;
}
void setEd(CEncDesProtocol* ed)
{
this->ed = ed;
}
public:
int SckSendAndRec_EncDec3(unsigned char* in, int inlen, unsigned char* out, int* outlen)
{
int ret = 0;
unsigned char data[4096];
int datalen = 0;
ret = this->sp->cltSocketInit();
if (ret != 0)
{
goto End;
}
ret = this->ed->EncData(in, inlen, data, &datalen);
if (ret != 0)
{
goto End;
}
ret = this->sp->cltSocketSend(data, datalen); //发送数据之前对数据加密 ..
if (ret != 0)
{
goto End;
}
ret = sp->cltSocketRev(data, &datalen); //收到的数据是密文,需要进行解密
if (ret != 0)
{
goto End;
}
ret = ed->DecData(data, datalen, out, outlen);
if (ret != 0)
{
goto End;
}
End:
ret = sp->cltSocketDestory();
return 0;
}
private:
CSocketProtocol* sp;
CEncDesProtocol* ed;
};
//写一个框架
int main()
{
int ret = 0;
unsigned char in[4096];
int inlen;
unsigned char out[4096];
int outlen = 0;
strcpy((char*)in, "aadddddddddddaaaaaaaaaaa");
inlen = 9;
MainOp* myMainOp = new MainOp; //调用无参构造函数
CSocketProtocol* sp = NULL;
CEncDesProtocol* ed = NULL;
//sp = new CSckFactoryImp1
sp = new CSckFactoryImp2;
ed = new HwEncDec;
//将第三方厂商的产品注入到框架
myMainOp->setSp(sp);
myMainOp->setEd(ed);
ret = myMainOp->SckSendAndRec_EncDec3(in, inlen, out, &outlen);
if (ret != 0)
{
printf("myMainOp SckSendAndRec_EncDec3() err\n ", ret);
}
delete sp;
delete ed;
delete myMainOp;
cout << "hello..." << endl;
system("pause");
return ret;
}