天天看點

windows動态庫開發

本篇主要講述動态庫的開發、動态庫的測試與調用、添加log日志記錄的應用。

動态庫原理淺析

1.       動态庫是什麼,它有什麼作用?
Windows系統平台上提供了一種完全不同的較有效的程式設計和運作環境,你可以将獨立的程式子產品建立為較小的DLL(Dynamic Linkable Library)檔案,并可對它們單獨編譯和測試。在運作時,隻有當EXE程式确實要調用這些DLL子產品的情況下,系統才會将它們裝載到記憶體空間中。這種方式不僅減少了EXE檔案的大小和對記憶體空間的需求,而且使這些DLL子產品可以同時被多個應用程式使用。詳見文檔《Win32環境下動态連結庫(DLL)程式設計原理》。
2.       動态庫的導出

動态庫的導出有兩種方法:

(1)     __declspec(dllexport)intMyFunction(int n);

這樣就可以将動态庫中的函數MyFunction()導出了。

(2)     需要一個Des檔案,通過該檔案來導出動态庫。

(Master沒細說這種方法,不過說了這兩種方法都較常用)

說明:在後面的例程中将使用第一種導出方法,并且将__declspec(dllexport)int放到宏定義中去。

3.       與DLL子產品建立連結(應用程式加載動态庫)

與DLL子產品建立連結也有兩種方法:

(1)     隐式連結方式

程式員在建立一個DLL檔案時,連結程式會自動生成一個與之對應的LIB導入檔案。該檔案包含了每一個DLL導出函數的符号名和可選的辨別号,但是并不含有實際的代碼。LIB檔案作為DLL的替代檔案被編譯到應用程式項目中。具體操作方法會在後面提到。

(2)     顯式連結方式(動态加載)

顯式連結方式對于內建化的開發語言(例如VB)比較适合。

說明:在後面的例程中将使用第一種連結方法。

4.       應用程式(exe檔案)怎樣找到動态庫?
(Master)這對于不同的系統版本,其方式不一樣,如在XP、win7、win8系統下都不一樣。例如在XP系統下,應用程式是優先到C:\Windows\System32路徑下去找,完了之後再到對應目錄下去找。

動态庫的調用

先不說如何開發動态庫,先來感受一下動态庫是如何被使用的。

Step1:複制所需檔案到指定位置
這裡需要三個檔案,socketclient.dll、socketclient.lib、socketclientdll.h,将這三個檔案複制到項目目錄下,如下圖所示。然後再将頭檔案socketclientdll.h添加到工程裡。
windows動态庫開發
dll和lib檔案打不開,這裡就隻展示socketclientdll.h檔案了。
/*
下面定義了一套socket用戶端發送封包接受封包的api接口
請寫出這套接口api的調用方法
*/
 
#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--------------------------------//
    //用戶端初始化
    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 */
           
Step2:連結lib檔案
右擊工程名→properties,彈出如下對話框,
windows動态庫開發
将socketclient.lib檔案的檔案名複制到圖中所示位置,完了如下圖。
windows動态庫開發
Step3:建立源檔案,編寫調用動态庫的代碼。
main.c檔案
#include "stdio.h"
#include "string.h"
#include "socketclientdll.h"
 
int main()
{
    int  rv = 0;
    void *handle = NULL;
    unsigned char buf[100];//in
    int buflen = 100;//in
    unsigned char buf2[2048];
    int buflen2 = 2048;
 
    strcpy(buf, "abcdef");//将字元串"abcdef"拷貝到buf中,包括。
    buflen = strlen(buf);
   
//C語言的資料,并不一定都是以結尾的字元串,
    //比如說對明文加密的密文。
 
    //用戶端初始化
    rv = cltSocketInit(&handle);//顯然是在被調函數cltSocketInit()中為handle配置設定記憶體。(千萬不能将handle聲明為二級指針,然後再傳遞給該函數。)
    if(rv != 0)
    {
       return rv;
    }
   
    //用戶端發封包
    rv = cltSocketSend(handle, buf, buflen);//debug時隻能看到handle中存放的位址,看不到該位址所指記憶體空間的值,是以不确定handle所指字元是否以結尾。
    if(rv != 0)
    {
       return rv;
    }
   
    //用戶端收封包
    rv = cltSocketRev(handle, buf2, &buflen2);//debug時看到buf2中隻有abcdef六個字元,f後不是。是以handle所指空間的字元f後應該不是了。
    if(rv != 0)
    {
       return rv;
    }
    printf("%s\n", buf2);//buf2并非以結尾,程式執行到這裡時還沒有崩潰就已經很給面子了。
    //若真想正确列印接收到的封包,可以按照長度buflen2一個字元一個字元地列印。
 
    cltSocketDestory(handle);
 
    printf("hello...\n");
    getchar();
    return 0;
}
           
