/*
*运行平台:Win10,Visual Studio 2015
*参考资料:《C Primer plus 第六版》,传智扫地增C提高课程
*/
动态链接库,英文为DLL,是Dynamic Link Library 的缩写形式,DLL是一个包含可由多个程序同时使用的代码和数据的库,DLL不是可执行文件。
动态库在程序编译时并不会被链接到目标文件中,而是在程序运行时才会被载入。不同的应用程序如果需要调用相同的库,那么内存只需要有一份该共享库的实例。
静态链接库,英文名LIB,在链接步骤中,连接器将从库文件取得所需的代码,复制到生成的可执行文件中的这种库。
动态库 | 静态库 | |
---|---|---|
生成阶段 | 链接阶段没有被复制到程序中,而是程序在运行时由系统动态加载到内存中供程序调用 | 在程序的链接阶段被复制到了程序中,和程序运行的时候没有关系 |
内容 | 包含实际的函数和数据 | 一般是一些索引信息,记录了DLL中函数的入口和位置 |
使用动态链接时:应用程序会使用LIB文件链接到DLL 文件。在应用程序的可执行文件中,存放的不是被调用的函数代码,而是DLL中相应函数代码的地址,从而节省了内存资源。
所以在使用动态链接时会同时需要到lib文件和dll文件。
通过调用动态库,模拟服务器接受和发送数据,并以文件日志的方式打印错误信息。
如图所示:
代码如下:
需要添加__declspec(dllexport)
,才可以使动态库中的函数导出,即在外部程序中使用动态库的函数。
#define _CRT_SECURE_NO_WARNINGS
#include
#include
#include
//客户端初始化 获取handle上下
__declspec(dllexport)
int cltSocketInit(void **handle /*out*/)
{
return 0;
}
//客户端发报文
__declspec(dllexport)
int cltSocketSend(void *handle /*in*/, unsigned char *buf /*in*/, int buflen /*in*/)
{
return 0;
}
//客户端收报文
__declspec(dllexport)
int cltSocketRev(void *handle /*in*/, unsigned char *buf /*in*/, int *buflen /*in out*/)
{
return 0;
}
//客户端释放资源
__declspec(dllexport)
int cltSocketDestory(void *handle/*in*/)
{
return 0;
}
此时,在工程文件中的Debug目录下可以找到如下dll,lib文件
添加main.c和socketclientdll.h文件
socketclientdll.h内容如下:
#ifndef _INC_Demo01_H
#define _INC_Demo01_H
#ifdef __cplusplus
extern "C" {
#endif
//------------------第一套api接口---Begin--------------------------------//
//客户端初始化 获取handle上下
int cltSocketInit(void **handle /*out*/);
//客户端发报文
int cltSocketSend(void *handle /*in*/, unsigned char *buf /*in*/, int buflen /*in*/);
//客户端收报文
int cltSocketRev(void *handle /*in*/, unsigned char *buf /*in*/, int *buflen /*in out*/);
//客户端释放资源
int cltSocketDestory(void *handle/*in*/);
//------------------第一套api接口---End-----------------------------------//
//------------------第二套api接口---Begin--------------------------------//
//客户端初始化 获取handle上下
int cltSocketInit2(void **handle);
//客户端发报文
int cltSocketSend2(void *handle, unsigned char *buf, int buflen);
//客户端收报文
int cltSocketRev2(void *handle, unsigned char **buf, int *buflen);
int cltSocketRev2_Free(unsigned char **buf);
//客户端释放资源
int cltSocketDestory2(void **handle);
//------------------第二套api接口---End--------------------------------//
#ifdef __cplusplus
}
#endif
#endif /* _INC_Demo01_H */
main.c内容如下:
#define _CRT_SECURE_NO_WARNINGS
#include
#include
#include
#include "socketclientdll.h"
int main()
{
int *handle = NULL;
cltSocketInit(&handle);
system("pause");
return 0;
}
通过上述步骤,可以初步搭建一个测试框架进行测试,下面来完善部分C文件。
第一套API函数
int main()
{
unsigned char inbuf[12] = "12345";
unsigned char outbuf[12] = { "0" };
int inbuflen;
int ret;
int outbuflen;
void *handle = NULL;
inbuflen = strlen(inbuf);
// 1、初始化
ret = cltSocketInit(&handle/*out*/);
if (ret != 0)
{
return ret;
}
// 2、发送报文
ret = 0;
ret = cltSocketSend(handle, inbuf, inbuflen);
if (ret != 0)
{
return ret;
}
// 3、接受报文
ret = 0;
ret = cltSocketRev(handle, outbuf, &outbuflen);
if (ret != 0)
{
return ret;
}
// 4、释放资源
ret = 0;
ret = cltSocketDestory(handle);
if (ret != 0)
{
return ret;
}
handle = NULL; //避免野指针
system("pause");
return 0;
}
使用第二套API函数所用到的main函数
int main()
{
unsigned char *outbuf = NULL;
unsigned char inbuf[12] = "12345";
int inbuflen;
int outbuflen;
int ret;
void *handle = NULL;
inbuflen = strlen(inbuf);
// 1、初始化
ret = cltSocketInit2(&handle);
if (ret != 0)
{
return ret;
}
// 2、客户端发报文
ret = 0;
ret = cltSocketSend2(handle, inbuf, inbuflen);
if (ret != 0)
{
return ret;
}
// 3、客户端收报文
ret = 0;
ret = cltSocketRev2(handle, &outbuf, &outbuflen);
if (ret != 0)
{
return ret;
}
// 4、客户端释放资源
ret = 0;
ret = cltSocketRev2_Free(&outbuf);
if (ret != 0)
{
return ret;
}
ret = 0;
ret = cltSocketDestory2(&handle);
if (ret != 0)
{
return ret;
}
system("pause");
return 0;
}
#define _CRT_SECURE_NO_WARNINGS
#include
#include
#include
#include "itcastlog.h"
//定义结构体变量进行数据类型封装:ip,version,port,*p,plen
typedef struct SCK_HANDLE
{
char verision[64];
char ip[128];
int port;
unsigned char *p;
int plen;
}SCK_HANDLE;
//------------------第一套api接口---Begin--------------------------------//
//客户端初始化 获取handle上下
__declspec(dllexport)
int cltSocketInit(void **handle /*out*/)
{
SCK_HANDLE *sck = NULL;
int ret;
/* 判断参数 */
if (handle == NULL)
{
ret = -1;
ITCAST_LOG(__FILE__, __LINE__, LogLevel[4], ret,"func cltSocketInit() err:%d handle == NULL\n" ,ret);
return ret;
}
sck = (SCK_HANDLE *)malloc(sizeof(SCK_HANDLE));//开辟内存
if (sck == NULL)
{
ret = -2;
ITCAST_LOG(__FILE__, __LINE__, LogLevel[4], ret,"func cltSocketInit() err:%d", ret);
return ret;
}
memset(sck, 0, sizeof(SCK_HANDLE));
/* 信息赋值 */
strcpy(sck->ip, "192.168.191.1");
strcpy(sck->verision, "version 1.0");
sck->port = 8081;
*handle = sck;
return 0;
}
//客户端发报文
__declspec(dllexport)
int cltSocketSend(void *handle /*in*/, unsigned char *buf /*in*/, int buflen /*in*/)
{
SCK_HANDLE *sck = NULL;
int ret;
/* 判断参数 */
if (handle == NULL || buf == NULL )
{
ret = -1;
ITCAST_LOG(__FILE__, __LINE__, LogLevel[4], ret,"func cltSocketSend() err:%d handle == NULL || buf == NULL || buflen == NULL\n", ret);//日志的形式打印错误信息
return ret;
}
/* 申请内存 */
sck = (SCK_HANDLE*)malloc(sizeof(SCK_HANDLE));
if (sck == NULL)
{
ret = -2;
ITCAST_LOG(__FILE__, __LINE__, LogLevel[4], ret,"func cltSocketSend() err:%d (SCK_HANDLE*)malloc(sizeof(SCK_HANDLE)) fail\n", ret);//日志的形式打印错误信息
return ret;
}
/* 申请内存 */
sck->p = (unsigned char *)malloc(buflen*sizeof(unsigned char));
if ((sck->p)==NULL)
{
ret = -3;
ITCAST_LOG(__FILE__, __LINE__, LogLevel[4], ret,"func cltSocketSend() err:%d (unsigned char *)malloc(buflen*sizeof(unsigned char)) fail\n", ret);//日志的形式打印错误信息
return ret;
}
memcpy(sck->p, buf, buflen); //以内存块的方式复制
sck->plen = buflen;
return 0;
}
//客户端收报文
__declspec(dllexport)
int cltSocketRev(void *handle /*in*/, unsigned char *buf /*in*/, int *buflen /*in out*/)
{
SCK_HANDLE *sck = NULL;
int ret;
if (handle == NULL || buf == NULL || buflen == NULL)
{
ret = -1;
ITCAST_LOG(__FILE__, __LINE__, LogLevel[4], ret,"func cltSocketRev() err:%d handle == NULL || buf == NULL || buflen == NULL\n", ret);//日志的形式打印错误信息
return ret;
}
sck = (SCK_HANDLE *)handle;
memcpy(buf, sck->p, sck->plen);
*buflen = sck->plen;
return 0;
}
//客户端释放资源
__declspec(dllexport)
int cltSocketDestory(void *handle/*in*/)
{
SCK_HANDLE *sck = NULL;
int ret;
if (handle == NULL)
{
ret = -1;
ITCAST_LOG(__FILE__, __LINE__, LogLevel[4], ret,"func cltSocketDestory() err:%d handle == NULL\n",ret);//日志的形式打印错误信息
return ret;
}
sck = (SCK_HANDLE *)handle;
if (sck->p)
{
free(sck->p);
sck->p = NULL;
}
free(sck);
return 0;
}
//------------------第二套api接口---Begin--------------------------------//
__declspec(dllexport)
int cltSocketInit2(void **handle/*out*/)
{
return cltSocketInit(handle);
}
//客户端发报文
__declspec(dllexport)
int cltSocketSend2(void *handle/*in*/, unsigned char *buf/*in*/, int buflen/*in*/)
{
return cltSocketSend(handle,buf,buflen);
}
//客户端收报文
__declspec(dllexport)
int cltSocketRev2(void *handle, unsigned char **buf, int *buflen)
{
SCK_HANDLE *sck = NULL;
int ret;
unsigned char *tmp = NULL;
if (handle == NULL || buf == NULL || buflen == NULL)
{
ret = -1;
ITCAST_LOG(__FILE__, __LINE__, LogLevel[4], ret,"func cltSocketRev() err:%d handle == NULL || buf == NULL || buflen == NULL\n", ret);//日志的形式打印错误信息
return ret;
}
sck = (SCK_HANDLE *)handle;
tmp = (unsigned char*)malloc(sck->plen);
if (tmp == NULL)
{
ret = -2;
ITCAST_LOG(__FILE__, __LINE__, LogLevel[4], ret,"func cltSocketRev() err:%d (unsigned char*)malloc(*buflen)\n", ret);//日志的形式打印错误信息
return ret;
}
memset(tmp, 0, sck->plen);
memcpy(tmp, sck->p, sck->plen);
*buflen = sck->plen;
*buf = tmp;
return 0;
}
//客户端释放资源
__declspec(dllexport)
int cltSocketRev2_Free(unsigned char **buf)
{
int ret;
if (buf == NULL)
{
ret = -1;
ITCAST_LOG(__FILE__, __LINE__, LogLevel[4], ret,"func cltSocketRev2_Free() err:%d buf == NULL\n",ret);//日志的形式打印错误信息
return ret;
}
free(*buf);
*buf = NULL;
return 0;
}
//客户端释放资源
__declspec(dllexport)
int cltSocketDestory2(void **handle)
{
SCK_HANDLE *sck = NULL;
int ret;
if (handle == NULL)
{
ret = -1;
ITCAST_LOG(__FILE__, __LINE__, LogLevel[4], ret,"func cltSocketDestory() err:%d handle == NULL\n", ret);//日志的形式打印错误信息
return ret;
}
sck = (SCK_HANDLE *)(*handle);
if (sck->p)
{
free(sck->p);
sck->p = NULL;//避免野指针
}
free(sck);
*handle = NULL;
return 0;
}