////////////////////////////////////////////////////////////////////////
// 传输协议根据TLV(type,length,value)协议编制
//
// TLV协议说明:
// TLV格式的数据包中类型type指明了当前包的含义,type是单一包的类型或者是嵌套包的类型;
// 长度length指明了当前包的大小,注意这个的大小包括了type、length、value三部分;
// 值value包括了该数据包的实际内容,如果是嵌套包,内容为里面各个子包的总和。
//
// 当前Type字段为signed short类型,长度为2个字节;
// Length字段为signed long 类型,长度为4个字节。
//
// 采用小端模式(little endian)发送,即数据低位存储在低地址的一种形式,
// intel公司的ix86系列芯片采用这种存储方式。
//
// 分2大类:实时车辆信息包、心跳包
// 实时车辆信息包格式:T L 通行信息子包 特写图子包 全景图子包
// 心跳包格式: T L
////////////////////////////////////////////////////////////////////////
// 定义传输包数据类型
const short TYPE_REALVEHICLE = 1101; // 实时车辆信息包
//const short TYPE_OVERTIMEVEHICLE = 1102; // 补传车辆信息包
const short TYPE_HEARTBEAT = 1111; // 心跳包
const short TYPE_TIME = 1151; // 时间包
const short TYPE_PASSINFO = 1201; // 通行信息包
const short TYPE_IMAGENEAR = 1202; // 特写图片数据包
const short TYPE_IMAGEFULL = 1203; // 全景图片数据包
const int DELAY_RECEIVEDATA = 20; // 每次接收网络数据后挂起等待时间
const int DELAY_SHUTDOWNSOCKET = 10; // 每次关闭Socket句柄后的延迟
const int DELAY_WAITSUCCESS = 10; // 等待成功延时
const int DELAY_WAITQUIT = 10; // 等待退出延时
const int DELAY_WAITSENDING = 500; // 等待本次传输完成时间
const int DELAY_HEARTBEATINTERVAL = 3000; // 心跳时间间隔
const int DELAY_INSPECT = 100; // 检查网络状态的间隔
const int LIMIT_WAITRECEIVE = 200; // 等待接收最长时间
const int LIMIT_WAITQUIT = 200; // 退出延时限制
const int LIMIT_DATAOVERTIME = 10000; // 数据超时限制(保存在发送缓存中的)
const int LIMIT_HEARTBEAT = 30000; // 心跳间隔超时限制(超出认为服务器异常)
const int LIMIT_MAXLISTENCLIENT = 128; // 最大监听客户数
const int LIMIT_DEVICEID_LENGTH = 20; // 设备编号字符串长度限制
const int LIMIT_PLATE_LENGTH = 20; // 车牌字符串长度限制
const int LIMIT_PASSTIME_LENGTH = 24; // 通行时间字符串长度限制
const int LIMIT_MINIMAGENUMBER = 1; // 最小图片数量
const int LIMIT_MAXIMAGENUMBER = 2; // 最大图片数量
const int LIMIT_MINIMAGESIZE = 1L; // 图片最小占用字节
const int LIMIT_MAXIMAGESIZE = 100L * 1024L; // 图片最大占用字节
const int LIMIT_SENDBUFFERSIZE = 201L * 1024L; // TCP包最大发送大小 201K
const int VALUE_ZERO = 0; // 零值
const int VALUE_ALIVE = 1; // 存活
typedef struct UDT_PassInfo
{
DWORD dwProtocalVersion; // 协议版本
char pchDeviceId[LIMIT_DEVICEID_LENGTH]; // 设备编号 最多20位
int iRoadWay; // 车道号
char pchPlate[LIMIT_PLATE_LENGTH]; // 号牌号码
char pchPassTime[LIMIT_PASSTIME_LENGTH]; // 经过时间
int iSpeed; // 车速
int iSpeedLimit; // 限速
DWORD dwDeviceState; // 设备状态
int iImageNumber; // 图片数量
DWORD dwImageNearSize; // 特写图片占用空间
DWORD dwImageFullSize; // 全景图片占用空间
}UDT_PassInfo;
const int PACKET_TYPE_LENGTH = 2; // T(类型)所占长度
const int PACKET_LENGTH_LENGTH = 4; // L(长度)所占长度
const int PACKET_HEADER_LENGTH = 6; // 头(T+L)所占长度
const int PACKET_TIME_LENGTH = 10; // 时间包长度
const int PACKET_PASSINFO_LENGTH = sizeof(UDT_PassInfo) + PACKET_HEADER_LENGTH; // 通行信息子包长度
typedef struct UDT_TCPCommunicationServer // 通讯控制参数结构体
{
HWND hWndProcess; // 调用主程序主窗口句柄
WORD wListenPort; // 监听Socket端口
BOOL bIsDebug; // 是否调试
BOOL bIsQuitListen; // 是否退出监听
SOCKET sckListen; // 监听Socket句柄
BOOL bListenAlive; // 监听Socket是否处于活动状态(是否在运行)
CString strListenMsg; // 监听线程的消息保存区
CWinThread* threadListen; // 监听Socket线程句柄
CRITICAL_SECTION ctsListen; // 监听线程的临界保护变量
BOOL bClientAlive; // 客户端线程是否处于活动状态(是否在运行)
BYTE btClientIp[4]; // 客户端Ip地址
SOCKET sckClient; // 客户端线程通讯使用的Socket句柄
CWinThread* threadClient; // 客户端线程句柄
CRITICAL_SECTION ctsClient; // 客户端临界区
CEvent evtClientEnd; // 客户端线程信号量(通知对应线程结束)
BOOL bIsReady; // 数据是否准备好
BOOL bIsSending; // 是否在发送过程中
DWORD dwPutTimeCount; // 放入数据的时间点
DWORD dwSendTimeCount; // 发送数据的时间点
DWORD dwHeartBeatTimeCount; // 心跳时间点
DWORD dwPutCount; // 服务器端放入的记录统计
DWORD dwSendCount; // 服务器端发送的记录统计
struct UDT_PassInfo udtPassInfo; // 车辆信息数据包(不包括图片)
BYTE pbtSendBufHeartBeat[PACKET_HEADER_LENGTH]; // 心跳包发送缓存区
BYTE pbtSendBufTime[PACKET_TIME_LENGTH]; // 时间包发送缓存区
BYTE pbtSendBuf[LIMIT_SENDBUFFERSIZE]; // 通用包发送缓存区
int iSendTotalLength; // 通用包发送总长度
}UDT_TCPCommunicationServer;
static struct UDT_TCPCommunicationServer m_udtTcpServer; // 模块级变量,控制通讯
static BOOL m_bInitSuccess = FALSE; // 初始化成功标记,除Start函数外均需要初始化成功才能调用
UINT TcpClientThread(LPVOID pParam);
UINT TcpListenThread(LPVOID pParam);
int WriteToLog(int iCueNumber,TCHAR *szMsg);
int WriteToLog(TCHAR *szMsg);
int WriteBinaryFile(BYTE* pbtBuffer,DWORD dwFileSize,char *pchFileName);
/*******************************************************
int GDW_VM2003Server_Start(DWORD hWnd);
int GDW_VM2003Server_Send(char* pchDeviceId,
int iRoadWay,
char* pchPlate,
char* pchTime,
int iSpeed,
int iSpeedLimit,
DWORD dwDeviceState,
int iImageNumber,
DWORD dwImageNearSize,
BYTE* pbtImageNear,
DWORD dwImageFullSize,
BYTE* pbtImageFull);
int GDW_VM2003Server_End();
*******************************************************/
//
// Note!
//
// If this DLL is dynamically linked against the MFC
// DLLs, any functions exported from this DLL which
// call into MFC must have the AFX_MANAGE_STATE macro
// added at the very beginning of the function.
//
// For example:
//
// extern "C" BOOL PASCAL EXPORT ExportedFunction()
// {
// AFX_MANAGE_STATE(AfxGetStaticModuleState());
// // normal function body here
// }
//
// It is very important that this macro appear in each
// function, prior to any calls into MFC. This means that
// it must appear as the first statement within the
// function, even before any object variable declarations
// as their constructors may generate calls into the MFC
// DLL.
//
// Please see MFC Technical Notes 33 and 58 for additional
// details.
//
/////////////////////////////////////////////////////////////////////////////
// CGDW_TransmitApp
BEGIN_MESSAGE_MAP(CGDW_TransmitApp, CWinApp)
//{{AFX_MSG_MAP(CGDW_TransmitApp)
// NOTE - the ClassWizard will add and remove mapping macros here.
// DO NOT EDIT what you see in these blocks of generated code!
//}}AFX_MSG_MAP
END_MESSAGE_MAP()
/////////////////////////////////////////////////////////////////////////////
// CGDW_TransmitApp construction
CGDW_TransmitApp::CGDW_TransmitApp()
{
// TODO: add construction code here,
// Place all significant initialization in InitInstance
}
/////////////////////////////////////////////////////////////////////////////
// The one and only CGDW_TransmitApp object
CGDW_TransmitApp theApp;
/************************************************************************
Function int WriteToLog:
write text message to file
Input:
int iCueNumber 指示信息:一般为返回值、错误号
TCHAR *szMsg 文本字符串
OutPut:
return:
If no error occurs, returns zero,other returns no zero;
Update:
Version Date Author Description
1.0 2008-03-19 Shi Mingjie Create
************************************************************************/
int WriteToLog(int iCueNumber,TCHAR *szMsg)
{
int i = 0;
int iLastSperate = 0;
TCHAR szCurPath[272];
HANDLE hWndFile;
WIN32_FIND_DATA fileFind;
FILE *fp;
SYSTEMTIME lpSystemTime;
GetModuleFileName(GetModuleHandle(NULL),szCurPath,256);
for (i=0; i<256; i++)
{
if (szCurPath[i] == '//')
{
iLastSperate = i;
}
else if(szCurPath[i] == '/0')
{
break;
}
}
if (iLastSperate > 0 && i < 256)
{
szCurPath[iLastSperate] = '/0';
}
else
{
return -1;
}
strcat(szCurPath,"//Tcp_Server.evt");
GetLocalTime(&lpSystemTime);
hWndFile = FindFirstFile(szCurPath,&fileFind);
FindClose(hWndFile);
if (INVALID_HANDLE_VALUE == hWndFile)
{
if ((fp = fopen(szCurPath,"w")) == NULL)
{
return -2;
}
fprintf(fp,"%04d-%02d-%02d %02d:%02d:%02d:%03d Event:%06d %s/n",
lpSystemTime.wYear,lpSystemTime.wMonth,lpSystemTime.wDay,
lpSystemTime.wHour,lpSystemTime.wMinute,lpSystemTime.wSecond,lpSystemTime.wMilliseconds,
iCueNumber,szMsg);
fclose(fp);
}
else
{
if (fileFind.nFileSizeLow > 61440) // if event file size > 60K, delete, create new
{
if (DeleteFile(szCurPath))
{
if ((fp = fopen(szCurPath,"w")) == NULL)
{
return -2;
}
fprintf(fp,"%04d-%02d-%02d %02d:%02d:%02d:%03d Event:%06d %s/n",
lpSystemTime.wYear,lpSystemTime.wMonth,lpSystemTime.wDay,
lpSystemTime.wHour,lpSystemTime.wMinute,lpSystemTime.wSecond,lpSystemTime.wMilliseconds,
iCueNumber,szMsg);
fclose(fp);
}
}
else
{
if ((fp = fopen(szCurPath,"a+")) == NULL)
{
return -3;
}
else
{
fprintf(fp,"%04d-%02d-%02d %02d:%02d:%02d:%03d Event:%06d %s/n",
lpSystemTime.wYear,lpSystemTime.wMonth,lpSystemTime.wDay,
lpSystemTime.wHour,lpSystemTime.wMinute,lpSystemTime.wSecond,lpSystemTime.wMilliseconds,
iCueNumber,szMsg);
fclose(fp);
}
}
}
return VALUE_ZERO;
}
/************************************************************************
Function int WriteToLog:
write text message to file
Input:
TCHAR *szMsg 文本字符串
OutPut:
return:
use default iCueNumber;
If no error occurs, returns zero,other returns no zero;
Update:
Version Date Author Description
1.0 2008-03-19 Shi Mingjie Create
************************************************************************/
int WriteToLog(TCHAR *szMsg) // 写日志信息,错误号默认-999
{
const int ERR_DEFAULT = -999; // 默认的错误消息值
int iReturn;
iReturn = WriteToLog(ERR_DEFAULT,szMsg);
return iReturn;
}
/************************************************************************
Function int WriteBinaryFile:
write binary data to file
Input:
BYTE* pbtBuffer 数据 不允许为NULL
DWORD dwFileSize 数据大小 合法值大于VALUE_ZERO,小于等于LIMIT_SENDBUFFERSIZE
char *pchFileName 文件名 不允许为NULL
OutPut:
return:
If no error occurs, returns zero,other returns no zero;
Update:
Version Date Author Description
1.0 2008-03-19 Shi Mingjie Create
************************************************************************/
int WriteBinaryFile(BYTE* pbtBuffer,DWORD dwFileSize,char *pchFileName)
{
FILE* fileWrite;
if ((NULL == pbtBuffer) || (NULL == pchFileName))
{
return -1;
}
if ((dwFileSize <= VALUE_ZERO) || (dwFileSize > LIMIT_SENDBUFFERSIZE))
{
return -2;
}
fileWrite = fopen(pchFileName,"wb");
if (fileWrite == NULL)
{
return -3;
}
if (fwrite(pbtBuffer,1,dwFileSize,fileWrite) != dwFileSize)
{
fclose(fileWrite);
return -4;
}
fclose(fileWrite);
return VALUE_ZERO;
}
/************************************************************************
Function int GDW_VM2003Server_Start:
Init && Start listen thread
Input:
DWORD hWnd 接收消息的窗口句柄,不能为0值
OutPut:
return:
If no error occurs, returns zero,other returns no zero;
Update:
Version Date Author Description
1.0 2008-03-19 Shi Mingjie Create
************************************************************************/
extern "C" int PASCAL EXPORT GDW_VM2003Server_Start(DWORD hWnd)
{
AFX_MANAGE_STATE(AfxGetStaticModuleState());
int i = 0;
int iLastSperate = 0;
int iIsDebugToLog;
int iProtocalVersion;
TCHAR chCurPath[MAX_PATH];
HANDLE hWndFile;
WIN32_FIND_DATA fileFind;
if (VALUE_ZERO == hWnd) // 为无效句柄则立即返回
{
return -1;
}
m_udtTcpServer.hWndProcess = (HWND)hWnd; // 保存消息处理句柄
// 读取配置参数:Tcp监听端口
GetModuleFileName(GetModuleHandle(NULL),chCurPath,MAX_PATH-20);
for (i=0; i<MAX_PATH-20; i++)
{
if (chCurPath[i] == '//')
{
iLastSperate = i;
}
else if(chCurPath[i] == '/0')
{
break;
}
}
if (iLastSperate > 0 && i < MAX_PATH-20)
{
chCurPath[iLastSperate] = '/0';
strcat(chCurPath,"//vm2003_server.ini");
hWndFile = FindFirstFile(chCurPath,&fileFind);
FindClose(hWndFile);
if (INVALID_HANDLE_VALUE != hWndFile)
{
m_udtTcpServer.wListenPort = GetPrivateProfileInt("Server","ListenPort",5001,chCurPath);
iIsDebugToLog = GetPrivateProfileInt("Server","DebugToLog",0,chCurPath);
iProtocalVersion = GetPrivateProfileInt("Server","ProtocalVersion",1000,chCurPath);
if (iIsDebugToLog != 0)
{
m_udtTcpServer.bIsDebug = TRUE;
}
m_udtTcpServer.udtPassInfo.dwProtocalVersion = (DWORD)iProtocalVersion;
}
else
{
m_udtTcpServer.wListenPort = 5001;
m_udtTcpServer.bIsDebug = FALSE;
m_udtTcpServer.udtPassInfo.dwProtocalVersion = 999;
}
}
else
{
m_udtTcpServer.wListenPort = 5001;
m_udtTcpServer.bIsDebug = FALSE;
m_udtTcpServer.udtPassInfo.dwProtocalVersion = 999;
}
// 初始化结构体UDT_TCPCommunicationServer参数
m_udtTcpServer.bIsQuitListen = FALSE;
m_udtTcpServer.bListenAlive = FALSE;
m_udtTcpServer.sckListen = NULL;
m_udtTcpServer.threadListen = NULL;
m_udtTcpServer.strListenMsg = "";
m_udtTcpServer.bClientAlive = FALSE;
for (i=0; i<4; i++)
{
m_udtTcpServer.btClientIp[i] = 0;
}
m_udtTcpServer.sckClient = NULL;
m_udtTcpServer.threadClient = NULL;
m_udtTcpServer.bIsReady = FALSE;
m_udtTcpServer.bIsSending = FALSE;
m_udtTcpServer.dwPutCount = 0;
m_udtTcpServer.dwSendCount = 0;
m_udtTcpServer.dwPutTimeCount = 0;
m_udtTcpServer.dwSendTimeCount = 0;
m_udtTcpServer.dwHeartBeatTimeCount = 0;
// 发送缓存区初始化
m_udtTcpServer.iSendTotalLength = 0;
// 实时车辆信息包格式:T L 通行信息子包(T-L-V) 特写图子包(T-L-V) 全景图子包(T-L-V)
memcpy(&m_udtTcpServer.pbtSendBuf[VALUE_ZERO],&TYPE_REALVEHICLE,PACKET_TYPE_LENGTH); // 填充总类型:实时车辆信息 2BYTE
memcpy(&m_udtTcpServer.pbtSendBuf[PACKET_HEADER_LENGTH],&TYPE_PASSINFO,PACKET_TYPE_LENGTH); // 填充子类型1:车辆信息包 2BYTE
memcpy(&m_udtTcpServer.pbtSendBuf[PACKET_HEADER_LENGTH+PACKET_TYPE_LENGTH],&PACKET_PASSINFO_LENGTH,PACKET_LENGTH_LENGTH); // 填充子类型1长度:车辆信息子包字节 4BYTE
// 心跳包格式: T L
memcpy(&m_udtTcpServer.pbtSendBufHeartBeat[VALUE_ZERO],&TYPE_HEARTBEAT,PACKET_TYPE_LENGTH); // 填充总类型:心跳 2BYTE
memcpy(&m_udtTcpServer.pbtSendBufHeartBeat[PACKET_TYPE_LENGTH],&PACKET_HEADER_LENGTH,PACKET_LENGTH_LENGTH); // 填充心跳总长度 4BYTE
// 校时包格式: T L V,初始化时复制T L
memcpy(&m_udtTcpServer.pbtSendBufTime[VALUE_ZERO],&TYPE_TIME,PACKET_TYPE_LENGTH);
memcpy(&m_udtTcpServer.pbtSendBufTime[PACKET_TYPE_LENGTH],&PACKET_TIME_LENGTH,PACKET_LENGTH_LENGTH);
InitializeCriticalSection(&m_udtTcpServer.ctsListen); //临界区初始化
InitializeCriticalSection(&m_udtTcpServer.ctsClient); //临界区初始化
m_udtTcpServer.evtClientEnd.ResetEvent(); // Sets the state of the event to nonsignaled
if (m_udtTcpServer.bIsDebug)
{
WriteToLog(0,"[启动]------------------信息分割线------------------");
}
// 建立Socket监听线程
m_udtTcpServer.threadListen = AfxBeginThread(TcpListenThread, &m_udtTcpServer, THREAD_PRIORITY_NORMAL, 0, CREATE_SUSPENDED);
m_udtTcpServer.threadListen->m_bAutoDelete = FALSE;
m_udtTcpServer.threadListen->ResumeThread();
m_bInitSuccess = TRUE;
return VALUE_ZERO;
}
/************************************************************************
Function int GDW_VM2003Server_Send:
Put data to sendbuffer
Input:
char* pchDeviceId 设备编号 最多20位 不允许为NULL
int iRoadWay 车道号
char* pchPlate 车牌号码 最多20位 不允许为NULL
char* pchTime 通行时间 最多20位 不允许为NULL
int iSpeed 车速
int iSpeedLimit 限速
DWORD dwDeviceState 设备状态
int iImageNumber 图片数量
DWORD dwImageNearSize 特写图片大小 小于等于100K
BYTE* pbtImageNear 特写图片数据 不允许为NULL
DWORD dwImageFullSize 全景图片大小 小于等于100K
BYTE* pbtImageFull 全景图片数据 不允许为NULL
OutPut:
return:
GDW_VM2003Server_Start must call success first;
If no error occurs, returns zero,other returns no zero;
Update:
Version Date Author Description
1.0 2008-03-20 Shi Mingjie Create
************************************************************************/
extern "C" int PASCAL EXPORT GDW_VM2003Server_Send(char* pchDeviceId,
int iRoadWay,
char* pchPlate,
char* pchTime,
int iSpeed,
int iSpeedLimit,
DWORD dwDeviceState,
int iImageNumber,
DWORD dwImageNearSize,
BYTE* pbtImageNear,
DWORD dwImageFullSize,
BYTE* pbtImageFull)
{
AFX_MANAGE_STATE(AfxGetStaticModuleState());
DWORD dwTimeCountInterval; // 时间差值
int iCursor;
int iImageNearPacketLength;
int iImageFullPacketLength;
if (!m_bInitSuccess)
{
return -1;
}
// 监测线程未启动则直接返回
if (!m_udtTcpServer.bListenAlive)
{
return -101;
}
// 参数有空指针直接返回
if ((NULL == pchDeviceId) ||
(NULL == pchPlate) ||
(NULL == pchTime) ||
(NULL == pbtImageNear) ||
(NULL == pbtImageFull))
{
return -2;
}
// 字符串参数长度超出限制直接返回
if ((strlen(pchDeviceId) > LIMIT_DEVICEID_LENGTH) ||
(strlen(pchPlate) > LIMIT_PLATE_LENGTH) ||
(strlen(pchTime) > LIMIT_PASSTIME_LENGTH))
{
return -3;
}
// 其它参数超出限制直接返回
if ((dwImageNearSize < LIMIT_MINIMAGESIZE) ||
(dwImageNearSize > LIMIT_MAXIMAGESIZE) ||
(dwImageFullSize < LIMIT_MINIMAGESIZE) ||
(dwImageFullSize > LIMIT_MAXIMAGESIZE) ||
(iImageNumber < LIMIT_MINIMAGENUMBER) ||
(iImageNumber > LIMIT_MAXIMAGENUMBER))
{
return -4;
}
// 不判断客户线程是否已经启动--客户端线程后启动只要不超时即可
if (m_udtTcpServer.bIsSending) // 正在传输的时候等待足够的时间再尝试
{
Sleep(DELAY_WAITSENDING);
if (m_udtTcpServer.bIsSending)
{
WriteToLog("GDW_VM2003Server_Send函数:数据发送中,等待DELAY_WAITSENDING未完成,强制返回!");
return -5;
}
}
if (m_udtTcpServer.bIsReady) // 上次数据是否还存在
{
dwTimeCountInterval = GetTickCount() - m_udtTcpServer.dwPutTimeCount;
if (dwTimeCountInterval < LIMIT_DATAOVERTIME)
{
Sleep(DELAY_WAITSENDING);
if (m_udtTcpServer.bIsReady)
{
if (m_udtTcpServer.bIsDebug)
{
WriteToLog(dwTimeCountInterval,"GDW_VM2003Server_Send函数:上次数据未发送,等待DELAY_WAITSENDING仍未发送,强制覆盖!");
}
}
}
}
// 设置数据尚未准备好标记,客户线程不能读取
EnterCriticalSection(&m_udtTcpServer.ctsClient);
m_udtTcpServer.bIsReady = FALSE;
LeaveCriticalSection(&m_udtTcpServer.ctsClient);
strcpy(m_udtTcpServer.udtPassInfo.pchDeviceId,pchDeviceId);
strcpy(m_udtTcpServer.udtPassInfo.pchPlate,pchPlate);
strcpy(m_udtTcpServer.udtPassInfo.pchPassTime,pchTime);
m_udtTcpServer.udtPassInfo.iRoadWay = iRoadWay;
m_udtTcpServer.udtPassInfo.iSpeed = iSpeed;
m_udtTcpServer.udtPassInfo.iSpeedLimit = iSpeedLimit;
m_udtTcpServer.udtPassInfo.iImageNumber = iImageNumber;
m_udtTcpServer.udtPassInfo.dwDeviceState = dwDeviceState;
m_udtTcpServer.udtPassInfo.dwImageNearSize = dwImageNearSize;
m_udtTcpServer.udtPassInfo.dwImageFullSize = dwImageFullSize;
iImageNearPacketLength = (int)dwImageNearSize + PACKET_HEADER_LENGTH; // 特写图子包长度
iImageFullPacketLength = (int)dwImageFullSize + PACKET_HEADER_LENGTH; // 全景图子包长度
// 往发送缓存区填充内容
// 实时车辆信息包格式:T L 通行信息子包(T-L-V) 特写图子包(T-L-V) 全景图子包(T-L-V)
m_udtTcpServer.iSendTotalLength = PACKET_HEADER_LENGTH + PACKET_PASSINFO_LENGTH + iImageNearPacketLength + iImageFullPacketLength;
// 1-总类型已经填充
// 2-填充总长度:实时车辆信息 4BYTE
memcpy(&m_udtTcpServer.pbtSendBuf[PACKET_TYPE_LENGTH],&m_udtTcpServer.iSendTotalLength,PACKET_LENGTH_LENGTH);
// 3&4-子包1类型、长度已填充
// 5-子包1数据
iCursor = PACKET_HEADER_LENGTH * 2;
memcpy(&m_udtTcpServer.pbtSendBuf[iCursor],&m_udtTcpServer.udtPassInfo,sizeof(m_udtTcpServer.udtPassInfo));
// 6-子包2类型
iCursor = PACKET_HEADER_LENGTH + PACKET_PASSINFO_LENGTH;
memcpy(&m_udtTcpServer.pbtSendBuf[iCursor],&TYPE_IMAGENEAR,PACKET_TYPE_LENGTH);
// 7-子包2长度
iCursor += PACKET_TYPE_LENGTH;
memcpy(&m_udtTcpServer.pbtSendBuf[iCursor],&iImageNearPacketLength,PACKET_LENGTH_LENGTH);
// 8-子包2数据
iCursor += PACKET_LENGTH_LENGTH;
memcpy(&m_udtTcpServer.pbtSendBuf[iCursor],pbtImageNear,dwImageNearSize);
// 9-子包3类型
iCursor += dwImageNearSize;
memcpy(&m_udtTcpServer.pbtSendBuf[iCursor],&TYPE_IMAGEFULL,PACKET_TYPE_LENGTH);
// 10-子包3长度
iCursor += PACKET_TYPE_LENGTH;
memcpy(&m_udtTcpServer.pbtSendBuf[iCursor],&iImageFullPacketLength,PACKET_LENGTH_LENGTH);
// 11-子包3数据
iCursor += PACKET_LENGTH_LENGTH;
memcpy(&m_udtTcpServer.pbtSendBuf[iCursor],pbtImageFull,dwImageFullSize);
// 设置数据准备好标记,客户线程可以读取
EnterCriticalSection(&m_udtTcpServer.ctsClient);
m_udtTcpServer.bIsReady = TRUE;
m_udtTcpServer.dwPutTimeCount = GetTickCount();
LeaveCriticalSection(&m_udtTcpServer.ctsClient);
m_udtTcpServer.dwPutCount++;
return VALUE_ZERO;
}
/************************************************************************
Function int GDW_VM2003Server_Debug:
Get Debug Info
Input:
OutPut:
WORD* wListenPort 服务器监听的端口号
int* iListenAlive 监听线程是否存活 1 存活;0 无
int* iClientAlive 客户处理线程是否存活 1 存活;0 无
BYTE* pbtClientIp 客户端的IP地址,至少申请4个字节(*.*.*.*),每个字节放一个*
DWORD* dwDataPutCount 数据有效放入发送区记数
DWORD* dwDataSendCount 数据有效发送记数
return:
GDW_VM2003Server_Start must call success first;
If no error occurs, returns zero,other returns no zero;
Update:
Version Date Author Description
1.0 2008-03-20 Shi Mingjie Create
************************************************************************/
extern "C" int PASCAL EXPORT GDW_VM2003Server_Debug(WORD* wListenPort,
int* iListenAlive,
int* iClientAlive,
BYTE* pbtClientIp,
DWORD* dwDataPutCount,
DWORD* dwDataSendCount)
{
AFX_MANAGE_STATE(AfxGetStaticModuleState());
if (!m_bInitSuccess)
{
return -1;
}
*wListenPort = m_udtTcpServer.wListenPort;
if (m_udtTcpServer.bListenAlive)
{
*iListenAlive = VALUE_ALIVE;
}
else
{
*iListenAlive = VALUE_ZERO;
}
if (m_udtTcpServer.bClientAlive)
{
*iClientAlive = VALUE_ALIVE;
}
else
{
*iClientAlive = VALUE_ZERO;
}
if (NULL != pbtClientIp)
{
memcpy(pbtClientIp,m_udtTcpServer.btClientIp,sizeof(m_udtTcpServer.btClientIp));
}
*dwDataPutCount = m_udtTcpServer.dwPutCount;
*dwDataSendCount = m_udtTcpServer.dwSendCount;
return VALUE_ZERO;
}
/************************************************************************
Function int GDW_VM2003Server_End:
End listen thread and client thread
Input:
OutPut:
return:
GDW_VM2003Server_Start must call success first;
If no error occurs, returns zero,other returns no zero;
Update:
Version Date Author Description
1.0 2008-03-19 Shi Mingjie Create
************************************************************************/
extern "C" int PASCAL EXPORT GDW_VM2003Server_End()
{
AFX_MANAGE_STATE(AfxGetStaticModuleState());
DWORD dwExitCode;
DWORD nWaitMilliSecond;
int iReturn = VALUE_ZERO; // 函数返回值
int iRetTmp;
if (!m_bInitSuccess)
{
return -1;
}
m_udtTcpServer.bIsQuitListen = TRUE;
m_udtTcpServer.evtClientEnd.SetEvent(); // 通知客户线程终止
if (m_udtTcpServer.bListenAlive)
{
shutdown(m_udtTcpServer.sckListen,SD_BOTH);
Sleep(DELAY_SHUTDOWNSOCKET);
closesocket(m_udtTcpServer.sckListen);
m_udtTcpServer.sckListen = NULL;
}
if (m_udtTcpServer.bClientAlive)
{
shutdown(m_udtTcpServer.sckClient,SD_BOTH);
Sleep(DELAY_SHUTDOWNSOCKET);
closesocket(m_udtTcpServer.sckClient);
m_udtTcpServer.sckClient = NULL;
}
Sleep(DELAY_SHUTDOWNSOCKET);
nWaitMilliSecond = 0;
if (m_udtTcpServer.threadListen != NULL)
{
for ( ;; )
{
iRetTmp = ::GetExitCodeThread(m_udtTcpServer.threadListen->m_hThread, &dwExitCode);
if (iRetTmp != 0)
{
if (dwExitCode != STILL_ACTIVE)
{
break;
}
else
{
Sleep(DELAY_WAITQUIT);
nWaitMilliSecond += 1;
if ((nWaitMilliSecond * DELAY_WAITQUIT) > LIMIT_WAITQUIT)
{
iRetTmp = TerminateThread(m_udtTcpServer.threadListen->m_hThread,1);
if (iRetTmp != 0) // 终止监听线程成功
{
break;
}
else // 终止监听线程失败
{
iReturn = -101;
break;
}
}
}
}
else
{
break;
}
}
}
m_udtTcpServer.threadListen = NULL;
Sleep(DELAY_WAITQUIT);
if (m_udtTcpServer.bClientAlive)
{
iRetTmp = TerminateThread(m_udtTcpServer.threadClient->m_hThread,1000);
if (0 == iRetTmp)
{
iReturn = -102;
}
}
m_udtTcpServer.threadClient= NULL;
DeleteCriticalSection(&m_udtTcpServer.ctsListen); // 删除临界区
DeleteCriticalSection(&m_udtTcpServer.ctsClient); // 删除临界区
WSACleanup();
m_bInitSuccess = FALSE;
if (m_udtTcpServer.bIsDebug)
{
WriteToLog(0,"[终止]------------------信息分割线------------------");
}
return iReturn;
}
/************************************************************************
Function int TcpListenThread:
listen thread
Input:
UDT_TCPCommunicationServer m_udtTcpServer
OutPut:
UDT_TCPCommunicationServer m_udtTcpServer
return:
If no error occurs, returns zero,other returns no zero;
Update:
Version Date Author Description
1.0 2008-03-19 Shi Mingjie Create
************************************************************************/
UINT TcpListenThread(LPVOID pParam)
{
struct UDT_TCPCommunicationServer *pudtTcpListen = (struct UDT_TCPCommunicationServer *)pParam;
struct sockaddr_in serverAddr;
int iRetTmp = 0;
int iLength = 0;
SOCKET sckAccept = NULL; // 用于临时accept socket
DWORD dwWaitCount = 0;
int iErrNumber = 0;
int iRetval = 0;
WORD wVersion;
WSADATA wsaData;
wVersion = MAKEWORD(1,1);
iRetval = WSAStartup(wVersion,&wsaData);
if (iRetval != 0)
{
if (pudtTcpListen->bIsDebug)
{
WriteToLog(iRetval,"TcpListenThread线程:没有发现可用的socket通讯库!退出!");
}
return -1;
}
if ((LOBYTE(wsaData.wVersion) != 1) || (HIBYTE(wsaData.wVersion) != 1))
{
if (pudtTcpListen->bIsDebug)
{
WriteToLog("TcpListenThread线程:不是正确的socket通讯库版本!退出!");
}
iRetval = WSACleanup();
if (iRetval == SOCKET_ERROR)
{
if (pudtTcpListen->bIsDebug)
{
WriteToLog(iRetval,"TcpListenThread线程:WSACleanup失败!退出!");
}
}
return -2;
}
pudtTcpListen->sckListen = socket(AF_INET, SOCK_STREAM, 0);
if (pudtTcpListen->sckListen == INVALID_SOCKET)
{
if (pudtTcpListen->bIsDebug)
{
WriteToLog("TcpListenThread线程:socket 函数返回失败!退出!");
}
iRetval = WSACleanup();
if (iRetval == SOCKET_ERROR)
{
if (pudtTcpListen->bIsDebug)
{
WriteToLog(iRetval,"TcpListenThread线程:WSACleanup失败!退出!");
}
}
return -3;
}
serverAddr.sin_port = htons(pudtTcpListen->wListenPort);
serverAddr.sin_family = AF_INET;
serverAddr.sin_addr.s_addr = htonl(INADDR_ANY);
iRetval = bind(pudtTcpListen->sckListen, (LPSOCKADDR)&serverAddr, sizeof(serverAddr));
if (iRetval == SOCKET_ERROR)
{
if (pudtTcpListen->bIsDebug)
{
WriteToLog("TcpListenThread线程:Bind 函数返回失败!退出!");
}
return -4;
}
iLength = sizeof(serverAddr);
if (getsockname(pudtTcpListen->sckListen,(struct sockaddr *)&serverAddr,&iLength) == SOCKET_ERROR)
{
if (pudtTcpListen->bIsDebug)
{
WriteToLog("TcpListenThread线程:getsockname 函数返回失败!退出!");
}
return -5;
}
iRetval = listen(pudtTcpListen->sckListen, LIMIT_MAXLISTENCLIENT);
if (iRetval == SOCKET_ERROR)
{
if (pudtTcpListen->bIsDebug)
{
WriteToLog("TcpListenThread线程:listen 函数返回失败!退出!");
}
return -6;
}
pudtTcpListen->bListenAlive = TRUE; // 监听线程状态为存活状态
if (pudtTcpListen->bIsDebug)
{
WriteToLog(0,"TcpListenThread线程:监听成功。");
}
for (; ; )
{
if (NULL != sckAccept)
{
sckAccept = NULL;
}
sckAccept = accept(pudtTcpListen->sckListen,(struct sockaddr *)&serverAddr,&iLength);
if (INVALID_SOCKET == sckAccept)
{
if (pudtTcpListen->bIsQuitListen)
{
if (pudtTcpListen->bIsDebug)
{
WriteToLog(0,"TcpListenThread线程:接收到退出指令。");
}
break;
}
else
{
iErrNumber = WSAGetLastError();
WriteToLog(iErrNumber,"TcpListenThread线程:没有退出命令时accept 函数返回无效Socket!(记录WSAGetLastError)");
// 2008-03-27 下面这段被注释的代码没有达到预期的作用
/*****************************************************************
WriteToLog("TcpListenThread线程:重新监听");
shutdown(pudtTcpListen->sckListen,SD_BOTH);
Sleep(DELAY_SHUTDOWNSOCKET);
closesocket(pudtTcpListen->sckListen);
pudtTcpListen->sckListen = NULL;
pudtTcpListen->sckListen = socket(AF_INET, SOCK_STREAM, 0);
if (INVALID_SOCKET == pudtTcpListen->sckListen)
{
WriteToLog("TcpListenThread线程:socket 失败!");
}
iRetval = bind(pudtTcpListen->sckListen, (LPSOCKADDR)&serverAddr, sizeof(serverAddr));
if (SOCKET_ERROR == iRetval)
{
WriteToLog("TcpListenThread线程:bind 失败!");
}
iLength = sizeof(serverAddr);
if (getsockname(pudtTcpListen->sckListen,(struct sockaddr *)&serverAddr,&iLength) == SOCKET_ERROR)
{
WriteToLog("TcpListenThread线程:getsockname 失败!");
}
iRetval = listen(pudtTcpListen->sckListen, LIMIT_MAXLISTENCLIENT);
if (SOCKET_ERROR == iRetval)
{
WriteToLog("TcpListenThread线程:listen 失败!");
}
*****************************************************************/
}
}
else
{
if (pudtTcpListen->bClientAlive)
{
pudtTcpListen->evtClientEnd.SetEvent();
if (pudtTcpListen->bIsDebug)
{
WriteToLog("TcpListenThread线程:新连接进入时客户线程还存在,销毁!");
}
shutdown(pudtTcpListen->sckListen,SD_BOTH);
Sleep(DELAY_SHUTDOWNSOCKET);
closesocket(pudtTcpListen->sckListen);
pudtTcpListen->sckListen = NULL;
Sleep(DELAY_WAITQUIT * 3);
if (m_udtTcpServer.bClientAlive)
{
iRetTmp = TerminateThread(pudtTcpListen->threadClient,1000);
if (0 == iRetTmp)
{
WriteToLog("TcpListenThread线程:TerminateThread 函数销毁client线程失败!");
}
}
pudtTcpListen->bIsSending = FALSE;
pudtTcpListen->bClientAlive = FALSE;
pudtTcpListen->threadClient= NULL;
}
else
{
if (pudtTcpListen->bIsDebug)
{
WriteToLog(0,"TcpListenThread线程:创建新的客户线程。");
}
}
pudtTcpListen->sckClient = sckAccept;
pudtTcpListen->btClientIp[0] = serverAddr.sin_addr.S_un.S_un_b.s_b1;
pudtTcpListen->btClientIp[1] = serverAddr.sin_addr.S_un.S_un_b.s_b2;
pudtTcpListen->btClientIp[2] = serverAddr.sin_addr.S_un.S_un_b.s_b3;
pudtTcpListen->btClientIp[3] = serverAddr.sin_addr.S_un.S_un_b.s_b4;
pudtTcpListen->evtClientEnd.ResetEvent();
// 建立独立的数据接收处理线程
pudtTcpListen->threadClient = AfxBeginThread(TcpClientThread, pudtTcpListen, THREAD_PRIORITY_NORMAL, 0, CREATE_SUSPENDED);
pudtTcpListen->threadClient->m_bAutoDelete = FALSE;
pudtTcpListen->threadClient->ResumeThread();
Sleep(DELAY_WAITSUCCESS);
if (!pudtTcpListen->bClientAlive) // 如果创建的线程尚未完成启动则等待
{
dwWaitCount = 0;
for (; ; )
{
Sleep(DELAY_WAITSUCCESS);
if (pudtTcpListen->bClientAlive)
{
break;
}
dwWaitCount++;
if (DELAY_WAITSUCCESS * dwWaitCount > LIMIT_WAITQUIT)
{
if (pudtTcpListen->bIsDebug)
{
WriteToLog("TcpListenThread线程:超时未等待到client线程启动成功!");
}
break;
}
}
}
}
Sleep(DELAY_WAITSUCCESS);
}
closesocket(pudtTcpListen->sckListen);
pudtTcpListen->bListenAlive = FALSE; // 监听线程状态为静止状态
if (pudtTcpListen->bIsDebug)
{
WriteToLog(0,"TcpListenThread线程:监听线程关闭。");
}
return VALUE_ZERO;
}
/************************************************************************
Function int TcpClientThread:
client thread
Input:
UDT_TCPCommunicationServer m_udtTcpServer
OutPut:
UDT_TCPCommunicationServer m_udtTcpServer
return:
If no error occurs, returns zero,other returns no zero;
Update:
Version Date Author Description
1.0 2008-03-20 Shi Mingjie Create
************************************************************************/
UINT TcpClientThread(LPVOID pParam)
{
struct UDT_TCPCommunicationServer *pudtTcpClient = (struct UDT_TCPCommunicationServer *)pParam;
int iSendRetval;
BYTE pbtReceiveBuf[LIMIT_SENDBUFFERSIZE];
int iRealReceive = 0;
int iReceiveRetval = 0;
short siType = 0; // 接收到的包类型
int iLength = 0; // 接收到的包长度
DWORD dwTimeCountInterval; // 时间差值
const long TIMEOUT_RECEIVECHECK = 200000; // 单位为microseconds 微妙
struct timeval tvReceive={0,TIMEOUT_RECEIVECHECK};
fd_set fdReceive;
int iSelectRet; // Select函数的返回值
time_t unixTime; // Unix格式时间
struct tm *tmTime; // tm格式时间
SYSTEMTIME sysTime;
int iRet;
pudtTcpClient->bClientAlive = TRUE;
if (pudtTcpClient->bIsDebug)
{
WriteToLog(0,"TcpClientThread线程:数据处理开始。");
}
pudtTcpClient->dwSendTimeCount = GetTickCount(); // 预设为初始发送时间点
pudtTcpClient->dwHeartBeatTimeCount = GetTickCount(); // 预设为初始心跳时间点
for (; ;)
{
if (WAIT_OBJECT_0 == ::WaitForSingleObject(pudtTcpClient->evtClientEnd.m_hObject, 0))
{
if (pudtTcpClient->bIsDebug)
{
WriteToLog(0,"TcpClientThread线程:接收到信号量终止指令。");
}
break;
}
// 数据是否已经准备好并发送
if (pudtTcpClient->bIsReady)
{
EnterCriticalSection(&pudtTcpClient->ctsClient);
pudtTcpClient->bIsSending = TRUE;
LeaveCriticalSection(&pudtTcpClient->ctsClient);
dwTimeCountInterval = GetTickCount() - pudtTcpClient->dwPutTimeCount;
if (dwTimeCountInterval > LIMIT_DATAOVERTIME)
{
WriteToLog(dwTimeCountInterval,"TcpClientThread线程:超时数据删除不再发送。");
}
else
{
iSendRetval = send(pudtTcpClient->sckClient,(char *)pudtTcpClient->pbtSendBuf,pudtTcpClient->iSendTotalLength,0);
if (SOCKET_ERROR == iSendRetval)
{
if (pudtTcpClient->bIsDebug)
{
WriteToLog(iSendRetval,"TcpClientThread线程:实时车辆信息发送失败!");
}
}
else
{
pudtTcpClient->dwSendCount++;
if (pudtTcpClient->bIsDebug)
{
WriteToLog(iSendRetval,"TcpClientThread线程:实时车辆信息数据发送成功。");
}
}
}
EnterCriticalSection(&pudtTcpClient->ctsClient);
pudtTcpClient->bIsSending = FALSE;
pudtTcpClient->bIsReady = FALSE;
pudtTcpClient->dwSendTimeCount = GetTickCount();
LeaveCriticalSection(&pudtTcpClient->ctsClient);
}
// 接收心跳包并回复
FD_ZERO(&fdReceive);
FD_SET(pudtTcpClient->sckClient,&fdReceive);
iSelectRet = select(0,&fdReceive,NULL,NULL,&tvReceive);
switch (iSelectRet)
{
case SOCKET_ERROR: // SOCKET_ERROR if an error occurred.
pudtTcpClient->evtClientEnd.SetEvent();
WriteToLog(iSelectRet,"TcpClientThread线程:select 返回SOCKET_ERROR,置退出信号!");
break;
case VALUE_ZERO: // zero if the time limit expired
break;
default: // the total number of socket handles that are ready and contained in the fd_set structures
if (FD_ISSET(pudtTcpClient->sckClient,&fdReceive))
{
Sleep(DELAY_WAITSUCCESS);
iRealReceive = 0;
iReceiveRetval = recv(pudtTcpClient->sckClient,(char *)&pbtReceiveBuf[iRealReceive],PACKET_HEADER_LENGTH,0);
if (SOCKET_ERROR == iReceiveRetval)
{
pudtTcpClient->evtClientEnd.SetEvent();
WriteToLog(iReceiveRetval,"TcpClientThread线程:recv 返回SOCKET_ERROR,被迫关闭!");
break;
}
else if (VALUE_ZERO == iReceiveRetval) // If the connection has been gracefully closed, the return value is zero.
{
pudtTcpClient->evtClientEnd.SetEvent();
WriteToLog(iReceiveRetval,"TcpClientThread线程:recv 返回VALUE_ZERO,客户端连接完美关闭!");
break;
}
else // If no error occurs, recv returns the number of bytes received.
{
siType = 0;
iLength = 0;
if (PACKET_HEADER_LENGTH != iReceiveRetval)
{
if (pudtTcpClient->bIsDebug)
{
WriteToLog(iReceiveRetval,"TcpClientThread线程:1次未接收满PACKET_HEADER_LENGTH字节。");
}
}
else
{
memcpy(&siType,&pbtReceiveBuf[VALUE_ZERO],PACKET_TYPE_LENGTH); //包类型 2BYTE
memcpy(&iLength,&pbtReceiveBuf[PACKET_TYPE_LENGTH],PACKET_LENGTH_LENGTH); //包长度 4BYTE
iRealReceive += iReceiveRetval;
switch (siType)
{
case TYPE_HEARTBEAT: // 心跳包
if (pudtTcpClient->bIsDebug)
{
WriteToLog(0,"TcpClientThread线程:接收到心跳包。");
}
pudtTcpClient->dwHeartBeatTimeCount = GetTickCount();
// 回复心跳包
iSendRetval = send(pudtTcpClient->sckClient,(char *)pudtTcpClient->pbtSendBufHeartBeat,PACKET_HEADER_LENGTH,0);
if (SOCKET_ERROR == iSendRetval)
{
if (pudtTcpClient->bIsDebug)
{
WriteToLog(iSendRetval,"TcpClientThread线程:心跳包发送失败!");
}
}
else
{
if (pudtTcpClient->bIsDebug)
{
WriteToLog(iSendRetval,"TcpClientThread线程:心跳包发送成功。");
}
}
break;
case TYPE_TIME: // 时间包
if (pudtTcpClient->bIsDebug)
{
WriteToLog(0,"TcpClientThread线程:接收到校时。");
}
iReceiveRetval = recv(pudtTcpClient->sckClient,(char *)&pbtReceiveBuf[iRealReceive],iLength-PACKET_HEADER_LENGTH,0);
if (SOCKET_ERROR == iReceiveRetval)
{
if (pudtTcpClient->bIsDebug)
{
WriteToLog("TcpClientThread线程:接收时间数据发生错误!");
}
break;
}
else if (VALUE_ZERO == iReceiveRetval)
{
pudtTcpClient->evtClientEnd.SetEvent();
WriteToLog(0,"TcpClientThread线程:recv 返回VALUE_ZERO,客户端连接完美关闭!");
break;
}
else
{
memcpy(&unixTime,&pbtReceiveBuf[iRealReceive],sizeof(unixTime));
if (pudtTcpClient->bIsDebug)
{
WriteToLog(unixTime,"TcpClientThread线程:标准时间。");
}
tmTime = gmtime(&unixTime);
sysTime.wYear = tmTime->tm_year + 1900; // tm struct: current year minus 1900
sysTime.wMonth = tmTime->tm_mon + 1; // tm struct: Month (0 – 11; January = 0)
sysTime.wDay = tmTime->tm_mday;
sysTime.wHour = tmTime->tm_hour;
sysTime.wMinute = tmTime->tm_min;
sysTime.wSecond = tmTime->tm_sec;
sysTime.wMilliseconds = 500;
iRet = SetSystemTime(&sysTime);
if (VALUE_ZERO == iRet) // 校时失败
{
if (pudtTcpClient->bIsDebug)
{
WriteToLog("TcpClientThread线程:校时失败!");
}
}
else // 校时成功,返回服务器时间
{
if (pudtTcpClient->bIsDebug)
{
WriteToLog(iRet,"TcpClientThread线程:校时成功。返回当前服务器时间。");
}
time(&unixTime);
memcpy(&pudtTcpClient->pbtSendBufTime[PACKET_HEADER_LENGTH],&unixTime,sizeof(unixTime));
// 回复校时包
iSendRetval = send(pudtTcpClient->sckClient,(char *)pudtTcpClient->pbtSendBufTime,PACKET_TIME_LENGTH,0);
if (SOCKET_ERROR == iSendRetval)
{
if (pudtTcpClient->bIsDebug)
{
WriteToLog(iSendRetval,"TcpClientThread线程:校时成功回复包发送失败!");
}
}
else
{
if (pudtTcpClient->bIsDebug)
{
WriteToLog(iSendRetval,"TcpClientThread线程:校时成功回复包发送成功。");
}
}
} // end else adjusttime ok
} // end else receive time packed ok
break;
default:
WriteToLog(siType,"TcpClientThread线程:接收到其它包。");
break;
}
}
}
} // end if (FD_ISSET(pudtTcpClient->sckClient,&fdReceive))
else
{
if (pudtTcpClient->bIsDebug)
{
WriteToLog("TcpClientThread线程:FD_ISSET 返回ZERO!");
}
}
break;
}
dwTimeCountInterval = GetTickCount() - pudtTcpClient->dwHeartBeatTimeCount;
if ((dwTimeCountInterval > LIMIT_HEARTBEAT) && (GetTickCount() > pudtTcpClient->dwHeartBeatTimeCount))
{
WriteToLog(dwTimeCountInterval,"TcpClientThread线程:心跳超时,主动退出,等待重连!");
pudtTcpClient->evtClientEnd.SetEvent();
}
Sleep(DELAY_WAITSUCCESS);
}
shutdown(pudtTcpClient->sckClient,SD_BOTH);
closesocket(pudtTcpClient->sckClient);
pudtTcpClient->bIsSending = FALSE;
pudtTcpClient->bClientAlive = FALSE;
return VALUE_ZERO;
}