上面的例程是已經定義了一套socket用戶端發送封包、接受封包的api接口,并且發送、接收等函數都已經寫好了,封裝在庫檔案中(lib檔案),我們隻需調用即可。那麼這些動态庫庫究竟是如何生成的呢?這些庫檔案(這裡其實就是dll和lib檔案)裡面裝的是什麼呢?這在後面會詳細講到。

前面一節是講述如何使用已經建立好的動态庫,這一節則是講述如何開發動态庫。這裡就以實作前一節的動态庫功能為例來講述實際工程開發中是如何實作動态庫的建立與開發的。

動态庫的建立

Step1:建立工程
第一步和之前的建立工程一樣,如下圖所示。
windows動态庫開發
接下來的一步和之前略有不同,如下圖所示。
windows動态庫開發
Step2:添加頭檔案itcast_comm.h。頭檔案内容如下。
#ifndef _SOCEKETCLIET_H_
#define _SOCEKETCLIET_H_
 
#ifdef __cplusplus
extern "C" {
#endif
 
#define _ITCAST_OS_WIN32_ 
#define _ITCAST_OS_LINUX_
 
 
#ifdef _ITCAST_OS_WIN32_   
#define ITCAST_FUNC_EXPORT(returnType)     __declspec(dllexport) returnType
#else
#ifdef _ITCAST_OS_LINUX
#define ITCAST_FUNC_EXPORT(returnType) __attribute__ ((visibility("default"))) returnType
#else 
#define ITCAST_FUNC_EXPORT(returnType)    returnType
#endif
#endif
 
#ifdef __cplusplus
}
#endif
 
 
#endif
           
Step3:建立源檔案MySocketLib.c

接下來就是在要在檔案MySocketLib.c裡編輯自己的庫函數了。我們就以master給的第一套api接口為例來編寫這些函數的具體源代碼。

這裡先打樁,然後再編寫個動态庫測試程式來看看有木有成功,若成功了則繼續往下編寫動态庫函數。

#include "stdio.h"
#include "stdlib.h"
#include "string.h"
#include "itcast_comm.h"
 
//------------------第一套api接口---Begin--------------------------------//
//用戶端初始化
int cltSocketInit(void **handle /*out*/)
{
    printf("func cltSocketInit() begin....\n");//這裡先随便輸出個東西,然後再測試動态庫有沒有建立成功,若成功了然後再繼續往下編寫動态庫。
    printf("func cltSocketInit() end....\n");
    return 0;
}
 
//用戶端發封包
int cltSocketSend(void *handle /*in*/, unsigned char *buf /*in*/,  int buflen /*in*/)
{
    return 0;
}
 
//用戶端收封包
int cltSocketRev(void *handle /*in*/, unsigned char *buf /*in*/, int *buflen /*in out*/)
{
    return 0;
}
 
//用戶端釋放資源
int cltSocketDestory(void *handle/*in*/)
{
    return 0;
}
//------------------第一套api接口---End-----------------------------------//
           
一般的函數是像上述這樣定義的,可是動态庫開發不一樣,這裡需要将各個函數的傳回類型用itcast_comm.h頭檔案中的宏定義ITCAST_FUNC_EXPORT(returnType)來替換,完了如下所示。
#include "stdio.h"
#include "stdlib.h"
#include "string.h"
#include "itcast_comm.h"
 
//------------------第一套api接口---Begin--------------------------------//
//用戶端初始化
ITCAST_FUNC_EXPORT(int)
cltSocketInit(void **handle /*out*/)
{
    printf("func cltSocketInit() begin....\n");//這裡先随便輸出個東西,然後再測試動态庫有沒有建立成功,若成功了然後再繼續往下編寫動态庫。
    printf("func cltSocketInit() end....\n");
    return 0;
}
 
//用戶端發封包
ITCAST_FUNC_EXPORT(int)
cltSocketSend(void *handle /*in*/, unsigned char *buf /*in*/,  int buflen /*in*/)
{
    return 0;
}
 
//用戶端收封包
ITCAST_FUNC_EXPORT(int)
cltSocketRev(void *handle /*in*/, unsigned char *buf /*in*/, int *buflen /*in out*/)
{
    return 0;
}
 
//用戶端釋放資源
ITCAST_FUNC_EXPORT(int)
cltSocketDestory(void *handle/*in*/)
{
    return 0;
}
//------------------第一套api接口---End-----------------------------------//
           
Step4:編譯生成動态庫檔案,如下所示。
windows動态庫開發

動态庫測試程式的建立

類似動态庫調用那一節,需要添加lib檔案到測試工程的連結器。

Step1:建立測試工程
建立一個工程,這個工程是用來測試剛剛開發的動态庫的。注意這時候就是建立一個普通的工程,與建立動态庫開發環境有所不同,不同點如下圖所示。
windows動态庫開發
Step2:複制所需檔案到指定位置

如“同動态庫的調用”一節所述,這裡也需要三個檔案。

将在動态庫開發中生成的兩個檔案MySocketLib.dll、MySocketLib.lib和socketclientdll.h共三個檔案複制到指定位置,如下圖所示。然後再将頭檔案socketclientdll.h添加到工程裡。

windows動态庫開發
Step3:連結lib檔案
同樣是右擊工程名→properties,将MySocketLib.lib檔案的檔案名複制到下圖所示位置。
windows動态庫開發
Step4:建立源檔案,編寫調用動态庫的代碼。
#include "stdio.h"
#include "stdlib.h"
#include "string.h"
#include "socketclientdll.h"
 
int main()
{
    int rv = 0;
    void *handle = NULL;
    rv = cltSocketInit(&handle);
    if(rv != 0)
    {
       printf("func cltSocketInit():%d.\n",rv);
       return rv;
    }
    printf("hello.\n");
    system("pause");
    return 0;
}
           
Step5:編譯調試
在“rv = cltSocketInit(&handle);”處設定斷點,如果能夠成功進入該函數(也就是進入了動态庫),并成功執行該函數裡面的代碼,則說明動态庫調用成功了。

編寫動态庫函數

當測試程式成功測試了動态庫的調用後,就說明動态庫已經建立成功了。接下來就是要繼續完善動态庫函數了。

最後動态庫的源代碼如下。
#include "stdio.h"
#include "stdlib.h"
#include "string.h"
#include "itcast_comm.h"
 
//句柄隻不過是一套api運作時的資源環境,說白了它就是記憶體首位址。
typedef struct _SocketHandle
{
    char ip[128];
    int  port;
    unsigned char *buf;
    int buflen;
}SocketHandle;
 
//------------------第一套api接口---Begin--------------------------------//
//用戶端初始化
ITCAST_FUNC_EXPORT(int)
cltSocketInit(void **handle /*out*/)
{
    int ret = 0;
    SocketHandle *sh = NULL;
 
    //printf("func cltSocketInit() begin....\n");
   
    sh = (SocketHandle *)malloc(sizeof(SocketHandle));
    if(sh == NULL)
    {
       ret = -1;
       return ret;
    }
    memset(sh, 0, sizeof(SocketHandle));
    strcpy(sh->ip, "192.168.1.254");//随便寫得一個IP位址,示範而已。
    sh->port = 0x8;//随便寫得一個數字。
 
    printf("func cltSocketInit() end....\n");
    *handle = sh;
    return ret;
}
 
//用戶端發封包
ITCAST_FUNC_EXPORT(int)
cltSocketSend(void *handle /*in*/, unsigned char *buf /*in*/,  int buflen /*in*/)
{
    int ret = 0;
    SocketHandle *sh = NULL;
 
    if(handle==NULL || buf==NULL || buflen>2048*10)
    {
       ret = -1;
       return ret;
    }
    sh = (SocketHandle *)handle;
    sh->buf = (char *)malloc(buflen);
    if(sh->buf == NULL)
    {
       ret = -2;
       return ret;
    }
    memcpy(sh->buf, buf, buflen);
    sh->buflen = buflen;
 
    return ret;
}
 
//用戶端收封包
ITCAST_FUNC_EXPORT(int)
cltSocketRev(void *handle /*in*/, unsigned char *buf /*in*/, int *buflen /*in out*/)
{
    int ret = 0;
    SocketHandle *sh = NULL;
 
    if(handle==NULL || buf==NULL || buflen==NULL)
    {
       ret = -1;
       return ret;
    }
    sh = (SocketHandle *)handle;
    memcpy(buf, sh->buf, sh->buflen);
    *buflen = sh->buflen;
 
    return ret;
}
 
//用戶端釋放資源
ITCAST_FUNC_EXPORT(int)
cltSocketDestory(void *handle/*in*/)
{
    int ret = 0;
    SocketHandle *sh = NULL;
 
    if(handle==NULL)
    {
       ret = -1;
       return ret;
    }
    sh = (SocketHandle *)handle;
    if(sh->buf != NULL)
    {
       free(sh->buf);
       sh->buf = NULL;
    }
    if(sh != NULL)
    {
       free(sh);
       sh = NULL;
    }
    return ret;
}
//------------------第一套api接口---End-----------------------------------//
           
編譯該動态庫工程即可更新在對應路徑下的動态庫檔案MySocketLib.dll和MySocketLib.lib。
動态庫檔案弄好之後,就要測試動态庫中的函數是否可用,那麼就要将新生成的兩個庫檔案拷貝到之前的測試程式路徑下,以替換之前的庫檔案。具體方法在前面已經說過了。
将動态庫檔案拷貝後,就要編寫動态庫測試程式了,以便于測試這些動态庫函數是否可用,測試程式如下所示。
#include "stdio.h"
#include "stdlib.h"
#include "string.h"
#include "socketclientdll.h"
 
//句柄隻不過是一套api運作時的資源環境,說白了它就是記憶體首位址。
int main()
{
    int rv = 0;
    void *handle = NULL;
    unsigned char buf[2048];
    int buflen = 0;
    unsigned char buf2[2048];
    int buflen2 = 0;
 
    strcpy(buf, "abcdefg123456789");
    buflen = 10;//buf中可以有個字元,但是需要發送多少個是由buflen來決定滴。
 
    rv = cltSocketInit(&handle);//為handle配置設定記憶體,初始化handle。
    if(rv != 0)
    {
       printf("func cltSocketInit():%d.\n",rv);
       return rv;
    }
 
    rv = cltSocketSend(handle, buf, buflen);//從buf中拷貝buflen個字元到handle中的buf。
    if(rv != 0)
    {
       printf("func cltSocketSend():%d.\n",rv);
       return rv;
    }
 
    rv = cltSocketRev(handle, buf2, &buflen2);//從handle中的buf拷貝buflen2個字元到buf2中。
    if(rv != 0)
    {
       printf("func cltSocketRev():%d.\n",rv);
       return rv;
    }
 
    rv = cltSocketDestory(handle);
    if(rv != 0)
    {
       printf("func cltSocketDestory():%d.\n",rv);
       return 0;
    }
 
    printf("hello.\n");
    system("pause");
    return 0;
}
           
編譯該測試工程後,就可以調試測試動态庫函數是否編寫成功了,如果沒有則還需要到動态庫開發環境中去修改庫函數,然後将新生成的庫檔案拷貝到測試程式路徑下再次測試,這樣直到測試成功為止。

添加日志記錄

工程開發中,常常需要将程式的執行狀态記錄下來儲存到一個日志檔案中去,以便于調試。

這裡需要兩個檔案:itcastlog.h和itcastlog.c,哪裡需要用到日志記錄,就将這個兩個檔案複制到哪裡,比如上一節中的動态庫想要用日志來記錄程式執行狀态,就将這兩個檔案複制到動态庫程式目錄下,然後将頭檔案itcastlog.h添加到工程中。

若在MySocketLib.c檔案中想要用日志記錄,那麼還要在MySocketLib.c中包含頭檔案itcastlog.h。

itcastlog.h檔案如下
//written by [email protected]
//20140323
//itcastlog.h 日志頭檔案
 
 
#ifndef _ITCAST_LOG_H_
#define _ITCAST_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  LogLevel[5];
void ITCAST_LOG(const char *file, int line, int level, int status, const char *fmt, ...);
 
#endif
           
itcastlog.c
#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 "itcastLog.h"
 
#define ITCAST_DEBUG_FILE_  "socketclient.log"
#define ITCAST_MAX_STRING_LEN      10240
 
//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  LogLevel[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;
    }
   
    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]\n", line);
    strLen += tmpStrLen;
   
    //打開LOG檔案
    if(ITCAST_Error_OpenFile(&pf))
    {
       return ;
    }
   
    //寫入LOG檔案
    write(pf, str, strLen);
    //IC_Log_Error_WriteFile(str);
   
    //關閉檔案
    close(pf);
   
    return ;
}
 
void ITCAST_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 ;
}
           
那麼再實踐中是如何使用的呢?其實很簡單,作為示例,下面就展示一下cltSocketInit()函數是如何調用日志顯示的,如下所示。
//用戶端初始化
ITCAST_FUNC_EXPORT(int)
cltSocketInit(void **handle /*out*/)
{
    int ret = 0;
    SocketHandle *sh = NULL;
 
    //printf("func cltSocketInit() begin....\n");也可以将這一句也改為用log日志記錄的方式,如下所示。
    ITCAST_LOG(__FILE__, __LINE__, LogLevel[2], ret, "func cltSocketInit() begin....\n");
    sh = (SocketHandle *)malloc(sizeof(SocketHandle));
    if(sh == NULL)
    {
       ret = -1;
       ITCAST_LOG(__FILE__, __LINE__, LogLevel[4], ret, "(SocketHandle *)malloc(sizeof(SocketHandle))");
       return ret;
    }
    memset(sh, 0, sizeof(SocketHandle));
    strcpy(sh->ip, "192.168.1.254");//随便寫得一個IP位址,示範而已。
    sh->port = 0x8;//随便寫得一個數字。
   
    //printf("func cltSocketInit() end....\n");
    ITCAST_LOG(__FILE__, __LINE__, LogLevel[2], ret, "func cltSocketInit() end....\n");
    *handle = sh;
    return ret;
}
           
執行完程式後,看C:\itcast目錄下的log檔案,如下圖所示。
windows動态庫開發
生成的日志是被放在了C槽根目錄下的itcast檔案夾裡,這可以由itcastlog.c檔案中的ITCAST_Error_OpenFile()函數看出來。注意是要先在C槽根目錄下建立一個檔案名為itcast的檔案夾。

第二套API函數實作動态庫開發

前面講述動态庫的開發是基于第一套API函數接口來實作的,接下來要用第二套API函數來實作。

其實基本原理和第一套API函數實作是一樣的,隻不過第二套API的部分函數的接口不一樣,是以導緻了函數内容有些變化。如下所示。
#include "stdio.h"
#include "stdlib.h"
#include "string.h"
#include "itcast_comm.h"
#include "itcastlog.h"
 
typedef struct _SocketHandle
{
    char ip[128];
    int  port;
    unsigned char *buf;
    int buflen;
}SocketHandle;
 
//------------------第二套api接口---Begin--------------------------------//
//用戶端初始化
ITCAST_FUNC_EXPORT(int)
cltSocketInit2(void **handle)
{
    //同第一套API函數
}
 
//用戶端發封包
ITCAST_FUNC_EXPORT(int)
cltSocketSend2(void *handle, unsigned char *buf,  int buflen)
{
    //同第一套API函數
}
 
//用戶端收封包
ITCAST_FUNC_EXPORT(int)
cltSocketRev2(void *handle, unsigned char **buf, int *buflen)
{
    int ret = 0;
    SocketHandle *sh = NULL;
 
    char *tmp = NULL;
 
    if(handle==NULL || buf==NULL || buflen==NULL)
    {
       ret = -1;
       ITCAST_LOG(__FILE__, __LINE__, LogLevel[4], ret, "cltSocketRev2():(handle==NULL || buf==NULL || buflen==NULL)");
       return ret;
    }
    sh = (SocketHandle *)handle;
 
    tmp = (char *)malloc(sh->buflen);
    if(tmp == NULL)
    {
       ret = -1;
       ITCAST_LOG(__FILE__, __LINE__, LogLevel[4], ret, "tmp = (char *)malloc(sh->buflen)");
       return ret;
    }
    *buf = tmp;
    *buflen = sh->buflen;
 
    return ret;
}
ITCAST_FUNC_EXPORT(int)
cltSocketRev2_Free(unsigned char **buf)
{
    int ret = 0;
    unsigned char *tmp = NULL;
    if(buf == NULL)
    {
       ret = -1;
       ITCAST_LOG(__FILE__, __LINE__, LogLevel[4], ret, "cltSocketRev2_Free():(buf == NULL)");
       return ret;
    }
    tmp = *buf;
    if(tmp != NULL)
    {
       free(tmp);
       tmp = NULL;
    }
    *buf = NULL;
    return ret;
}
 
//用戶端釋放資源
ITCAST_FUNC_EXPORT(int)
cltSocketDestory2(void **handle)
{
    int ret = 0;
    SocketHandle *sh = NULL;
 
    if(handle==NULL)
    {
       ret = -1;
       return ret;
    }
    sh = (SocketHandle *)*handle; //強制類型轉換與取内容運算符優先級相同,但這裡遵循從又向左的優先級。
    if(sh->buf != NULL)
    {
       free(sh->buf);
       sh->buf = NULL;
    }
    if(sh != NULL)
    {
       free(sh);
       sh = NULL;
    }
    *handle = NULL;
    return ret;
}
//------------------第二套api接口---End--------------------------------//