10/07
16
vim -b file
then
:%!xxd
10/07
14
/* add include files */
#include "winsock2.h"
#include "afxmt.h"
#include "Mmsystem.h"
#include
#include
#include
#include
////////////////////////////////////////////////////////////////////////
// 传输协议根据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 = 150; // 检查网络状态的间隔
const int LIMIT_WAITRECEIVE = 200; // 等待接收最长时间
const int LIMIT_WAITQUIT = 200; // 退出延时限制
const int LIMIT_DATAOVERTIME = 10000; // 数据超时限制(保存在发送缓存中的)
const int LIMIT_HEARTBEAT = 30000; // 心跳间隔超时限制(超出认为服务器异常)
const int LIMIT_MAXLISTENCLIENT = 10; // 最大监听客户数
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 WM_VEHICLEPASS = 0x401; // 车辆经过
const int WM_TCPCONNECT = 0x402; // 建立TCP连接
const int WM_TCPDISCONNECT = 0x403; // TCP连接断开
const int VALUE_ZERO = 0; // 零值
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_TCPCommunicationClient // 通讯控制参数结构体
{
HWND hWndProcess; // 发送消息所需句柄
DWORD dwMsgDataReady; // 数据准备好消息 默认0x401
DWORD dwMsgConnectSuccess; // 网络连接成功消息 默认0x402
DWORD dwMsgDisconnect; // 网络断开消息 默认0x403
DWORD dwServerIp; // 服务器IP
WORD wServerPort; // 服务器监听端口
BOOL bIsDebug; // 是否开启调试模式
BOOL bConnectActive; // 网络是否处于活动状态
BOOL bThreadClientAlive; // 线程是否存活
BOOL bDataIsReady; // 数据是否准备好
BOOL bIsGetingData; // 是否正在取数据
BOOL bIsAdjustTime; // 是否校时
DWORD dwReceiveTime; // 接收到的时间 unix时间格式
DWORD dwHeartBeatTime; // 心跳时间点
DWORD dwHeartBeatCount; // 心跳次数
CWinThread* threadInspect; // 监测线程句柄
CEvent evtInspectEnd; // 监测线程信号量(通知对应线程结束)
SOCKET sckClient; // 客户端线程通讯使用的Socket句柄
CWinThread* threadClient; // 客户端线程句柄
CEvent evtClientEnd; // 客户端线程信号量(通知对应线程结束)
CRITICAL_SECTION ctsClient; // 客户端临界区
struct UDT_PassInfo udtPassInfo; // 车辆信息数据包(不包括图片)
BYTE pbtSendBufHeartBeat[PACKET_HEADER_LENGTH]; // 心跳包发送缓存区
BYTE pbtSendBufTime[PACKET_TIME_LENGTH]; // 时间包发送缓存区
BYTE* pbtImageNearBuf; // 特写图片数据缓存区
BYTE* pbtImageFullBuf; // 全景图片数据缓存区
}UDT_TCPCommunicationClient;
static struct UDT_TCPCommunicationClient m_udtTcpClient; // 模块级变量,控制通讯
static BOOL m_bInitSuccess = FALSE; // 初始化成功标记,除Connect函数外均需要初始化成功才能调用
int WriteToLog(int iCueNumber,TCHAR *szMsg);
int WriteToLog(TCHAR *szMsg);
int WriteBinaryFile(BYTE* pbtBuffer,DWORD dwFileSize,char *pchFileName);
int TcpReceive(SOCKET sckClient,
BYTE *pchBuffer,
int iOffset,
int iReceiveSize,
int iOverTime);
UINT TcpInspectThread(LPVOID pParam);
UINT TcpServerThread(LPVOID pParam);
/*******************************************************
int GDW_VM2003_Connect(DWORD hWnd,
DWORD dwMsgDataReady,
DWORD dwMsgConnectSuccess,
DWORD dwMsgDisconnect);
int GDW_VM2003_GetVehicleInfo(char* pchPlate,
char* pchTime,
BYTE* pbtImageBin,
BYTE* pbtImagePlate,
DWORD* dwImagePlateSize,
BYTE* pbtImageNear,
DWORD* dwImageNearSize,
BYTE* pbtImageFull,
DWORD* dwImageFullSize,
char* pchDeviceId,
int* iRoadWay,
int* iSpeed,
int* iSpeedLimit,
DWORD* dwDeviceState);
int GDW_VM2003_AdjustTime(char* pchTime);
int GDW_VM2003_Disconnect();
*******************************************************/
//
// 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_Client.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_VM2003_Connect:
Start InspectThread && Server Thread
Input:
DWORD hWnd 窗口句柄,不能为VALUE_ZERO
DWORD dwMsgDataReady 数据准备好消息,必须大于0x400,默认0x401
DWORD dwMsgConnectSuccess 网络连接成功,必须大于0x401,默认0x402
DWORD dwMsgDisconnect 网络连接断开,必须大于0x402,默认0x403
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_VM2003_Connect(DWORD hWnd,
DWORD dwMsgDataReady,
DWORD dwMsgConnectSuccess,
DWORD dwMsgDisconnect)
{
AFX_MANAGE_STATE(AfxGetStaticModuleState());
const DWORD dwMsgMin = 0x400; // 可用消息值临界值(自定义消息必须比该值大)
int i = 0;
int iLastSperate = 0;
TCHAR chCurPath[MAX_PATH];
HANDLE hWndFile;
WIN32_FIND_DATA fileFind;
int iIsDebugToLog;
TCHAR chIpBuffer[128];
int iServerPort;
DWORD dwReadLength;
int iRetval = 0;
WORD wVersion;
WSADATA wsaData;
if (VALUE_ZERO == hWnd) // 为无效句柄则立即返回
{
return 2;
}
if ((dwMsgDataReady <= dwMsgMin) ||
(dwMsgConnectSuccess <= dwMsgMin + 1) ||
(dwMsgDisconnect <= dwMsgMin + 2) ||
(dwMsgDataReady == dwMsgConnectSuccess) ||
(dwMsgDataReady == dwMsgDisconnect) ||
(dwMsgConnectSuccess == dwMsgDisconnect))
{
m_udtTcpClient.dwMsgDataReady = dwMsgMin + 1;
m_udtTcpClient.dwMsgConnectSuccess = dwMsgMin + 2;
m_udtTcpClient.dwMsgDisconnect = dwMsgMin + 3;
}
else
{
m_udtTcpClient.dwMsgDataReady = dwMsgDataReady;
m_udtTcpClient.dwMsgConnectSuccess = dwMsgConnectSuccess;
m_udtTcpClient.dwMsgDisconnect = dwMsgDisconnect;
}
m_udtTcpClient.hWndProcess = (HWND)hWnd; // 保存消息处理句柄
// 读取配置参数:服务器Ip、端口、是否调试
GetModuleFileName(GetModuleHandle(NULL),chCurPath,MAX_PATH-20);
for (i=0; 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_client.ini");
hWndFile = FindFirstFile(chCurPath,&fileFind);
FindClose(hWndFile);
if (INVALID_HANDLE_VALUE != hWndFile)
{
dwReadLength = GetPrivateProfileString("Client","serverIp","0.0.0.0",chIpBuffer,128,chCurPath);
if ((dwReadLength >= 7) && (dwReadLength <= 15))
{
m_udtTcpClient.dwServerIp = inet_addr(chIpBuffer);
}
else
{
return 11;
}
iServerPort = GetPrivateProfileInt("Client","serverPort",5001,chCurPath);
if ((iServerPort > 0) && (iServerPort <= 65536))
{
m_udtTcpClient.wServerPort = iServerPort;
}
else
{
return 12;
}
iIsDebugToLog = GetPrivateProfileInt("Client","DebugToLog",0,chCurPath);
if (iIsDebugToLog != 0)
{
m_udtTcpClient.bIsDebug = TRUE;
}
}
else
{
return -1;
}
}
else
{
return -2;
}
// 初始化结构体UDT_TCPCommunicationClient参数
m_udtTcpClient.bConnectActive = FALSE; // 网络是否处于活动状态
m_udtTcpClient.bThreadClientAlive = FALSE; // 线程是否存活
m_udtTcpClient.bDataIsReady = FALSE; // 数据是否准备好
m_udtTcpClient.bIsGetingData = FALSE; // 是否正在取数据
m_udtTcpClient.bIsAdjustTime = FALSE; // 是否校时
m_udtTcpClient.dwReceiveTime = 0; // 收到的服务器回复时间 unix时间格式
m_udtTcpClient.dwHeartBeatTime = 0; // 心跳时间点
m_udtTcpClient.dwHeartBeatCount = 0; // 心跳次数
m_udtTcpClient.sckClient = NULL; // 客户端线程通讯使用的Socket句柄
m_udtTcpClient.threadClient = NULL; // 客户端线程句柄
m_udtTcpClient.pbtImageNearBuf = NULL; // 特写图片缓存区指针指向NULL
m_udtTcpClient.pbtImageFullBuf = NULL; // 全景图片缓存区指针指向NULL
// 心跳包格式: T L
memcpy(&m_udtTcpClient.pbtSendBufHeartBeat[VALUE_ZERO],&TYPE_HEARTBEAT,PACKET_TYPE_LENGTH);
memcpy(&m_udtTcpClient.pbtSendBufHeartBeat[PACKET_TYPE_LENGTH],&PACKET_HEADER_LENGTH,PACKET_LENGTH_LENGTH);
// 校时包格式: T L V,初始化时复制T L
memcpy(&m_udtTcpClient.pbtSendBufTime[VALUE_ZERO],&TYPE_TIME,PACKET_TYPE_LENGTH);
memcpy(&m_udtTcpClient.pbtSendBufTime[PACKET_TYPE_LENGTH],&PACKET_TIME_LENGTH,PACKET_LENGTH_LENGTH);
InitializeCriticalSection(&m_udtTcpClient.ctsClient); //临界区初始化
m_udtTcpClient.evtInspectEnd.ResetEvent();
m_udtTcpClient.evtClientEnd.ResetEvent(); // Sets the state of the event to nonsignaled
wVersion = MAKEWORD(1,1);
iRetval = WSAStartup(wVersion,&wsaData);
if (0 != iRetval)
{
if (m_udtTcpClient.bIsDebug)
{
WriteToLog(iRetval,"GDW_VM2003_Connect:没有发现可用的socket通讯库!退出!");
}
return -3;
}
if ((LOBYTE(wsaData.wVersion) != 1) || (HIBYTE(wsaData.wVersion) != 1))
{
if (m_udtTcpClient.bIsDebug)
{
WriteToLog("GDW_VM2003_Connect:不是正确的socket通讯库版本!退出!");
}
iRetval = WSACleanup();
if (SOCKET_ERROR == iRetval)
{
if (m_udtTcpClient.bIsDebug)
{
WriteToLog(iRetval,"GDW_VM2003_Connect:WSACleanup失败!");
}
}
return -4;
}
m_udtTcpClient.sckClient = socket(AF_INET, SOCK_STREAM, 0);
if (INVALID_SOCKET == m_udtTcpClient.sckClient)
{
if (m_udtTcpClient.bIsDebug)
{
WriteToLog("GDW_VM2003_Connect:socket 函数返回失败!退出!");
}
iRetval = WSACleanup();
if (SOCKET_ERROR == iRetval)
{
if (m_udtTcpClient.bIsDebug)
{
WriteToLog(iRetval,"GDW_VM2003_Connect:WSACleanup失败!");
}
}
return -5;
}
if (m_udtTcpClient.bIsDebug)
{
WriteToLog(0,"[启动]------------------信息分割线------------------");
}
// 建立监测线程
m_udtTcpClient.threadInspect = AfxBeginThread(TcpInspectThread, &m_udtTcpClient, THREAD_PRIORITY_NORMAL, 0, CREATE_SUSPENDED);
m_udtTcpClient.threadInspect->m_bAutoDelete = FALSE;
m_udtTcpClient.threadInspect->ResumeThread();
// 建立通讯服务线程
m_udtTcpClient.threadClient = AfxBeginThread(TcpServerThread, &m_udtTcpClient, THREAD_PRIORITY_NORMAL, 0, CREATE_SUSPENDED);
m_udtTcpClient.threadClient->m_bAutoDelete = FALSE;
m_udtTcpClient.threadClient->ResumeThread();
m_bInitSuccess = TRUE; // 初始化已经成功
return VALUE_ZERO;
}
/************************************************************************
Function int GDW_VM2003_GetVehicleInfo:
Get Vehicle Info
Input:
OutPut:
char* pchPlate 车牌号码 申请至少20字节空间,不允许NULL
char* pchTime 通行时间 申请至少24字节空间,不允许NULL
BYTE* pbtImageBin 车牌二值化图,不允许NULL
BYTE* pbtImagePlate 车牌彩色图,不允许NULL
DWORD* dwImagePlateSize 车牌彩色图大小
BYTE* pbtImageNear 特写车辆图
DWORD* dwImageNearSize 特写车辆图大小
BYTE* pbtImageFull 全景车辆图
DWORD* dwImageFullSize 全景车辆图大小
char* pchDeviceId 申请至少20字节空间,不允许NULL
int* iRoadWay 车道号
int* iSpeed 车速
int* iSpeedLimit 限速
DWORD* dwDeviceState 设备状态
return:
GDW_VM2003_Connect 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_VM2003_GetVehicleInfo(char* pchPlate,
char* pchTime,
BYTE* pbtImageBin,
BYTE* pbtImagePlate,
DWORD* dwImagePlateSize,
BYTE* pbtImageNear,
DWORD* dwImageNearSize,
BYTE* pbtImageFull,
DWORD* dwImageFullSize,
char* pchDeviceId,
int* iRoadWay,
int* iSpeed,
int* iSpeedLimit,
DWORD* dwDeviceState)
{
AFX_MANAGE_STATE(AfxGetStaticModuleState());
if (!m_bInitSuccess)
{
return -1;
}
if (!m_udtTcpClient.bDataIsReady)
{
return 10;
}
// 参数有空指针直接返回
if ((NULL == pchDeviceId) ||
(NULL == pchPlate) ||
(NULL == pchTime) ||
(NULL == pbtImageNear) ||
(NULL == pbtImageFull))
{
return 3;
}
// 设置数据正在读取标记,线程写入等待
EnterCriticalSection(&m_udtTcpClient.ctsClient);
m_udtTcpClient.bIsGetingData = TRUE;
LeaveCriticalSection(&m_udtTcpClient.ctsClient);
strcpy(pchPlate,m_udtTcpClient.udtPassInfo.pchPlate);
strcpy(pchTime,m_udtTcpClient.udtPassInfo.pchPassTime);
strcpy(pchDeviceId,m_udtTcpClient.udtPassInfo.pchDeviceId);
*iRoadWay = m_udtTcpClient.udtPassInfo.iRoadWay;
*iSpeed = m_udtTcpClient.udtPassInfo.iSpeed ;
*iSpeedLimit = m_udtTcpClient.udtPassInfo.iSpeedLimit;
*dwDeviceState = m_udtTcpClient.udtPassInfo.dwDeviceState;
*dwImageNearSize = m_udtTcpClient.udtPassInfo.dwImageNearSize;
*dwImageFullSize = m_udtTcpClient.udtPassInfo.dwImageFullSize;
memcpy(pbtImageNear,m_udtTcpClient.pbtImageNearBuf,m_udtTcpClient.udtPassInfo.dwImageNearSize);
memcpy(pbtImageFull,m_udtTcpClient.pbtImageFullBuf,m_udtTcpClient.udtPassInfo.dwImageFullSize);
// pbtImageBin pbtImagePlate dwImagePlateSize 不赋值
// 设置数据读取完成标记,线程允许写入
EnterCriticalSection(&m_udtTcpClient.ctsClient);
m_udtTcpClient.bIsGetingData = FALSE;
m_udtTcpClient.bDataIsReady = FALSE;
LeaveCriticalSection(&m_udtTcpClient.ctsClient);
return VALUE_ZERO;
}
/************************************************************************
Function int GDW_VM2003_AdjustTime:
adjust server time
Input:
pchTime must format "yyyyMMddHHmmss"(兼容原定义接口,此参数可不赋值)
OutPut:
return:
GDW_VM2003_Connect 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_VM2003_AdjustTime(char* pchTime)
{
AFX_MANAGE_STATE(AfxGetStaticModuleState());
time_t unixTime;
if (!m_bInitSuccess)
{
return 1;
}
if (!m_udtTcpClient.bConnectActive)
{
return 11;
}
/*********************************************************************
if (NULL == pchTime)
{
return 2;
}
if (14 != strlen(pchTime))
{
return 12;
}
struct tm tmTime;
int iASCIIZero = '0';
tmTime.tm_sec = (pchTime[1]-iASCIIZero) * 10 + (pchTime[0]-iASCIIZero);
tmTime.tm_min = (pchTime[3]-iASCIIZero) * 10 + (pchTime[2]-iASCIIZero);
tmTime.tm_hour = (pchTime[5]-iASCIIZero) * 10 + (pchTime[4]-iASCIIZero);
tmTime.tm_mday = (pchTime[7]-iASCIIZero) * 10 + (pchTime[6]-iASCIIZero);
tmTime.tm_mon = (pchTime[9]-iASCIIZero) * 10 + (pchTime[8]-iASCIIZero);
tmTime.tm_year = (pchTime[13]-iASCIIZero) * 1000 + (pchTime[12]-iASCIIZero) * 100 + (pchTime[11]-iASCIIZero) * 10 + (pchTime[10]-iASCIIZero);
if ((tmTime.tm_sec < 0) || (tmTime.tm_sec > 59))
{
return 8;
}
if ((tmTime.tm_min < 0) || (tmTime.tm_min > 59))
{
return 7;
}
if ((tmTime.tm_hour < 0) || (tmTime.tm_hour > 23))
{
return 6;
}
if ((tmTime.tm_mday < 1) || (tmTime.tm_mday > 31))
{
return 5;
}
if ((tmTime.tm_mon < 1) || (tmTime.tm_mon > 12))
{
return 4;
}
if ((tmTime.tm_year < 2000) || (tmTime.tm_year > 2099))
{
return 3;
}
unixTime = mktime(&tmTime);
*********************************************************************/
// 校时没必要那么复杂,省略上一段内容,采用直接获取系统时间
time(&unixTime);
// 写入时间
EnterCriticalSection(&m_udtTcpClient.ctsClient);
memcpy(&m_udtTcpClient.pbtSendBufTime[PACKET_HEADER_LENGTH],&unixTime,sizeof(unixTime));
m_udtTcpClient.bIsAdjustTime = TRUE;
if (m_udtTcpClient.bIsDebug)
{
WriteToLog(unixTime,"GDW_VM2003_AdjustTime函数:已设置校时。");
}
LeaveCriticalSection(&m_udtTcpClient.ctsClient);
return VALUE_ZERO;
}
/************************************************************************
Function int GDW_VM2003_AdjustTime:
adjust server time
Input:
pchTime must format "yyyyMMddHHmmss"(兼容原定义接口,此参数可不赋值)
OutPut:
return:
GDW_VM2003_Connect 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_AdjustTime(char* pchTime)
{
AFX_MANAGE_STATE(AfxGetStaticModuleState());
time_t unixTime;
if (!m_bInitSuccess)
{
return 1;
}
if (!m_udtTcpClient.bConnectActive)
{
return 11;
}
// 校时没必要那么复杂,省略上一段内容,采用直接获取系统时间
time(&unixTime);
// 写入时间
EnterCriticalSection(&m_udtTcpClient.ctsClient);
memcpy(&m_udtTcpClient.pbtSendBufTime[PACKET_HEADER_LENGTH],&unixTime,sizeof(unixTime));
m_udtTcpClient.bIsAdjustTime = TRUE;
if (m_udtTcpClient.bIsDebug)
{
WriteToLog(unixTime,"GDW_VM2003_AdjustTime函数:已设置校时。");
}
LeaveCriticalSection(&m_udtTcpClient.ctsClient);
return VALUE_ZERO;
}
/************************************************************************
Function int GDW_VM2003_Disconnect:
end listen thread and client thread
Input:
OutPut:
return:
GDW_VM2003_Connect 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_VM2003_Disconnect()
{
AFX_MANAGE_STATE(AfxGetStaticModuleState());
DWORD dwExitCode;
DWORD nWaitMilliSecond;
int iReturn = VALUE_ZERO; // 函数返回值
int iRetTmp;
if (!m_bInitSuccess)
{
return -1;
}
m_udtTcpClient.evtInspectEnd.SetEvent(); // 通知监测线程终止
m_udtTcpClient.evtClientEnd.SetEvent(); // 通知客户线程终止
if (m_udtTcpClient.bThreadClientAlive)
{
shutdown(m_udtTcpClient.sckClient,SD_BOTH);
Sleep(DELAY_SHUTDOWNSOCKET);
closesocket(m_udtTcpClient.sckClient);
m_udtTcpClient.sckClient = NULL;
}
Sleep(DELAY_SHUTDOWNSOCKET);
nWaitMilliSecond = 0;
if (m_udtTcpClient.threadClient != NULL)
{
for ( ;; )
{
iRetTmp = ::GetExitCodeThread(m_udtTcpClient.threadClient->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_udtTcpClient.threadClient->m_hThread,1);
if (iRetTmp != 0) // 终止线程成功
{
break;
}
else // 终止线程失败
{
iReturn = -10;
break;
}
}
}
}
else
{
break;
}
}
}
m_udtTcpClient.threadClient = NULL;
if (NULL != m_udtTcpClient.pbtImageNearBuf)
{
delete[] m_udtTcpClient.pbtImageNearBuf;
m_udtTcpClient.pbtImageNearBuf = NULL;
}
if (NULL != m_udtTcpClient.pbtImageFullBuf)
{
delete[] m_udtTcpClient.pbtImageFullBuf;
m_udtTcpClient.pbtImageFullBuf = NULL;
}
iRetTmp = ::GetExitCodeThread(m_udtTcpClient.threadInspect->m_hThread, &dwExitCode);
if (iRetTmp != 0)
{
if (dwExitCode != STILL_ACTIVE)
{
// 已终止
}
else
{
iRetTmp = TerminateThread(m_udtTcpClient.threadInspect->m_hThread,1);
if (iRetTmp != 0) // 终止线程成功
{
}
else // 终止线程失败
{
iReturn = -11;
}
}
}
WSACleanup();
m_bInitSuccess = FALSE;
if (m_udtTcpClient.bIsDebug)
{
WriteToLog(0,"[终止]------------------信息分割线------------------");
}
return iReturn;
}
/************************************************************************
Function int TcpReceive:
receive data from socket
Input:
SOCKET sckClient 接收数据的socket句柄
int iOffset 接收的偏移位置
int iReceiveSize 接收的大小
int iOverTime 接收的超时限制
OutPut:
BYTE *pchBuffer 接收缓存区
return:
If no error occurs, returns receive data size,other returns SOCKET_ERROR or zero;
Update:
Version Date Author Description
1.0 2008-03-19 Shi Mingjie Create
************************************************************************/
int TcpReceive(SOCKET sckClient,
BYTE *pchBuffer,
int iOffset,
int iReceiveSize,
int iOverTime)
{
int iRev;
int iHaveRev;
int iReadTimes;
int iReadTimesLimit;
iReadTimesLimit = iOverTime / DELAY_WAITSUCCESS;
iReadTimes = 0;
iHaveRev = 0;
Sleep(DELAY_WAITSUCCESS);
iRev = recv(sckClient,(char *)&pchBuffer[iOffset+iHaveRev],iReceiveSize,0);
if (SOCKET_ERROR == iRev)
{
return SOCKET_ERROR;
}
else
{
iHaveRev += iRev;
}
while ((iHaveRev < iReceiveSize) && (iReadTimes++ < iReadTimesLimit))
{
Sleep(DELAY_WAITSUCCESS);
iRev = recv(sckClient,(char *)&pchBuffer[iOffset+iHaveRev],iReceiveSize-iHaveRev,0);
if (SOCKET_ERROR == iRev)
{
return SOCKET_ERROR;
}
else
{
iHaveRev += iRev;
}
}
if (iHaveRev == iReceiveSize)
{
return iHaveRev;
}
else
{
return VALUE_ZERO;
}
}
/************************************************************************
Function int TcpInspectThread:
inspect thread (通过此线程与服务器建立socket连接并监测连接状态)
Input:
UDT_TCPCommunicationClient m_udtTcpClient
OutPut:
UDT_TCPCommunicationClient m_udtTcpClient
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 TcpInspectThread(LPVOID pParam)
{
struct UDT_TCPCommunicationClient *pudtTcpConnect = (struct UDT_TCPCommunicationClient *)pParam;
struct sockaddr_in serverAddr;
DWORD dwTimeCountInterval; // 时间差值
int iRetval;
//DWORD dwServerIp;
//dwServerIp = inet_addr("192.168.58.10");
//memcpy(&serverAddr.sin_addr, &dwServerIp, sizeof(dwServerIp));
//serverAddr.sin_port = htons(6999);
memcpy(&serverAddr.sin_addr, &pudtTcpConnect->dwServerIp, sizeof(pudtTcpConnect->dwServerIp));
serverAddr.sin_port = htons(pudtTcpConnect->wServerPort);
serverAddr.sin_family = AF_INET;
if (pudtTcpConnect->bIsDebug)
{
WriteToLog(0,"TcpInspectThread线程:启动。");
}
for (; ;)
{
if (WAIT_OBJECT_0 == ::WaitForSingleObject(pudtTcpConnect->evtInspectEnd.m_hObject, 0))
{
if (pudtTcpConnect->bIsDebug)
{
WriteToLog(0,"TcpInspectThread线程:接收到信号量终止指令。");
}
break;
}
if (!pudtTcpConnect->bConnectActive) // 网络未连接
{
if (NULL != pudtTcpConnect->sckClient)
{
shutdown(pudtTcpConnect->sckClient,SD_BOTH);
Sleep(DELAY_WAITSUCCESS);
closesocket(pudtTcpConnect->sckClient);
pudtTcpConnect->sckClient = NULL;
}
pudtTcpConnect->sckClient = socket(AF_INET, SOCK_STREAM, 0);
if (INVALID_SOCKET == pudtTcpConnect->sckClient)
{
if (pudtTcpConnect->bIsDebug)
{
WriteToLog("TcpInspectThread线程:socket 函数返回失败。");
}
}
iRetval = connect(pudtTcpConnect->sckClient,(struct sockaddr *)&serverAddr,sizeof(serverAddr));
if (SOCKET_ERROR == iRetval)
{
if (pudtTcpConnect->bIsDebug)
{
WriteToLog(iRetval,"TcpInspectThread线程:connect 连接失败!");
}
}
else if(0 == iRetval) // 建立连接成功
{
EnterCriticalSection(&pudtTcpConnect->ctsClient);
pudtTcpConnect->bConnectActive = TRUE; // 连接成功标志置为TRUE
LeaveCriticalSection(&pudtTcpConnect->ctsClient);
PostMessage(pudtTcpConnect->hWndProcess, pudtTcpConnect->dwMsgConnectSuccess,0,0);
pudtTcpConnect->dwHeartBeatTime = GetTickCount(); // 默认上次心跳时间点为启动线程时间
if (pudtTcpConnect->bIsDebug)
{
WriteToLog(0,"TcpInspectThread线程:connect 连接成功。");
}
}
else
{
WriteToLog(iRetval,"TcpInspectThread线程:connect 返回异常值!");
}
}
else
{
dwTimeCountInterval = GetTickCount() - pudtTcpConnect->dwHeartBeatTime;
if ((dwTimeCountInterval > LIMIT_HEARTBEAT) && (GetTickCount() > pudtTcpConnect->dwHeartBeatTime))
{
EnterCriticalSection(&pudtTcpConnect->ctsClient);
pudtTcpConnect->bConnectActive = FALSE; // 连接成功标志置为FALSE,通过心跳是否超时检测连接是否失效
LeaveCriticalSection(&pudtTcpConnect->ctsClient);
PostMessage(pudtTcpConnect->hWndProcess, pudtTcpConnect->dwMsgDisconnect,0,0);
if (pudtTcpConnect->bIsDebug)
{
WriteToLog(dwTimeCountInterval,"TcpInspectThread线程:检测到心跳超时,重新连接!");
}
}
}
Sleep(DELAY_INSPECT);
}
return VALUE_ZERO;
}
/************************************************************************
Function int TcpServerThread:
server thread (通过此线程与服务器端进行通讯)
Input:
UDT_TCPCommunicationClient m_udtTcpClient
OutPut:
UDT_TCPCommunicationClient m_udtTcpClient
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 TcpServerThread(LPVOID pParam)
{
struct UDT_TCPCommunicationClient *pudtTcpClient = (struct UDT_TCPCommunicationClient *)pParam;
int iSendRetval;
BYTE pbtReceiveBuf[LIMIT_SENDBUFFERSIZE];
int iRealReceive = 0;
int iReceiveRetval = 0;
short siType = 0; // 接收到的包类型
int iLength = 0; // 接收到的包长度
int iToalLength = 0; // 接收到的包总长度
int iImageNearSize = 0; // 接收到的近景图片数据尺寸
int iImageFullSize = 0; // 接收到的全景图片数据尺寸
DWORD dwTimeCountInterval; // 时间差值
int iWaitTimes = 0;
const long TIMEOUT_RECEIVECHECK = 200000; // 单位为microseconds 微妙
struct timeval tvReceive={0,TIMEOUT_RECEIVECHECK};
fd_set fdReceive;
int iSelectRet; // Select函数的返回值
pudtTcpClient->bThreadClientAlive = TRUE;
if (pudtTcpClient->bIsDebug)
{
WriteToLog(0,"TcpServerThread线程:启动。");
}
for (; ;)
{
if (WAIT_OBJECT_0 == ::WaitForSingleObject(pudtTcpClient->evtClientEnd.m_hObject, 0))
{
if (pudtTcpClient->bIsDebug)
{
WriteToLog(0,"TcpServerThread线程:接收到信号量终止指令。");
}
break;
}
// 网络已连接:接包
if (pudtTcpClient->bConnectActive)
{
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.
if (pudtTcpClient->bIsDebug)
{
WriteToLog(iSelectRet,"TcpServerThread线程:select 返回ZERO!");
}
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,"TcpServerThread线程:recv 返回SOCKET_ERROR,继续!");
pudtTcpClient->bConnectActive = FALSE;
PostMessage(pudtTcpClient->hWndProcess, pudtTcpClient->dwMsgDisconnect,0,0);
break;
}
else if (VALUE_ZERO == iReceiveRetval) // If the connection has been gracefully closed, the return value is zero.
{
// pudtTcpClient->evtClientEnd.SetEvent();
WriteToLog(iReceiveRetval,"TcpServerThread线程:recv 返回VALUE_ZERO,客户端连接完美关闭,继续!");
pudtTcpClient->bConnectActive = FALSE;
PostMessage(pudtTcpClient->hWndProcess, pudtTcpClient->dwMsgDisconnect,0,0);
break;
}
else // If no error occurs, recv returns the number of bytes received.
{
siType = 0;
iLength = 0;
if (PACKET_HEADER_LENGTH != iReceiveRetval)
{
WriteToLog(iReceiveRetval,"TcpServerThread线程: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(pudtTcpClient->dwHeartBeatTime,"TcpServerThread线程:接收心跳包正常。");
}
break;
case TYPE_TIME: // 时间包
iReceiveRetval = recv(pudtTcpClient->sckClient,(char *)&pbtReceiveBuf[iRealReceive],iLength-PACKET_HEADER_LENGTH,0);
if (SOCKET_ERROR == iReceiveRetval)
{
if (pudtTcpClient->bIsDebug)
{
WriteToLog("TcpServerThread线程:接收时间数据发生错误!");
}
break;
}
else if (VALUE_ZERO == iReceiveRetval)
{
break;
}
else
{
memcpy(&pudtTcpClient->dwReceiveTime,&pbtReceiveBuf[iRealReceive],sizeof(pudtTcpClient->dwReceiveTime));
if (pudtTcpClient->bIsDebug)
{
WriteToLog(pudtTcpClient->dwReceiveTime,"TcpServerThread线程:校时成功,接收到服务器时间。");
}
}
break;
case TYPE_REALVEHICLE: // 实时车辆信息包
iToalLength = iLength;
// 接收子包1 :通行信息包 TYPE_PASSINFO
iReceiveRetval = recv(pudtTcpClient->sckClient,(char *)&pbtReceiveBuf[iRealReceive],PACKET_HEADER_LENGTH,0);
if (SOCKET_ERROR == iReceiveRetval)
{
break;
}
else if (VALUE_ZERO == iReceiveRetval)
{
break;
}
else
{
memcpy(&siType,&pbtReceiveBuf[iRealReceive],PACKET_TYPE_LENGTH); //包类型 2BYTE
memcpy(&iLength,&pbtReceiveBuf[iRealReceive + PACKET_TYPE_LENGTH],PACKET_LENGTH_LENGTH); //包长度 4BYTE
iRealReceive += iReceiveRetval;
if (TYPE_PASSINFO == siType) // 通行信息包
{
iReceiveRetval = TcpReceive(pudtTcpClient->sckClient,pbtReceiveBuf,iRealReceive,iLength-PACKET_HEADER_LENGTH,LIMIT_WAITRECEIVE);
if (SOCKET_ERROR == iReceiveRetval)
{
break;
}
else if (VALUE_ZERO == iReceiveRetval)
{
break;
}
else
{
iWaitTimes = 0;
while ((pudtTcpClient->bIsGetingData) && (iWaitTimes < LIMIT_WAITQUIT/DELAY_WAITSUCCESS))
{
Sleep(DELAY_WAITSUCCESS);
iWaitTimes++;
}
memcpy(&pudtTcpClient->udtPassInfo,&pbtReceiveBuf[iRealReceive],sizeof(pudtTcpClient->udtPassInfo));
iRealReceive += iReceiveRetval;
}
}
}
// 接收子包2 :特写图片数据包 TYPE_IMAGENEAR
iReceiveRetval = recv(pudtTcpClient->sckClient,(char *)&pbtReceiveBuf[iRealReceive],PACKET_HEADER_LENGTH,0);
if (SOCKET_ERROR == iReceiveRetval)
{
break;
}
else if (VALUE_ZERO == iReceiveRetval)
{
break;
}
else
{
memcpy(&siType,&pbtReceiveBuf[iRealReceive],PACKET_TYPE_LENGTH); //包类型 2BYTE
memcpy(&iLength,&pbtReceiveBuf[iRealReceive + PACKET_TYPE_LENGTH],PACKET_LENGTH_LENGTH); //包长度 4BYTE
iRealReceive += iReceiveRetval;
if (TYPE_IMAGENEAR == siType) // 特写图片数据包
{
iReceiveRetval = TcpReceive(pudtTcpClient->sckClient,pbtReceiveBuf,iRealReceive,iLength-PACKET_HEADER_LENGTH,LIMIT_WAITRECEIVE);
if (SOCKET_ERROR == iReceiveRetval)
{
break;
}
else if (VALUE_ZERO == iReceiveRetval)
{
break;
}
else
{
if (NULL != pudtTcpClient->pbtImageNearBuf)
{
delete[] pudtTcpClient->pbtImageNearBuf;
pudtTcpClient->pbtImageNearBuf = NULL;
}
iImageNearSize = iLength - PACKET_HEADER_LENGTH;
if ((iImageNearSize < LIMIT_MINIMAGESIZE) || (iImageNearSize > LIMIT_MAXIMAGESIZE))
{
// 近景图片大小不正确,退出
if (pudtTcpClient->bIsDebug)
{
WriteToLog(iImageNearSize,"TcpServerThread线程:近景图片长度不合法。");
}
break;
}
pudtTcpClient->pbtImageNearBuf = new BYTE[iImageNearSize];
memcpy(pudtTcpClient->pbtImageNearBuf,&pbtReceiveBuf[iRealReceive],iImageNearSize);
iRealReceive += iReceiveRetval;
}
}
}
// 接收子包3 :全景图片数据包 TYPE_IMAGEFULL
iReceiveRetval = recv(pudtTcpClient->sckClient,(char *)&pbtReceiveBuf[iRealReceive],PACKET_HEADER_LENGTH,0);
if (SOCKET_ERROR == iReceiveRetval)
{
break;
}
else if (VALUE_ZERO == iReceiveRetval)
{
break;
}
else
{
memcpy(&siType,&pbtReceiveBuf[iRealReceive],PACKET_TYPE_LENGTH); //包类型 2BYTE
memcpy(&iLength,&pbtReceiveBuf[iRealReceive + PACKET_TYPE_LENGTH],PACKET_LENGTH_LENGTH); //包长度 4BYTE
iRealReceive += iReceiveRetval;
if (TYPE_IMAGEFULL == siType) // 全景图片数据包
{
iReceiveRetval = TcpReceive(pudtTcpClient->sckClient,pbtReceiveBuf,iRealReceive,iLength-PACKET_HEADER_LENGTH,LIMIT_WAITRECEIVE);
if (SOCKET_ERROR == iReceiveRetval)
{
break;
}
else if (VALUE_ZERO == iReceiveRetval)
{
break;
}
else
{
if (NULL != pudtTcpClient->pbtImageFullBuf)
{
delete[] pudtTcpClient->pbtImageFullBuf;
pudtTcpClient->pbtImageFullBuf = NULL;
}
iImageFullSize = iLength - PACKET_HEADER_LENGTH;
if ((iImageFullSize < LIMIT_MINIMAGESIZE) || (iImageFullSize > LIMIT_MAXIMAGESIZE))
{
// 全景图片大小不正确,退出
if (pudtTcpClient->bIsDebug)
{
WriteToLog(iImageFullSize,"TcpServerThread线程:全景图片长度不合法。");
}
break;
}
pudtTcpClient->pbtImageFullBuf = new BYTE[iImageFullSize];
memcpy(pudtTcpClient->pbtImageFullBuf,&pbtReceiveBuf[iRealReceive],iImageFullSize);
iRealReceive += iReceiveRetval;
}
}
}
EnterCriticalSection(&pudtTcpClient->ctsClient);
pudtTcpClient->bDataIsReady = TRUE;
LeaveCriticalSection(&pudtTcpClient->ctsClient);
PostMessage(pudtTcpClient->hWndProcess, pudtTcpClient->dwMsgDataReady,0,0);
break;
default:
WriteToLog(siType,"TcpServerThread线程:T-L-V协议T不属于已定义类型!继续!");
// pudtTcpClient->evtClientEnd.SetEvent();
pudtTcpClient->bConnectActive = FALSE;
PostMessage(pudtTcpClient->hWndProcess, pudtTcpClient->dwMsgDisconnect,0,0);
break;
}
} // 接收到PACKET_HEADER_LENGTH字节的字符
}
} // end if (FD_ISSET(pudtTcpClient->sckClient,&fdReceive))
else
{
if (pudtTcpClient->bIsDebug)
{
WriteToLog("TcpServerThread线程:FD_ISSET 返回ZERO!");
}
}
break;
}
// 网络已连接:检查是否发送心跳包
if (pudtTcpClient->bConnectActive)
{
dwTimeCountInterval = GetTickCount() - pudtTcpClient->dwHeartBeatTime;
if (dwTimeCountInterval > DELAY_HEARTBEATINTERVAL)
{
iSendRetval = send(pudtTcpClient->sckClient,(char *)pudtTcpClient->pbtSendBufHeartBeat,PACKET_HEADER_LENGTH,0);
if (SOCKET_ERROR == iSendRetval)
{
if (pudtTcpClient->bIsDebug)
{
WriteToLog(iSendRetval,"TcpServerThread线程:心跳包发送失败!");
}
}
else
{
pudtTcpClient->dwHeartBeatTime = GetTickCount(); // 发送成功即认为成功
if (pudtTcpClient->bIsDebug)
{
WriteToLog(iSendRetval,"TcpServerThread线程:心跳包发送成功。");
}
}
}
}
// 网络已连接:检查是否进行校时
if (pudtTcpClient->bConnectActive)
{
if (pudtTcpClient->bIsAdjustTime)
{
iSendRetval = send(pudtTcpClient->sckClient,(char *)pudtTcpClient->pbtSendBufTime,PACKET_TIME_LENGTH,0);
if (SOCKET_ERROR == iSendRetval)
{
if (pudtTcpClient->bIsDebug)
{
WriteToLog(iSendRetval,"TcpServerThread线程:校时包发送失败!");
}
}
else
{
EnterCriticalSection(&pudtTcpClient->ctsClient);
pudtTcpClient->bIsAdjustTime = FALSE; // 发送成功即认为成功
LeaveCriticalSection(&pudtTcpClient->ctsClient);
if (pudtTcpClient->bIsDebug)
{
WriteToLog(iSendRetval,"TcpServerThread线程:校时包发送成功。");
}
}
}
}
}
Sleep(DELAY_WAITSUCCESS);
}
shutdown(pudtTcpClient->sckClient,SD_BOTH);
closesocket(pudtTcpClient->sckClient);
pudtTcpClient->bConnectActive = FALSE;
pudtTcpClient->bThreadClientAlive = FALSE;
return VALUE_ZERO;
}
#include "winsock2.h"
#include "afxmt.h"
#include "Mmsystem.h"
#include
#include
#include
#include
////////////////////////////////////////////////////////////////////////
// 传输协议根据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 = 150; // 检查网络状态的间隔
const int LIMIT_WAITRECEIVE = 200; // 等待接收最长时间
const int LIMIT_WAITQUIT = 200; // 退出延时限制
const int LIMIT_DATAOVERTIME = 10000; // 数据超时限制(保存在发送缓存中的)
const int LIMIT_HEARTBEAT = 30000; // 心跳间隔超时限制(超出认为服务器异常)
const int LIMIT_MAXLISTENCLIENT = 10; // 最大监听客户数
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 WM_VEHICLEPASS = 0x401; // 车辆经过
const int WM_TCPCONNECT = 0x402; // 建立TCP连接
const int WM_TCPDISCONNECT = 0x403; // TCP连接断开
const int VALUE_ZERO = 0; // 零值
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_TCPCommunicationClient // 通讯控制参数结构体
{
HWND hWndProcess; // 发送消息所需句柄
DWORD dwMsgDataReady; // 数据准备好消息 默认0x401
DWORD dwMsgConnectSuccess; // 网络连接成功消息 默认0x402
DWORD dwMsgDisconnect; // 网络断开消息 默认0x403
DWORD dwServerIp; // 服务器IP
WORD wServerPort; // 服务器监听端口
BOOL bIsDebug; // 是否开启调试模式
BOOL bConnectActive; // 网络是否处于活动状态
BOOL bThreadClientAlive; // 线程是否存活
BOOL bDataIsReady; // 数据是否准备好
BOOL bIsGetingData; // 是否正在取数据
BOOL bIsAdjustTime; // 是否校时
DWORD dwReceiveTime; // 接收到的时间 unix时间格式
DWORD dwHeartBeatTime; // 心跳时间点
DWORD dwHeartBeatCount; // 心跳次数
CWinThread* threadInspect; // 监测线程句柄
CEvent evtInspectEnd; // 监测线程信号量(通知对应线程结束)
SOCKET sckClient; // 客户端线程通讯使用的Socket句柄
CWinThread* threadClient; // 客户端线程句柄
CEvent evtClientEnd; // 客户端线程信号量(通知对应线程结束)
CRITICAL_SECTION ctsClient; // 客户端临界区
struct UDT_PassInfo udtPassInfo; // 车辆信息数据包(不包括图片)
BYTE pbtSendBufHeartBeat[PACKET_HEADER_LENGTH]; // 心跳包发送缓存区
BYTE pbtSendBufTime[PACKET_TIME_LENGTH]; // 时间包发送缓存区
BYTE* pbtImageNearBuf; // 特写图片数据缓存区
BYTE* pbtImageFullBuf; // 全景图片数据缓存区
}UDT_TCPCommunicationClient;
static struct UDT_TCPCommunicationClient m_udtTcpClient; // 模块级变量,控制通讯
static BOOL m_bInitSuccess = FALSE; // 初始化成功标记,除Connect函数外均需要初始化成功才能调用
int WriteToLog(int iCueNumber,TCHAR *szMsg);
int WriteToLog(TCHAR *szMsg);
int WriteBinaryFile(BYTE* pbtBuffer,DWORD dwFileSize,char *pchFileName);
int TcpReceive(SOCKET sckClient,
BYTE *pchBuffer,
int iOffset,
int iReceiveSize,
int iOverTime);
UINT TcpInspectThread(LPVOID pParam);
UINT TcpServerThread(LPVOID pParam);
/*******************************************************
int GDW_VM2003_Connect(DWORD hWnd,
DWORD dwMsgDataReady,
DWORD dwMsgConnectSuccess,
DWORD dwMsgDisconnect);
int GDW_VM2003_GetVehicleInfo(char* pchPlate,
char* pchTime,
BYTE* pbtImageBin,
BYTE* pbtImagePlate,
DWORD* dwImagePlateSize,
BYTE* pbtImageNear,
DWORD* dwImageNearSize,
BYTE* pbtImageFull,
DWORD* dwImageFullSize,
char* pchDeviceId,
int* iRoadWay,
int* iSpeed,
int* iSpeedLimit,
DWORD* dwDeviceState);
int GDW_VM2003_AdjustTime(char* pchTime);
int GDW_VM2003_Disconnect();
*******************************************************/
//
// 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_Client.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_VM2003_Connect:
Start InspectThread && Server Thread
Input:
DWORD hWnd 窗口句柄,不能为VALUE_ZERO
DWORD dwMsgDataReady 数据准备好消息,必须大于0x400,默认0x401
DWORD dwMsgConnectSuccess 网络连接成功,必须大于0x401,默认0x402
DWORD dwMsgDisconnect 网络连接断开,必须大于0x402,默认0x403
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_VM2003_Connect(DWORD hWnd,
DWORD dwMsgDataReady,
DWORD dwMsgConnectSuccess,
DWORD dwMsgDisconnect)
{
AFX_MANAGE_STATE(AfxGetStaticModuleState());
const DWORD dwMsgMin = 0x400; // 可用消息值临界值(自定义消息必须比该值大)
int i = 0;
int iLastSperate = 0;
TCHAR chCurPath[MAX_PATH];
HANDLE hWndFile;
WIN32_FIND_DATA fileFind;
int iIsDebugToLog;
TCHAR chIpBuffer[128];
int iServerPort;
DWORD dwReadLength;
int iRetval = 0;
WORD wVersion;
WSADATA wsaData;
if (VALUE_ZERO == hWnd) // 为无效句柄则立即返回
{
return 2;
}
if ((dwMsgDataReady <= dwMsgMin) ||
(dwMsgConnectSuccess <= dwMsgMin + 1) ||
(dwMsgDisconnect <= dwMsgMin + 2) ||
(dwMsgDataReady == dwMsgConnectSuccess) ||
(dwMsgDataReady == dwMsgDisconnect) ||
(dwMsgConnectSuccess == dwMsgDisconnect))
{
m_udtTcpClient.dwMsgDataReady = dwMsgMin + 1;
m_udtTcpClient.dwMsgConnectSuccess = dwMsgMin + 2;
m_udtTcpClient.dwMsgDisconnect = dwMsgMin + 3;
}
else
{
m_udtTcpClient.dwMsgDataReady = dwMsgDataReady;
m_udtTcpClient.dwMsgConnectSuccess = dwMsgConnectSuccess;
m_udtTcpClient.dwMsgDisconnect = dwMsgDisconnect;
}
m_udtTcpClient.hWndProcess = (HWND)hWnd; // 保存消息处理句柄
// 读取配置参数:服务器Ip、端口、是否调试
GetModuleFileName(GetModuleHandle(NULL),chCurPath,MAX_PATH-20);
for (i=0; 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_client.ini");
hWndFile = FindFirstFile(chCurPath,&fileFind);
FindClose(hWndFile);
if (INVALID_HANDLE_VALUE != hWndFile)
{
dwReadLength = GetPrivateProfileString("Client","serverIp","0.0.0.0",chIpBuffer,128,chCurPath);
if ((dwReadLength >= 7) && (dwReadLength <= 15))
{
m_udtTcpClient.dwServerIp = inet_addr(chIpBuffer);
}
else
{
return 11;
}
iServerPort = GetPrivateProfileInt("Client","serverPort",5001,chCurPath);
if ((iServerPort > 0) && (iServerPort <= 65536))
{
m_udtTcpClient.wServerPort = iServerPort;
}
else
{
return 12;
}
iIsDebugToLog = GetPrivateProfileInt("Client","DebugToLog",0,chCurPath);
if (iIsDebugToLog != 0)
{
m_udtTcpClient.bIsDebug = TRUE;
}
}
else
{
return -1;
}
}
else
{
return -2;
}
// 初始化结构体UDT_TCPCommunicationClient参数
m_udtTcpClient.bConnectActive = FALSE; // 网络是否处于活动状态
m_udtTcpClient.bThreadClientAlive = FALSE; // 线程是否存活
m_udtTcpClient.bDataIsReady = FALSE; // 数据是否准备好
m_udtTcpClient.bIsGetingData = FALSE; // 是否正在取数据
m_udtTcpClient.bIsAdjustTime = FALSE; // 是否校时
m_udtTcpClient.dwReceiveTime = 0; // 收到的服务器回复时间 unix时间格式
m_udtTcpClient.dwHeartBeatTime = 0; // 心跳时间点
m_udtTcpClient.dwHeartBeatCount = 0; // 心跳次数
m_udtTcpClient.sckClient = NULL; // 客户端线程通讯使用的Socket句柄
m_udtTcpClient.threadClient = NULL; // 客户端线程句柄
m_udtTcpClient.pbtImageNearBuf = NULL; // 特写图片缓存区指针指向NULL
m_udtTcpClient.pbtImageFullBuf = NULL; // 全景图片缓存区指针指向NULL
// 心跳包格式: T L
memcpy(&m_udtTcpClient.pbtSendBufHeartBeat[VALUE_ZERO],&TYPE_HEARTBEAT,PACKET_TYPE_LENGTH);
memcpy(&m_udtTcpClient.pbtSendBufHeartBeat[PACKET_TYPE_LENGTH],&PACKET_HEADER_LENGTH,PACKET_LENGTH_LENGTH);
// 校时包格式: T L V,初始化时复制T L
memcpy(&m_udtTcpClient.pbtSendBufTime[VALUE_ZERO],&TYPE_TIME,PACKET_TYPE_LENGTH);
memcpy(&m_udtTcpClient.pbtSendBufTime[PACKET_TYPE_LENGTH],&PACKET_TIME_LENGTH,PACKET_LENGTH_LENGTH);
InitializeCriticalSection(&m_udtTcpClient.ctsClient); //临界区初始化
m_udtTcpClient.evtInspectEnd.ResetEvent();
m_udtTcpClient.evtClientEnd.ResetEvent(); // Sets the state of the event to nonsignaled
wVersion = MAKEWORD(1,1);
iRetval = WSAStartup(wVersion,&wsaData);
if (0 != iRetval)
{
if (m_udtTcpClient.bIsDebug)
{
WriteToLog(iRetval,"GDW_VM2003_Connect:没有发现可用的socket通讯库!退出!");
}
return -3;
}
if ((LOBYTE(wsaData.wVersion) != 1) || (HIBYTE(wsaData.wVersion) != 1))
{
if (m_udtTcpClient.bIsDebug)
{
WriteToLog("GDW_VM2003_Connect:不是正确的socket通讯库版本!退出!");
}
iRetval = WSACleanup();
if (SOCKET_ERROR == iRetval)
{
if (m_udtTcpClient.bIsDebug)
{
WriteToLog(iRetval,"GDW_VM2003_Connect:WSACleanup失败!");
}
}
return -4;
}
m_udtTcpClient.sckClient = socket(AF_INET, SOCK_STREAM, 0);
if (INVALID_SOCKET == m_udtTcpClient.sckClient)
{
if (m_udtTcpClient.bIsDebug)
{
WriteToLog("GDW_VM2003_Connect:socket 函数返回失败!退出!");
}
iRetval = WSACleanup();
if (SOCKET_ERROR == iRetval)
{
if (m_udtTcpClient.bIsDebug)
{
WriteToLog(iRetval,"GDW_VM2003_Connect:WSACleanup失败!");
}
}
return -5;
}
if (m_udtTcpClient.bIsDebug)
{
WriteToLog(0,"[启动]------------------信息分割线------------------");
}
// 建立监测线程
m_udtTcpClient.threadInspect = AfxBeginThread(TcpInspectThread, &m_udtTcpClient, THREAD_PRIORITY_NORMAL, 0, CREATE_SUSPENDED);
m_udtTcpClient.threadInspect->m_bAutoDelete = FALSE;
m_udtTcpClient.threadInspect->ResumeThread();
// 建立通讯服务线程
m_udtTcpClient.threadClient = AfxBeginThread(TcpServerThread, &m_udtTcpClient, THREAD_PRIORITY_NORMAL, 0, CREATE_SUSPENDED);
m_udtTcpClient.threadClient->m_bAutoDelete = FALSE;
m_udtTcpClient.threadClient->ResumeThread();
m_bInitSuccess = TRUE; // 初始化已经成功
return VALUE_ZERO;
}
/************************************************************************
Function int GDW_VM2003_GetVehicleInfo:
Get Vehicle Info
Input:
OutPut:
char* pchPlate 车牌号码 申请至少20字节空间,不允许NULL
char* pchTime 通行时间 申请至少24字节空间,不允许NULL
BYTE* pbtImageBin 车牌二值化图,不允许NULL
BYTE* pbtImagePlate 车牌彩色图,不允许NULL
DWORD* dwImagePlateSize 车牌彩色图大小
BYTE* pbtImageNear 特写车辆图
DWORD* dwImageNearSize 特写车辆图大小
BYTE* pbtImageFull 全景车辆图
DWORD* dwImageFullSize 全景车辆图大小
char* pchDeviceId 申请至少20字节空间,不允许NULL
int* iRoadWay 车道号
int* iSpeed 车速
int* iSpeedLimit 限速
DWORD* dwDeviceState 设备状态
return:
GDW_VM2003_Connect 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_VM2003_GetVehicleInfo(char* pchPlate,
char* pchTime,
BYTE* pbtImageBin,
BYTE* pbtImagePlate,
DWORD* dwImagePlateSize,
BYTE* pbtImageNear,
DWORD* dwImageNearSize,
BYTE* pbtImageFull,
DWORD* dwImageFullSize,
char* pchDeviceId,
int* iRoadWay,
int* iSpeed,
int* iSpeedLimit,
DWORD* dwDeviceState)
{
AFX_MANAGE_STATE(AfxGetStaticModuleState());
if (!m_bInitSuccess)
{
return -1;
}
if (!m_udtTcpClient.bDataIsReady)
{
return 10;
}
// 参数有空指针直接返回
if ((NULL == pchDeviceId) ||
(NULL == pchPlate) ||
(NULL == pchTime) ||
(NULL == pbtImageNear) ||
(NULL == pbtImageFull))
{
return 3;
}
// 设置数据正在读取标记,线程写入等待
EnterCriticalSection(&m_udtTcpClient.ctsClient);
m_udtTcpClient.bIsGetingData = TRUE;
LeaveCriticalSection(&m_udtTcpClient.ctsClient);
strcpy(pchPlate,m_udtTcpClient.udtPassInfo.pchPlate);
strcpy(pchTime,m_udtTcpClient.udtPassInfo.pchPassTime);
strcpy(pchDeviceId,m_udtTcpClient.udtPassInfo.pchDeviceId);
*iRoadWay = m_udtTcpClient.udtPassInfo.iRoadWay;
*iSpeed = m_udtTcpClient.udtPassInfo.iSpeed ;
*iSpeedLimit = m_udtTcpClient.udtPassInfo.iSpeedLimit;
*dwDeviceState = m_udtTcpClient.udtPassInfo.dwDeviceState;
*dwImageNearSize = m_udtTcpClient.udtPassInfo.dwImageNearSize;
*dwImageFullSize = m_udtTcpClient.udtPassInfo.dwImageFullSize;
memcpy(pbtImageNear,m_udtTcpClient.pbtImageNearBuf,m_udtTcpClient.udtPassInfo.dwImageNearSize);
memcpy(pbtImageFull,m_udtTcpClient.pbtImageFullBuf,m_udtTcpClient.udtPassInfo.dwImageFullSize);
// pbtImageBin pbtImagePlate dwImagePlateSize 不赋值
// 设置数据读取完成标记,线程允许写入
EnterCriticalSection(&m_udtTcpClient.ctsClient);
m_udtTcpClient.bIsGetingData = FALSE;
m_udtTcpClient.bDataIsReady = FALSE;
LeaveCriticalSection(&m_udtTcpClient.ctsClient);
return VALUE_ZERO;
}
/************************************************************************
Function int GDW_VM2003_AdjustTime:
adjust server time
Input:
pchTime must format "yyyyMMddHHmmss"(兼容原定义接口,此参数可不赋值)
OutPut:
return:
GDW_VM2003_Connect 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_VM2003_AdjustTime(char* pchTime)
{
AFX_MANAGE_STATE(AfxGetStaticModuleState());
time_t unixTime;
if (!m_bInitSuccess)
{
return 1;
}
if (!m_udtTcpClient.bConnectActive)
{
return 11;
}
/*********************************************************************
if (NULL == pchTime)
{
return 2;
}
if (14 != strlen(pchTime))
{
return 12;
}
struct tm tmTime;
int iASCIIZero = '0';
tmTime.tm_sec = (pchTime[1]-iASCIIZero) * 10 + (pchTime[0]-iASCIIZero);
tmTime.tm_min = (pchTime[3]-iASCIIZero) * 10 + (pchTime[2]-iASCIIZero);
tmTime.tm_hour = (pchTime[5]-iASCIIZero) * 10 + (pchTime[4]-iASCIIZero);
tmTime.tm_mday = (pchTime[7]-iASCIIZero) * 10 + (pchTime[6]-iASCIIZero);
tmTime.tm_mon = (pchTime[9]-iASCIIZero) * 10 + (pchTime[8]-iASCIIZero);
tmTime.tm_year = (pchTime[13]-iASCIIZero) * 1000 + (pchTime[12]-iASCIIZero) * 100 + (pchTime[11]-iASCIIZero) * 10 + (pchTime[10]-iASCIIZero);
if ((tmTime.tm_sec < 0) || (tmTime.tm_sec > 59))
{
return 8;
}
if ((tmTime.tm_min < 0) || (tmTime.tm_min > 59))
{
return 7;
}
if ((tmTime.tm_hour < 0) || (tmTime.tm_hour > 23))
{
return 6;
}
if ((tmTime.tm_mday < 1) || (tmTime.tm_mday > 31))
{
return 5;
}
if ((tmTime.tm_mon < 1) || (tmTime.tm_mon > 12))
{
return 4;
}
if ((tmTime.tm_year < 2000) || (tmTime.tm_year > 2099))
{
return 3;
}
unixTime = mktime(&tmTime);
*********************************************************************/
// 校时没必要那么复杂,省略上一段内容,采用直接获取系统时间
time(&unixTime);
// 写入时间
EnterCriticalSection(&m_udtTcpClient.ctsClient);
memcpy(&m_udtTcpClient.pbtSendBufTime[PACKET_HEADER_LENGTH],&unixTime,sizeof(unixTime));
m_udtTcpClient.bIsAdjustTime = TRUE;
if (m_udtTcpClient.bIsDebug)
{
WriteToLog(unixTime,"GDW_VM2003_AdjustTime函数:已设置校时。");
}
LeaveCriticalSection(&m_udtTcpClient.ctsClient);
return VALUE_ZERO;
}
/************************************************************************
Function int GDW_VM2003_AdjustTime:
adjust server time
Input:
pchTime must format "yyyyMMddHHmmss"(兼容原定义接口,此参数可不赋值)
OutPut:
return:
GDW_VM2003_Connect 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_AdjustTime(char* pchTime)
{
AFX_MANAGE_STATE(AfxGetStaticModuleState());
time_t unixTime;
if (!m_bInitSuccess)
{
return 1;
}
if (!m_udtTcpClient.bConnectActive)
{
return 11;
}
// 校时没必要那么复杂,省略上一段内容,采用直接获取系统时间
time(&unixTime);
// 写入时间
EnterCriticalSection(&m_udtTcpClient.ctsClient);
memcpy(&m_udtTcpClient.pbtSendBufTime[PACKET_HEADER_LENGTH],&unixTime,sizeof(unixTime));
m_udtTcpClient.bIsAdjustTime = TRUE;
if (m_udtTcpClient.bIsDebug)
{
WriteToLog(unixTime,"GDW_VM2003_AdjustTime函数:已设置校时。");
}
LeaveCriticalSection(&m_udtTcpClient.ctsClient);
return VALUE_ZERO;
}
/************************************************************************
Function int GDW_VM2003_Disconnect:
end listen thread and client thread
Input:
OutPut:
return:
GDW_VM2003_Connect 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_VM2003_Disconnect()
{
AFX_MANAGE_STATE(AfxGetStaticModuleState());
DWORD dwExitCode;
DWORD nWaitMilliSecond;
int iReturn = VALUE_ZERO; // 函数返回值
int iRetTmp;
if (!m_bInitSuccess)
{
return -1;
}
m_udtTcpClient.evtInspectEnd.SetEvent(); // 通知监测线程终止
m_udtTcpClient.evtClientEnd.SetEvent(); // 通知客户线程终止
if (m_udtTcpClient.bThreadClientAlive)
{
shutdown(m_udtTcpClient.sckClient,SD_BOTH);
Sleep(DELAY_SHUTDOWNSOCKET);
closesocket(m_udtTcpClient.sckClient);
m_udtTcpClient.sckClient = NULL;
}
Sleep(DELAY_SHUTDOWNSOCKET);
nWaitMilliSecond = 0;
if (m_udtTcpClient.threadClient != NULL)
{
for ( ;; )
{
iRetTmp = ::GetExitCodeThread(m_udtTcpClient.threadClient->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_udtTcpClient.threadClient->m_hThread,1);
if (iRetTmp != 0) // 终止线程成功
{
break;
}
else // 终止线程失败
{
iReturn = -10;
break;
}
}
}
}
else
{
break;
}
}
}
m_udtTcpClient.threadClient = NULL;
if (NULL != m_udtTcpClient.pbtImageNearBuf)
{
delete[] m_udtTcpClient.pbtImageNearBuf;
m_udtTcpClient.pbtImageNearBuf = NULL;
}
if (NULL != m_udtTcpClient.pbtImageFullBuf)
{
delete[] m_udtTcpClient.pbtImageFullBuf;
m_udtTcpClient.pbtImageFullBuf = NULL;
}
iRetTmp = ::GetExitCodeThread(m_udtTcpClient.threadInspect->m_hThread, &dwExitCode);
if (iRetTmp != 0)
{
if (dwExitCode != STILL_ACTIVE)
{
// 已终止
}
else
{
iRetTmp = TerminateThread(m_udtTcpClient.threadInspect->m_hThread,1);
if (iRetTmp != 0) // 终止线程成功
{
}
else // 终止线程失败
{
iReturn = -11;
}
}
}
WSACleanup();
m_bInitSuccess = FALSE;
if (m_udtTcpClient.bIsDebug)
{
WriteToLog(0,"[终止]------------------信息分割线------------------");
}
return iReturn;
}
/************************************************************************
Function int TcpReceive:
receive data from socket
Input:
SOCKET sckClient 接收数据的socket句柄
int iOffset 接收的偏移位置
int iReceiveSize 接收的大小
int iOverTime 接收的超时限制
OutPut:
BYTE *pchBuffer 接收缓存区
return:
If no error occurs, returns receive data size,other returns SOCKET_ERROR or zero;
Update:
Version Date Author Description
1.0 2008-03-19 Shi Mingjie Create
************************************************************************/
int TcpReceive(SOCKET sckClient,
BYTE *pchBuffer,
int iOffset,
int iReceiveSize,
int iOverTime)
{
int iRev;
int iHaveRev;
int iReadTimes;
int iReadTimesLimit;
iReadTimesLimit = iOverTime / DELAY_WAITSUCCESS;
iReadTimes = 0;
iHaveRev = 0;
Sleep(DELAY_WAITSUCCESS);
iRev = recv(sckClient,(char *)&pchBuffer[iOffset+iHaveRev],iReceiveSize,0);
if (SOCKET_ERROR == iRev)
{
return SOCKET_ERROR;
}
else
{
iHaveRev += iRev;
}
while ((iHaveRev < iReceiveSize) && (iReadTimes++ < iReadTimesLimit))
{
Sleep(DELAY_WAITSUCCESS);
iRev = recv(sckClient,(char *)&pchBuffer[iOffset+iHaveRev],iReceiveSize-iHaveRev,0);
if (SOCKET_ERROR == iRev)
{
return SOCKET_ERROR;
}
else
{
iHaveRev += iRev;
}
}
if (iHaveRev == iReceiveSize)
{
return iHaveRev;
}
else
{
return VALUE_ZERO;
}
}
/************************************************************************
Function int TcpInspectThread:
inspect thread (通过此线程与服务器建立socket连接并监测连接状态)
Input:
UDT_TCPCommunicationClient m_udtTcpClient
OutPut:
UDT_TCPCommunicationClient m_udtTcpClient
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 TcpInspectThread(LPVOID pParam)
{
struct UDT_TCPCommunicationClient *pudtTcpConnect = (struct UDT_TCPCommunicationClient *)pParam;
struct sockaddr_in serverAddr;
DWORD dwTimeCountInterval; // 时间差值
int iRetval;
//DWORD dwServerIp;
//dwServerIp = inet_addr("192.168.58.10");
//memcpy(&serverAddr.sin_addr, &dwServerIp, sizeof(dwServerIp));
//serverAddr.sin_port = htons(6999);
memcpy(&serverAddr.sin_addr, &pudtTcpConnect->dwServerIp, sizeof(pudtTcpConnect->dwServerIp));
serverAddr.sin_port = htons(pudtTcpConnect->wServerPort);
serverAddr.sin_family = AF_INET;
if (pudtTcpConnect->bIsDebug)
{
WriteToLog(0,"TcpInspectThread线程:启动。");
}
for (; ;)
{
if (WAIT_OBJECT_0 == ::WaitForSingleObject(pudtTcpConnect->evtInspectEnd.m_hObject, 0))
{
if (pudtTcpConnect->bIsDebug)
{
WriteToLog(0,"TcpInspectThread线程:接收到信号量终止指令。");
}
break;
}
if (!pudtTcpConnect->bConnectActive) // 网络未连接
{
if (NULL != pudtTcpConnect->sckClient)
{
shutdown(pudtTcpConnect->sckClient,SD_BOTH);
Sleep(DELAY_WAITSUCCESS);
closesocket(pudtTcpConnect->sckClient);
pudtTcpConnect->sckClient = NULL;
}
pudtTcpConnect->sckClient = socket(AF_INET, SOCK_STREAM, 0);
if (INVALID_SOCKET == pudtTcpConnect->sckClient)
{
if (pudtTcpConnect->bIsDebug)
{
WriteToLog("TcpInspectThread线程:socket 函数返回失败。");
}
}
iRetval = connect(pudtTcpConnect->sckClient,(struct sockaddr *)&serverAddr,sizeof(serverAddr));
if (SOCKET_ERROR == iRetval)
{
if (pudtTcpConnect->bIsDebug)
{
WriteToLog(iRetval,"TcpInspectThread线程:connect 连接失败!");
}
}
else if(0 == iRetval) // 建立连接成功
{
EnterCriticalSection(&pudtTcpConnect->ctsClient);
pudtTcpConnect->bConnectActive = TRUE; // 连接成功标志置为TRUE
LeaveCriticalSection(&pudtTcpConnect->ctsClient);
PostMessage(pudtTcpConnect->hWndProcess, pudtTcpConnect->dwMsgConnectSuccess,0,0);
pudtTcpConnect->dwHeartBeatTime = GetTickCount(); // 默认上次心跳时间点为启动线程时间
if (pudtTcpConnect->bIsDebug)
{
WriteToLog(0,"TcpInspectThread线程:connect 连接成功。");
}
}
else
{
WriteToLog(iRetval,"TcpInspectThread线程:connect 返回异常值!");
}
}
else
{
dwTimeCountInterval = GetTickCount() - pudtTcpConnect->dwHeartBeatTime;
if ((dwTimeCountInterval > LIMIT_HEARTBEAT) && (GetTickCount() > pudtTcpConnect->dwHeartBeatTime))
{
EnterCriticalSection(&pudtTcpConnect->ctsClient);
pudtTcpConnect->bConnectActive = FALSE; // 连接成功标志置为FALSE,通过心跳是否超时检测连接是否失效
LeaveCriticalSection(&pudtTcpConnect->ctsClient);
PostMessage(pudtTcpConnect->hWndProcess, pudtTcpConnect->dwMsgDisconnect,0,0);
if (pudtTcpConnect->bIsDebug)
{
WriteToLog(dwTimeCountInterval,"TcpInspectThread线程:检测到心跳超时,重新连接!");
}
}
}
Sleep(DELAY_INSPECT);
}
return VALUE_ZERO;
}
/************************************************************************
Function int TcpServerThread:
server thread (通过此线程与服务器端进行通讯)
Input:
UDT_TCPCommunicationClient m_udtTcpClient
OutPut:
UDT_TCPCommunicationClient m_udtTcpClient
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 TcpServerThread(LPVOID pParam)
{
struct UDT_TCPCommunicationClient *pudtTcpClient = (struct UDT_TCPCommunicationClient *)pParam;
int iSendRetval;
BYTE pbtReceiveBuf[LIMIT_SENDBUFFERSIZE];
int iRealReceive = 0;
int iReceiveRetval = 0;
short siType = 0; // 接收到的包类型
int iLength = 0; // 接收到的包长度
int iToalLength = 0; // 接收到的包总长度
int iImageNearSize = 0; // 接收到的近景图片数据尺寸
int iImageFullSize = 0; // 接收到的全景图片数据尺寸
DWORD dwTimeCountInterval; // 时间差值
int iWaitTimes = 0;
const long TIMEOUT_RECEIVECHECK = 200000; // 单位为microseconds 微妙
struct timeval tvReceive={0,TIMEOUT_RECEIVECHECK};
fd_set fdReceive;
int iSelectRet; // Select函数的返回值
pudtTcpClient->bThreadClientAlive = TRUE;
if (pudtTcpClient->bIsDebug)
{
WriteToLog(0,"TcpServerThread线程:启动。");
}
for (; ;)
{
if (WAIT_OBJECT_0 == ::WaitForSingleObject(pudtTcpClient->evtClientEnd.m_hObject, 0))
{
if (pudtTcpClient->bIsDebug)
{
WriteToLog(0,"TcpServerThread线程:接收到信号量终止指令。");
}
break;
}
// 网络已连接:接包
if (pudtTcpClient->bConnectActive)
{
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.
if (pudtTcpClient->bIsDebug)
{
WriteToLog(iSelectRet,"TcpServerThread线程:select 返回ZERO!");
}
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,"TcpServerThread线程:recv 返回SOCKET_ERROR,继续!");
pudtTcpClient->bConnectActive = FALSE;
PostMessage(pudtTcpClient->hWndProcess, pudtTcpClient->dwMsgDisconnect,0,0);
break;
}
else if (VALUE_ZERO == iReceiveRetval) // If the connection has been gracefully closed, the return value is zero.
{
// pudtTcpClient->evtClientEnd.SetEvent();
WriteToLog(iReceiveRetval,"TcpServerThread线程:recv 返回VALUE_ZERO,客户端连接完美关闭,继续!");
pudtTcpClient->bConnectActive = FALSE;
PostMessage(pudtTcpClient->hWndProcess, pudtTcpClient->dwMsgDisconnect,0,0);
break;
}
else // If no error occurs, recv returns the number of bytes received.
{
siType = 0;
iLength = 0;
if (PACKET_HEADER_LENGTH != iReceiveRetval)
{
WriteToLog(iReceiveRetval,"TcpServerThread线程: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(pudtTcpClient->dwHeartBeatTime,"TcpServerThread线程:接收心跳包正常。");
}
break;
case TYPE_TIME: // 时间包
iReceiveRetval = recv(pudtTcpClient->sckClient,(char *)&pbtReceiveBuf[iRealReceive],iLength-PACKET_HEADER_LENGTH,0);
if (SOCKET_ERROR == iReceiveRetval)
{
if (pudtTcpClient->bIsDebug)
{
WriteToLog("TcpServerThread线程:接收时间数据发生错误!");
}
break;
}
else if (VALUE_ZERO == iReceiveRetval)
{
break;
}
else
{
memcpy(&pudtTcpClient->dwReceiveTime,&pbtReceiveBuf[iRealReceive],sizeof(pudtTcpClient->dwReceiveTime));
if (pudtTcpClient->bIsDebug)
{
WriteToLog(pudtTcpClient->dwReceiveTime,"TcpServerThread线程:校时成功,接收到服务器时间。");
}
}
break;
case TYPE_REALVEHICLE: // 实时车辆信息包
iToalLength = iLength;
// 接收子包1 :通行信息包 TYPE_PASSINFO
iReceiveRetval = recv(pudtTcpClient->sckClient,(char *)&pbtReceiveBuf[iRealReceive],PACKET_HEADER_LENGTH,0);
if (SOCKET_ERROR == iReceiveRetval)
{
break;
}
else if (VALUE_ZERO == iReceiveRetval)
{
break;
}
else
{
memcpy(&siType,&pbtReceiveBuf[iRealReceive],PACKET_TYPE_LENGTH); //包类型 2BYTE
memcpy(&iLength,&pbtReceiveBuf[iRealReceive + PACKET_TYPE_LENGTH],PACKET_LENGTH_LENGTH); //包长度 4BYTE
iRealReceive += iReceiveRetval;
if (TYPE_PASSINFO == siType) // 通行信息包
{
iReceiveRetval = TcpReceive(pudtTcpClient->sckClient,pbtReceiveBuf,iRealReceive,iLength-PACKET_HEADER_LENGTH,LIMIT_WAITRECEIVE);
if (SOCKET_ERROR == iReceiveRetval)
{
break;
}
else if (VALUE_ZERO == iReceiveRetval)
{
break;
}
else
{
iWaitTimes = 0;
while ((pudtTcpClient->bIsGetingData) && (iWaitTimes < LIMIT_WAITQUIT/DELAY_WAITSUCCESS))
{
Sleep(DELAY_WAITSUCCESS);
iWaitTimes++;
}
memcpy(&pudtTcpClient->udtPassInfo,&pbtReceiveBuf[iRealReceive],sizeof(pudtTcpClient->udtPassInfo));
iRealReceive += iReceiveRetval;
}
}
}
// 接收子包2 :特写图片数据包 TYPE_IMAGENEAR
iReceiveRetval = recv(pudtTcpClient->sckClient,(char *)&pbtReceiveBuf[iRealReceive],PACKET_HEADER_LENGTH,0);
if (SOCKET_ERROR == iReceiveRetval)
{
break;
}
else if (VALUE_ZERO == iReceiveRetval)
{
break;
}
else
{
memcpy(&siType,&pbtReceiveBuf[iRealReceive],PACKET_TYPE_LENGTH); //包类型 2BYTE
memcpy(&iLength,&pbtReceiveBuf[iRealReceive + PACKET_TYPE_LENGTH],PACKET_LENGTH_LENGTH); //包长度 4BYTE
iRealReceive += iReceiveRetval;
if (TYPE_IMAGENEAR == siType) // 特写图片数据包
{
iReceiveRetval = TcpReceive(pudtTcpClient->sckClient,pbtReceiveBuf,iRealReceive,iLength-PACKET_HEADER_LENGTH,LIMIT_WAITRECEIVE);
if (SOCKET_ERROR == iReceiveRetval)
{
break;
}
else if (VALUE_ZERO == iReceiveRetval)
{
break;
}
else
{
if (NULL != pudtTcpClient->pbtImageNearBuf)
{
delete[] pudtTcpClient->pbtImageNearBuf;
pudtTcpClient->pbtImageNearBuf = NULL;
}
iImageNearSize = iLength - PACKET_HEADER_LENGTH;
if ((iImageNearSize < LIMIT_MINIMAGESIZE) || (iImageNearSize > LIMIT_MAXIMAGESIZE))
{
// 近景图片大小不正确,退出
if (pudtTcpClient->bIsDebug)
{
WriteToLog(iImageNearSize,"TcpServerThread线程:近景图片长度不合法。");
}
break;
}
pudtTcpClient->pbtImageNearBuf = new BYTE[iImageNearSize];
memcpy(pudtTcpClient->pbtImageNearBuf,&pbtReceiveBuf[iRealReceive],iImageNearSize);
iRealReceive += iReceiveRetval;
}
}
}
// 接收子包3 :全景图片数据包 TYPE_IMAGEFULL
iReceiveRetval = recv(pudtTcpClient->sckClient,(char *)&pbtReceiveBuf[iRealReceive],PACKET_HEADER_LENGTH,0);
if (SOCKET_ERROR == iReceiveRetval)
{
break;
}
else if (VALUE_ZERO == iReceiveRetval)
{
break;
}
else
{
memcpy(&siType,&pbtReceiveBuf[iRealReceive],PACKET_TYPE_LENGTH); //包类型 2BYTE
memcpy(&iLength,&pbtReceiveBuf[iRealReceive + PACKET_TYPE_LENGTH],PACKET_LENGTH_LENGTH); //包长度 4BYTE
iRealReceive += iReceiveRetval;
if (TYPE_IMAGEFULL == siType) // 全景图片数据包
{
iReceiveRetval = TcpReceive(pudtTcpClient->sckClient,pbtReceiveBuf,iRealReceive,iLength-PACKET_HEADER_LENGTH,LIMIT_WAITRECEIVE);
if (SOCKET_ERROR == iReceiveRetval)
{
break;
}
else if (VALUE_ZERO == iReceiveRetval)
{
break;
}
else
{
if (NULL != pudtTcpClient->pbtImageFullBuf)
{
delete[] pudtTcpClient->pbtImageFullBuf;
pudtTcpClient->pbtImageFullBuf = NULL;
}
iImageFullSize = iLength - PACKET_HEADER_LENGTH;
if ((iImageFullSize < LIMIT_MINIMAGESIZE) || (iImageFullSize > LIMIT_MAXIMAGESIZE))
{
// 全景图片大小不正确,退出
if (pudtTcpClient->bIsDebug)
{
WriteToLog(iImageFullSize,"TcpServerThread线程:全景图片长度不合法。");
}
break;
}
pudtTcpClient->pbtImageFullBuf = new BYTE[iImageFullSize];
memcpy(pudtTcpClient->pbtImageFullBuf,&pbtReceiveBuf[iRealReceive],iImageFullSize);
iRealReceive += iReceiveRetval;
}
}
}
EnterCriticalSection(&pudtTcpClient->ctsClient);
pudtTcpClient->bDataIsReady = TRUE;
LeaveCriticalSection(&pudtTcpClient->ctsClient);
PostMessage(pudtTcpClient->hWndProcess, pudtTcpClient->dwMsgDataReady,0,0);
break;
default:
WriteToLog(siType,"TcpServerThread线程:T-L-V协议T不属于已定义类型!继续!");
// pudtTcpClient->evtClientEnd.SetEvent();
pudtTcpClient->bConnectActive = FALSE;
PostMessage(pudtTcpClient->hWndProcess, pudtTcpClient->dwMsgDisconnect,0,0);
break;
}
} // 接收到PACKET_HEADER_LENGTH字节的字符
}
} // end if (FD_ISSET(pudtTcpClient->sckClient,&fdReceive))
else
{
if (pudtTcpClient->bIsDebug)
{
WriteToLog("TcpServerThread线程:FD_ISSET 返回ZERO!");
}
}
break;
}
// 网络已连接:检查是否发送心跳包
if (pudtTcpClient->bConnectActive)
{
dwTimeCountInterval = GetTickCount() - pudtTcpClient->dwHeartBeatTime;
if (dwTimeCountInterval > DELAY_HEARTBEATINTERVAL)
{
iSendRetval = send(pudtTcpClient->sckClient,(char *)pudtTcpClient->pbtSendBufHeartBeat,PACKET_HEADER_LENGTH,0);
if (SOCKET_ERROR == iSendRetval)
{
if (pudtTcpClient->bIsDebug)
{
WriteToLog(iSendRetval,"TcpServerThread线程:心跳包发送失败!");
}
}
else
{
pudtTcpClient->dwHeartBeatTime = GetTickCount(); // 发送成功即认为成功
if (pudtTcpClient->bIsDebug)
{
WriteToLog(iSendRetval,"TcpServerThread线程:心跳包发送成功。");
}
}
}
}
// 网络已连接:检查是否进行校时
if (pudtTcpClient->bConnectActive)
{
if (pudtTcpClient->bIsAdjustTime)
{
iSendRetval = send(pudtTcpClient->sckClient,(char *)pudtTcpClient->pbtSendBufTime,PACKET_TIME_LENGTH,0);
if (SOCKET_ERROR == iSendRetval)
{
if (pudtTcpClient->bIsDebug)
{
WriteToLog(iSendRetval,"TcpServerThread线程:校时包发送失败!");
}
}
else
{
EnterCriticalSection(&pudtTcpClient->ctsClient);
pudtTcpClient->bIsAdjustTime = FALSE; // 发送成功即认为成功
LeaveCriticalSection(&pudtTcpClient->ctsClient);
if (pudtTcpClient->bIsDebug)
{
WriteToLog(iSendRetval,"TcpServerThread线程:校时包发送成功。");
}
}
}
}
}
Sleep(DELAY_WAITSUCCESS);
}
shutdown(pudtTcpClient->sckClient,SD_BOTH);
closesocket(pudtTcpClient->sckClient);
pudtTcpClient->bConnectActive = FALSE;
pudtTcpClient->bThreadClientAlive = FALSE;
return VALUE_ZERO;
}
10/07
10
http://www.neu.edu.cn/cxsj/online/C1/Lisp%E8%AF%AD%E8%A8%80.htm
http://www.lisperati.com/casting.html
http://www.lisperati.com/casting.html
10/07
9
符号
C- 意思是按住 Ctrol 键
M- 意指 Meta 键 (键盘上若无Meta 键,则可以ALT ESC 键来取而代之)
DEL 意指退格键 (不是 删除(Delete) key)
RET 意指回车键
SPC 意指空格键
ESC 意指Escape键
TAB 意指Tab键
像 "C-M-" (or "M-C") 这样连在一起的意味着同时按住 Control 和 Meta 键不放.
用方向键
C-p 、 C-b 、 C-f 和 C-n 这四个命令。它们的功能和方向键是一样的,如下图所示:
上一行 C-p (Prev line)
.
.
.
向左移 C-b .... 。.... 向右移 C-f (Forward character)
(Backward ) .
.
.
下一行 C-n (Next line)
“P N B F”四个字母分别代表了四个词,用这四个词记忆这些组合键会更容易:
P 代表 previous(上一行),
N 代表 next(下一行),
B 代表 backward(回
退),
F 则代表 forward(前进)
进入Emacs
要进入GNU Emacs,只需键入它的名字 emacs
离开Emacs
挂起Emacs: C-z
永久离开Emacs C-x C-c
文件
读取文件到Emacs C-x C-f
保存文件到磁盘 C-x C-s
保存所有文件 C-x s
插入其它文件的内容到当前缓冲 C-x i
用将要读取的文件替换当前文件 C-x C-v
将当前缓冲写入指定的文件 C-x C-w
Version control checkin/checkout C-x C-q
取得帮助
进入帮助系统很简单,只需要输入C-h(或F1)并跟随要获取帮助的对象,初次使用Emacs的用户可以输入C-h t进入使用手册
离开帮助窗口 C-x 1
滚动帮助窗口 C-M-v
匹配:显示与字符a串匹配的命令 C-h a
显示一个键的功能 C-h c
详细描述一个功能 C-h f
取得详细模式的信息 C-h m
错误恢复
取消当前要执行的命令 C-g
恢复系统崩溃后丢失的文件 M-x recover-file
撤销更新 C-x u或C-_
使缓冲回复到初始内容 M-x revert-buffer
Redraw garbaged scree n C-l
增量查找(Incremental Search)
向前查找 C-s
向后查找 C-r
规则表达式查找 C-M-s
反向规则表达式查找 C-M-r
选择前一个查找字符串 M-p
选择下一个查找字符串 M-n
退出增量查找 RET
取消上一个字符的作用 DEL(Backspace)
退出当前查找模式 C-g
在查找的过程中可重复使用C-s和C-r来改变查找方向
移动(Motion)
向前一个字符 C-f
向后一个字符 C-b
向前一个字 M-f
向后一个字 M-b
向上一行 C-p
向下一行 C-n
到行首 C-a
到行尾 C-e
到句首 M-a
到句尾 M-e
到段首 M-{
到段尾 M-}
到页首 C-x [
到页尾 C-x ]
到表达式首部 C-M-f
到表达式尾部 C-M-b
到函数首部 C-M-a
到函数尾部 C-M-e
到缓冲首部 M-<
到缓冲尾部 M->
滚动到下一屏 C-v
滚动到上一屏 M-v
滚动到右边一屏(内容向左移动) C-x <
滚动到左边一屏(内容向右移动) C-x >
滚动当前行到屏幕中央 C-u C-l
Killing和Deleting
向前delete字符 C-d
向后delete字符 DEL(Backspace)
向前delete到字首 M-d
向后delete到字尾 M-DEL(Backspace)
向前delete到行首 M-0 C-k
向后delete到行尾 C-k
向前delete到句首 C-x DEL(Backspace)
向后delete到句尾 M-k
向前delete到表达式首部 M-- C-M-k
向后delete到表达式尾部 C-M-k
Kill区域 C-w
拷贝区域到Kill Ring M-w
Kill到下一个给定字符出现的位置 M-z
拉回(yank)上次kill的内容 C-y
用更早kill的内容取代拉回的上次kill的内容 M-y
标记(Marking)
标记当前位置 C-SPC或C-@
以字符为单位使用移动命令动态标记区域 C-x C-x
以字为单位使用移动命令动态标记区域 M-@
标记一段 M-h
标记一页 C-x C-p
标记一个表达式 C-M-@
标记一个函数 C-M-h
标记整个缓冲区 C-x h
Query Replace
交互式地替换一个文本串 M-%
交互式地替换一个规则表达式 M-x query-replace-regexp
替换当前的并移动到下一处 SPE
替换当前的但不移动到下一处 ,
不替换当前的并移动到下一处 L(Backspace)
替换所有剩下的符合条件的文本 !
退出替换模式 RET
进入递归的编辑模式 C-r
退出递归的编辑模式 C-M-c
多窗口(Multiple Windows)
(When two commands are shown,the second is for “other frame”)
删除所有其它窗口 C-x 1
上下分割当前窗口 C-x 2 C-x 5 2
左右分割当前窗口 C-x 3
删除当前窗口 C-x 0 C-x 5 0
滚动其它窗口 C-M-v
切换光标到另一个窗口 C-x o
选择另一个窗口中的缓冲 C-x 4 b C-x 5 b
显示另一个窗口中的缓冲 C-x 4 C-o C-x 5 C-o
在另一窗口中查找并打开文件 C-x 4 f C-x 5 f
在另一窗口中以只读方式打开文件 C-x 4 r C-x 5 r
在另一窗口中运行dired命令 C-x 4 d C-x 5 d
在另一窗口中查找tag C-x 4 . C-x 5 .
增加窗口高度 C-x ^
减小窗口宽度 C-x {
增加窗口宽度 C-x }
格式(Formatting)
缩进当前行(与模式相关) TAB
缩进区域(与模式相关) C-M-
缩进表达式(与模式相关) C-M-q
Indent region rigidly arg. Columns C-x TAB
在光标后插入一个新的行 C-o
静态地将一行下移 C-M-o
删除光标附近的空行(留下一行) C-x C-o
与上一行合并成一行 M-^
删除光标附近的所有空格 M-
删除光标附近的空格(留下一格) M-SPC
Fill paragraph M-q
Set fill column C-x f
设置每一行开始的前缀 C-x .
设置字体 M-g
Case Change
将一个字设置为大写 M-u
将一个字设置为小写 M-l
将一个字首字母设置为大写 M-c
将一个区域设置为大写 C-x C-u
将一个区域设置为小写 C-x C-l
The Minibuffer
(the following keys are defined in the minibuffer)
最大程度地补全命令 TAB
补全命令中的一个字 SPC
完成并执行一个命令 RET
列出命令所有可能的后续部分 ?
列出在当前命令之前输入的命令 M-p
列出在当前命令之后输入的命令 M-n
用规则表达式在命令历史记录中向后搜寻 M-r
用规则表达式在命令历史记录中向前搜寻 M-s
重复执行上一条命令 C-x ESC ESC
缓冲(Buffer)
选择另一个缓冲 C-x b
列出所有的缓冲 C-x C-b
Kill一个缓冲 C-x k
置换(Transposing)
字符置换 C-t
字置换 M-t
行置换 C-x C-t
表达式置换 C-M-t
拼写检查(Spelling Check)
对当前的字进行拼写检查 M-$
检查区域内所有的字 M-x ispell-origin
检查缓冲内所有的字 M-x ispell-buffer
标记 (Tags)
查找标记 M-.
查找标记下一次出现的位置 C-u M-.
指定一个新的标记文件 M-x visit-tags-table
Regexp search on all files in tabs table M-x tags-search
在所有文件中执行查询-替换 M-x tags-query-replace
继续进行上一次标记查找或查询-替换 M-,
Shells
执行一个shell命令 M-!
在一个区域上执行sheel命令 M-|
通过shell命令过滤区域 C-u M-|
在窗口中启动一个shell M-x shell
矩形(Rectangles)
拷贝一个矩形到寄存器 C-x r r
Kill矩形 C-x r k
拉回矩形 C-x r y
打开一个矩形, 将文本移动至右边 C-x r o
清空矩形 C-x r c
为矩形中每一行加上一个字符串前缀 C-x r t
规则表达式(Regular Expressions)
除换行符外的所有单个字符 .
零个或多个重复 *
一个以上的重复 +
零个或一个重复 ?
转译字符
选择(or) |
分组 (…)
与第n个组相同的文本 n
At work break b
Not at work break B
寄存器(Register)
存储区域到寄存器 C-x r s
插入矩形内容到缓冲 C-x r i
存储光标位置到寄存器 C-x r SPC
跳跃到寄存器中存储的光标位置 C-x r j
键盘宏(Keyboard Macros)
开始定义一个键盘宏 C-x (
结束键盘宏的定义 C-x )
执行上一次定义的键盘宏 C-x e
追加到上一个键盘宏 C-u C-x (
为上一个键盘宏命名 M-x name-last-kbd-macro
在缓冲中插入Lisp M-x insert-kbd-macro
Tags
Tags 是一个显为人知的功能? 所以我想提一下. 这不是emacs发明的, 而是vi 原本的特异功能. emacs只是发扬光大而已.
假设你有一个目录, 里面是一个程式的原始码, 比如说, tin 的原始码, 放在 ~/tin-1.3beta 下面. 你想看它们.
首先, 叫emacs cd到该目录: M-x cd
然后, 建立tag table. tag table 就是一张对照表, 记录哪个符号(variable/function call) 对映到哪个档案的哪个地方. 有这张表, emacs可以让我们快速的在程式码内游走. 一般这张表是一个档案, 叫作TAGS (大写)
M-! etags *.c
M-! 是执行external shell command的意思. etags就是emacs的建表程式. 你只要告诉它你的source code在那里即可.
vi的话是使用ctags这个程式, 它建出来的档名叫tags (小写). 因为我们介绍emacs, 所以不管它.
然 后, 怎么看程式? 你知道所有的C 程式都是由main()开始, 所以你想找到main()在哪个档案. 这时只要按 M-. 然后emacs会问你tag table在哪里. 因为我们已经cd到该目录, 直接按enter就好了. 然后输入main, emacs就会把你带到main(){ ... }去.
如果 你看到某个程式片断呼叫一个你没看过的函式, 你可以把游标移到该函式的名字上, M-. ENTER 就搞定了.
如果 emacs找错了 (比如有变数和函式同名, emacs跳到变数去), 那你可以用 C-u M-. 找下一个.
在编辑程式码的时候, M-SPC 很有用, 它会把游标附近的空白缩成一个. 在其它地方也有效.
拼写检查
当然只是针对英文。
Ispell
选中一块区域,或者对整个编辑缓冲区进行拼写检查: M-x ispell-buffer RET, 这时会打开ispell缓冲区,C-h可以查看一些拼写检查的帮助信息。
检查单词。在一个单词上执行M-$,会对这个单词进行拼写检查。
单词拼写补全。在一个未拼完的单词后执行ESC TAB(M-TAB)。
只要启用过Ispell, 他就将一直在后台运行。M-x ispell-kill-ispell,可以杀死这个进程。
感觉很好用。
flyspell
一个扩展,可以在编辑的时候直接进行拼写检查,也就是spell-check on the fly。它也是利用Ispell。
M-x flyspell-mode RET
Tips
改变buffer的只读属性
M-x toggle-read-only
在C模式下输入tab
C-q TAB : 对TAB不做解释,直接输入
C- 意思是按住 Ctrol 键
M- 意指 Meta 键 (键盘上若无Meta 键,则可以ALT ESC 键来取而代之)
DEL 意指退格键 (不是 删除(Delete) key)
RET 意指回车键
SPC 意指空格键
ESC 意指Escape键
TAB 意指Tab键
像 "C-M-" (or "M-C") 这样连在一起的意味着同时按住 Control 和 Meta 键不放.
用方向键
C-p 、 C-b 、 C-f 和 C-n 这四个命令。它们的功能和方向键是一样的,如下图所示:
上一行 C-p (Prev line)
.
.
.
向左移 C-b .... 。.... 向右移 C-f (Forward character)
(Backward ) .
.
.
下一行 C-n (Next line)
“P N B F”四个字母分别代表了四个词,用这四个词记忆这些组合键会更容易:
P 代表 previous(上一行),
N 代表 next(下一行),
B 代表 backward(回
退),
F 则代表 forward(前进)
进入Emacs
要进入GNU Emacs,只需键入它的名字 emacs
离开Emacs
挂起Emacs: C-z
永久离开Emacs C-x C-c
文件
读取文件到Emacs C-x C-f
保存文件到磁盘 C-x C-s
保存所有文件 C-x s
插入其它文件的内容到当前缓冲 C-x i
用将要读取的文件替换当前文件 C-x C-v
将当前缓冲写入指定的文件 C-x C-w
Version control checkin/checkout C-x C-q
取得帮助
进入帮助系统很简单,只需要输入C-h(或F1)并跟随要获取帮助的对象,初次使用Emacs的用户可以输入C-h t进入使用手册
离开帮助窗口 C-x 1
滚动帮助窗口 C-M-v
匹配:显示与字符a串匹配的命令 C-h a
显示一个键的功能 C-h c
详细描述一个功能 C-h f
取得详细模式的信息 C-h m
错误恢复
取消当前要执行的命令 C-g
恢复系统崩溃后丢失的文件 M-x recover-file
撤销更新 C-x u或C-_
使缓冲回复到初始内容 M-x revert-buffer
Redraw garbaged scree n C-l
增量查找(Incremental Search)
向前查找 C-s
向后查找 C-r
规则表达式查找 C-M-s
反向规则表达式查找 C-M-r
选择前一个查找字符串 M-p
选择下一个查找字符串 M-n
退出增量查找 RET
取消上一个字符的作用 DEL(Backspace)
退出当前查找模式 C-g
在查找的过程中可重复使用C-s和C-r来改变查找方向
移动(Motion)
向前一个字符 C-f
向后一个字符 C-b
向前一个字 M-f
向后一个字 M-b
向上一行 C-p
向下一行 C-n
到行首 C-a
到行尾 C-e
到句首 M-a
到句尾 M-e
到段首 M-{
到段尾 M-}
到页首 C-x [
到页尾 C-x ]
到表达式首部 C-M-f
到表达式尾部 C-M-b
到函数首部 C-M-a
到函数尾部 C-M-e
到缓冲首部 M-<
到缓冲尾部 M->
滚动到下一屏 C-v
滚动到上一屏 M-v
滚动到右边一屏(内容向左移动) C-x <
滚动到左边一屏(内容向右移动) C-x >
滚动当前行到屏幕中央 C-u C-l
Killing和Deleting
向前delete字符 C-d
向后delete字符 DEL(Backspace)
向前delete到字首 M-d
向后delete到字尾 M-DEL(Backspace)
向前delete到行首 M-0 C-k
向后delete到行尾 C-k
向前delete到句首 C-x DEL(Backspace)
向后delete到句尾 M-k
向前delete到表达式首部 M-- C-M-k
向后delete到表达式尾部 C-M-k
Kill区域 C-w
拷贝区域到Kill Ring M-w
Kill到下一个给定字符出现的位置 M-z
拉回(yank)上次kill的内容 C-y
用更早kill的内容取代拉回的上次kill的内容 M-y
标记(Marking)
标记当前位置 C-SPC或C-@
以字符为单位使用移动命令动态标记区域 C-x C-x
以字为单位使用移动命令动态标记区域 M-@
标记一段 M-h
标记一页 C-x C-p
标记一个表达式 C-M-@
标记一个函数 C-M-h
标记整个缓冲区 C-x h
Query Replace
交互式地替换一个文本串 M-%
交互式地替换一个规则表达式 M-x query-replace-regexp
替换当前的并移动到下一处 SPE
替换当前的但不移动到下一处 ,
不替换当前的并移动到下一处 L(Backspace)
替换所有剩下的符合条件的文本 !
退出替换模式 RET
进入递归的编辑模式 C-r
退出递归的编辑模式 C-M-c
多窗口(Multiple Windows)
(When two commands are shown,the second is for “other frame”)
删除所有其它窗口 C-x 1
上下分割当前窗口 C-x 2 C-x 5 2
左右分割当前窗口 C-x 3
删除当前窗口 C-x 0 C-x 5 0
滚动其它窗口 C-M-v
切换光标到另一个窗口 C-x o
选择另一个窗口中的缓冲 C-x 4 b C-x 5 b
显示另一个窗口中的缓冲 C-x 4 C-o C-x 5 C-o
在另一窗口中查找并打开文件 C-x 4 f C-x 5 f
在另一窗口中以只读方式打开文件 C-x 4 r C-x 5 r
在另一窗口中运行dired命令 C-x 4 d C-x 5 d
在另一窗口中查找tag C-x 4 . C-x 5 .
增加窗口高度 C-x ^
减小窗口宽度 C-x {
增加窗口宽度 C-x }
格式(Formatting)
缩进当前行(与模式相关) TAB
缩进区域(与模式相关) C-M-
缩进表达式(与模式相关) C-M-q
Indent region rigidly arg. Columns C-x TAB
在光标后插入一个新的行 C-o
静态地将一行下移 C-M-o
删除光标附近的空行(留下一行) C-x C-o
与上一行合并成一行 M-^
删除光标附近的所有空格 M-
删除光标附近的空格(留下一格) M-SPC
Fill paragraph M-q
Set fill column C-x f
设置每一行开始的前缀 C-x .
设置字体 M-g
Case Change
将一个字设置为大写 M-u
将一个字设置为小写 M-l
将一个字首字母设置为大写 M-c
将一个区域设置为大写 C-x C-u
将一个区域设置为小写 C-x C-l
The Minibuffer
(the following keys are defined in the minibuffer)
最大程度地补全命令 TAB
补全命令中的一个字 SPC
完成并执行一个命令 RET
列出命令所有可能的后续部分 ?
列出在当前命令之前输入的命令 M-p
列出在当前命令之后输入的命令 M-n
用规则表达式在命令历史记录中向后搜寻 M-r
用规则表达式在命令历史记录中向前搜寻 M-s
重复执行上一条命令 C-x ESC ESC
缓冲(Buffer)
选择另一个缓冲 C-x b
列出所有的缓冲 C-x C-b
Kill一个缓冲 C-x k
置换(Transposing)
字符置换 C-t
字置换 M-t
行置换 C-x C-t
表达式置换 C-M-t
拼写检查(Spelling Check)
对当前的字进行拼写检查 M-$
检查区域内所有的字 M-x ispell-origin
检查缓冲内所有的字 M-x ispell-buffer
标记 (Tags)
查找标记 M-.
查找标记下一次出现的位置 C-u M-.
指定一个新的标记文件 M-x visit-tags-table
Regexp search on all files in tabs table M-x tags-search
在所有文件中执行查询-替换 M-x tags-query-replace
继续进行上一次标记查找或查询-替换 M-,
Shells
执行一个shell命令 M-!
在一个区域上执行sheel命令 M-|
通过shell命令过滤区域 C-u M-|
在窗口中启动一个shell M-x shell
矩形(Rectangles)
拷贝一个矩形到寄存器 C-x r r
Kill矩形 C-x r k
拉回矩形 C-x r y
打开一个矩形, 将文本移动至右边 C-x r o
清空矩形 C-x r c
为矩形中每一行加上一个字符串前缀 C-x r t
规则表达式(Regular Expressions)
除换行符外的所有单个字符 .
零个或多个重复 *
一个以上的重复 +
零个或一个重复 ?
转译字符
选择(or) |
分组 (…)
与第n个组相同的文本 n
At work break b
Not at work break B
寄存器(Register)
存储区域到寄存器 C-x r s
插入矩形内容到缓冲 C-x r i
存储光标位置到寄存器 C-x r SPC
跳跃到寄存器中存储的光标位置 C-x r j
键盘宏(Keyboard Macros)
开始定义一个键盘宏 C-x (
结束键盘宏的定义 C-x )
执行上一次定义的键盘宏 C-x e
追加到上一个键盘宏 C-u C-x (
为上一个键盘宏命名 M-x name-last-kbd-macro
在缓冲中插入Lisp M-x insert-kbd-macro
Tags
Tags 是一个显为人知的功能? 所以我想提一下. 这不是emacs发明的, 而是vi 原本的特异功能. emacs只是发扬光大而已.
假设你有一个目录, 里面是一个程式的原始码, 比如说, tin 的原始码, 放在 ~/tin-1.3beta 下面. 你想看它们.
首先, 叫emacs cd到该目录: M-x cd
然后, 建立tag table. tag table 就是一张对照表, 记录哪个符号(variable/function call) 对映到哪个档案的哪个地方. 有这张表, emacs可以让我们快速的在程式码内游走. 一般这张表是一个档案, 叫作TAGS (大写)
M-! etags *.c
M-! 是执行external shell command的意思. etags就是emacs的建表程式. 你只要告诉它你的source code在那里即可.
vi的话是使用ctags这个程式, 它建出来的档名叫tags (小写). 因为我们介绍emacs, 所以不管它.
然 后, 怎么看程式? 你知道所有的C 程式都是由main()开始, 所以你想找到main()在哪个档案. 这时只要按 M-. 然后emacs会问你tag table在哪里. 因为我们已经cd到该目录, 直接按enter就好了. 然后输入main, emacs就会把你带到main(){ ... }去.
如果 你看到某个程式片断呼叫一个你没看过的函式, 你可以把游标移到该函式的名字上, M-. ENTER 就搞定了.
如果 emacs找错了 (比如有变数和函式同名, emacs跳到变数去), 那你可以用 C-u M-. 找下一个.
在编辑程式码的时候, M-SPC 很有用, 它会把游标附近的空白缩成一个. 在其它地方也有效.
拼写检查
当然只是针对英文。
Ispell
选中一块区域,或者对整个编辑缓冲区进行拼写检查: M-x ispell-buffer RET, 这时会打开ispell缓冲区,C-h可以查看一些拼写检查的帮助信息。
检查单词。在一个单词上执行M-$,会对这个单词进行拼写检查。
单词拼写补全。在一个未拼完的单词后执行ESC TAB(M-TAB)。
只要启用过Ispell, 他就将一直在后台运行。M-x ispell-kill-ispell,可以杀死这个进程。
感觉很好用。
flyspell
一个扩展,可以在编辑的时候直接进行拼写检查,也就是spell-check on the fly。它也是利用Ispell。
M-x flyspell-mode RET
Tips
改变buffer的只读属性
M-x toggle-read-only
在C模式下输入tab
C-q TAB : 对TAB不做解释,直接输入
10/07
2
一。 安装vsftp和db4
sudo apt-get install vsftpd
sudo apt-get install db4.6-util
二。建立虚拟用户口令库文件
sudo mkdir /etc/vsftpd
新建名为logins.txt 的用户口令文件,
sudo gedit /etc/vsftpd/logins.txt
奇数行为用户名。偶数行为密码,如下用户 User1的密码为123456,User2的密码为12345
User1
123456
User2
12345
admin
admin
三。生成vsftpd的认证文件
使用db_load命令生成认证文件:
db4.6_load -T -t hash -f /etc/vsftpd/logins.txt /etc/vsftpd/vsftpd_login.db
将vsftpd_login.db的权限设为只对root可读写,即600
chmod 600 /etc/vsftpd/vsftpd_login.db
四。建立虚拟用户所需的PAM配置文件
在/etc/pam.d目录中建立vsftpd.vu内容如下
sudo gedit /etc/pam.d/vsftpd.vu
auth required /lib/security/pam_userdb.so db=/etc/vsftpd/vsftpd_login
account required /lib/security/pam_userdb.so db=/etc/vsftpd/vsftpd_login
五。建立虚拟用户所需的系统用户和主目录
sudo useradd jtmvi -d /www -s /bin/false
sudo chown jtmvi.jtmvi /www
sudo chown jtmvi.jtmvi /www/User1
sudo chown jtmvi.jtmvi /www/User2
sudo chmod 700 /www
六。设置vsftpd.conf配置文件:(记得先备份)
sudo cp /etc/vsftpd.conf /etc/vsftpd.conf_old #备份
gedit /etc/vsftpd.conf
listen=YES
anonymous_enable=NO
dirmessage_enable=YES
xferlog_enable=YES
xferlog_file=/var/log/vsftpd.log
xferlog_std_format=YES
chroot_local_user=YES
guest_enable=YES
guest_username=jtmvi
user_config_dir=/etc/vsftpd_user_conf
pam_service_name=vsftpd.vu
local_enable=YES
anon_umask=022
七 。 对不同的虚拟用户设置不同权限
sudo mkdir /etc/vsftpd_user_conf
gedit /etc/vsftpd_user_conf/User1
write_enable=YES
anon_world_readable_only=NO
anon_upload_enable=YES
anon_mkdir_write_enable=YES
anon_other_write_enable=YES
local_root=/www/User1
gedit /etc/vsftpd_user_conf/User2
write_enable=YES
anon_world_readable_only=NO
anon_upload_enable=YES
anon_mkdir_write_enable=YES
anon_other_write_enable=YES
local_root=/www/User2
gedit /etc/vsftpd_user_conf/admin
write_enable=YES
anon_world_readable_only=NO
anon_upload_enable=YES
anon_mkdir_write_enable=YES
anon_other_write_enable=YES
local_root=/www
八。重启vsftpd
/etc/init.d/vsftpd restart
sudo apt-get install vsftpd
sudo apt-get install db4.6-util
二。建立虚拟用户口令库文件
sudo mkdir /etc/vsftpd
新建名为logins.txt 的用户口令文件,
sudo gedit /etc/vsftpd/logins.txt
奇数行为用户名。偶数行为密码,如下用户 User1的密码为123456,User2的密码为12345
User1
123456
User2
12345
admin
admin
三。生成vsftpd的认证文件
使用db_load命令生成认证文件:
db4.6_load -T -t hash -f /etc/vsftpd/logins.txt /etc/vsftpd/vsftpd_login.db
将vsftpd_login.db的权限设为只对root可读写,即600
chmod 600 /etc/vsftpd/vsftpd_login.db
四。建立虚拟用户所需的PAM配置文件
在/etc/pam.d目录中建立vsftpd.vu内容如下
sudo gedit /etc/pam.d/vsftpd.vu
auth required /lib/security/pam_userdb.so db=/etc/vsftpd/vsftpd_login
account required /lib/security/pam_userdb.so db=/etc/vsftpd/vsftpd_login
五。建立虚拟用户所需的系统用户和主目录
sudo useradd jtmvi -d /www -s /bin/false
sudo chown jtmvi.jtmvi /www
sudo chown jtmvi.jtmvi /www/User1
sudo chown jtmvi.jtmvi /www/User2
sudo chmod 700 /www
六。设置vsftpd.conf配置文件:(记得先备份)
sudo cp /etc/vsftpd.conf /etc/vsftpd.conf_old #备份
gedit /etc/vsftpd.conf
listen=YES
anonymous_enable=NO
dirmessage_enable=YES
xferlog_enable=YES
xferlog_file=/var/log/vsftpd.log
xferlog_std_format=YES
chroot_local_user=YES
guest_enable=YES
guest_username=jtmvi
user_config_dir=/etc/vsftpd_user_conf
pam_service_name=vsftpd.vu
local_enable=YES
anon_umask=022
七 。 对不同的虚拟用户设置不同权限
sudo mkdir /etc/vsftpd_user_conf
gedit /etc/vsftpd_user_conf/User1
write_enable=YES
anon_world_readable_only=NO
anon_upload_enable=YES
anon_mkdir_write_enable=YES
anon_other_write_enable=YES
local_root=/www/User1
gedit /etc/vsftpd_user_conf/User2
write_enable=YES
anon_world_readable_only=NO
anon_upload_enable=YES
anon_mkdir_write_enable=YES
anon_other_write_enable=YES
local_root=/www/User2
gedit /etc/vsftpd_user_conf/admin
write_enable=YES
anon_world_readable_only=NO
anon_upload_enable=YES
anon_mkdir_write_enable=YES
anon_other_write_enable=YES
local_root=/www
八。重启vsftpd
/etc/init.d/vsftpd restart
10/06
25
把图片实时同步到图片服务器。。。
10/06
24
1.先启动一个测试用的ttserver
注意使用的所有路径都是绝对路径
ttserver -host 192.168.0.100 -port 20000 -thnum 4 -dmn -ulim 1024m -ulog $(pwd)/ulog/ -log $(pwd)/temp/test.log -pid $(pwd)/temp/test.pid -sid 9 $(pwd)/temp/test_data.tch#bnum=1000#rcnum=0#xmsiz=0m
2. 查看服务器统计信息
tcrmgr inform -port 20000 -st 192.168.0.100
3. 写入数据
tcrmgr put -port 20000 192.168.0.100 test1 value1
4. 读取数据
tcrmgr get -port 20000 192.168.0.100 test1
5. 删除数据
tcrmgr out -port 20000 192.168.0.100 test1
6. 查看所有的key
tcrmgr list -port 20000 192.168.0.100
7. 备份数据
注意:路径是服务器上的绝对路径,否则显示“./tcrmgr: error: 9999: miscellaneous error”
tcrmgr copy -port 20000 192.168.0.100 $(pwd)/temp/test2.tch
8. 日志导出(屏幕上打印,十六进制字符串格式)
注意:命令不同了
注意:目录是服务器上的ulog的绝对路径
ttulmgr export $(pwd)/ulog/
9. 同步内存数据到磁盘(没搞懂有什么特别之处,可能是当ttserver使用-uas参数异步写入日志的时候起作用)
tcrmgr sync -port 20000 192.168.0.100
10. 数据导入
注意:tsv格式的文件以TAB分隔,如:test2\tvalue2\n
注意:路径是本地路径,所以不必是绝对路径
tcrmgr importtsv -port 20000 192.168.0.100 temp/2.tsv
11. 通过ulog日志恢复数据
注意:路径是服务器上的绝对路径
tcrmgr restore -port 20000 192.168.0.100 $(pwd)/old_ulog/
12. 打印更新日志(挂起,一直显示日志)
可能是用于实时查看ttserver有哪些操作,相当于tail -f
tcrmgr repl -port 20000 -ph 192.168.0.100
13. 下一个实验:启动一个带复制功能的ttserver,以前面启动的ttserver为master
ttserver -host 192.168.0.100 -port 20001 -mhost 192.168.0.100 -mport 20000 -rcc -rts $(pwd)/temp_1/test_1.rts -thnum 4 -dmn -ulim 1024m -ulog $(pwd)/temp_1/ -log $(pwd)/temp_1/test_1.log -pid $(pwd)/temp_1/test_1.pid -sid 10 $(pwd)/temp_1/test_1.tch#bnum=1000#rcnum=0#xmsiz=0m
14. 再启动一个普通的ttserver,不与其他服务器相关
ttserver -host 192.168.0.100 -port 20002 -thnum 4 -dmn -ulim 1024m -ulog $(pwd)/ulog_2/ -log $(pwd)/temp_2/test_2.log -pid $(pwd)/temp_2/test_2.pid -sid 11 $(pwd)/temp_2/test_data_2.tch#bnum=1000#rcnum=0#xmsiz=0m
15. 修改某个ttserver的master
tcrmgr setmst -port 20001 -mport 20002 192.168.0.100 192.168.0.100
注意:只有以复制方式启动的ttserver,修改master后才能从新的数据库复制数据
注意:每个ttserver只能有一个master,修改后,不能再从以前的master复制数据
注意使用的所有路径都是绝对路径
ttserver -host 192.168.0.100 -port 20000 -thnum 4 -dmn -ulim 1024m -ulog $(pwd)/ulog/ -log $(pwd)/temp/test.log -pid $(pwd)/temp/test.pid -sid 9 $(pwd)/temp/test_data.tch#bnum=1000#rcnum=0#xmsiz=0m
2. 查看服务器统计信息
tcrmgr inform -port 20000 -st 192.168.0.100
3. 写入数据
tcrmgr put -port 20000 192.168.0.100 test1 value1
4. 读取数据
tcrmgr get -port 20000 192.168.0.100 test1
5. 删除数据
tcrmgr out -port 20000 192.168.0.100 test1
6. 查看所有的key
tcrmgr list -port 20000 192.168.0.100
7. 备份数据
注意:路径是服务器上的绝对路径,否则显示“./tcrmgr: error: 9999: miscellaneous error”
tcrmgr copy -port 20000 192.168.0.100 $(pwd)/temp/test2.tch
8. 日志导出(屏幕上打印,十六进制字符串格式)
注意:命令不同了
注意:目录是服务器上的ulog的绝对路径
ttulmgr export $(pwd)/ulog/
9. 同步内存数据到磁盘(没搞懂有什么特别之处,可能是当ttserver使用-uas参数异步写入日志的时候起作用)
tcrmgr sync -port 20000 192.168.0.100
10. 数据导入
注意:tsv格式的文件以TAB分隔,如:test2\tvalue2\n
注意:路径是本地路径,所以不必是绝对路径
tcrmgr importtsv -port 20000 192.168.0.100 temp/2.tsv
11. 通过ulog日志恢复数据
注意:路径是服务器上的绝对路径
tcrmgr restore -port 20000 192.168.0.100 $(pwd)/old_ulog/
12. 打印更新日志(挂起,一直显示日志)
可能是用于实时查看ttserver有哪些操作,相当于tail -f
tcrmgr repl -port 20000 -ph 192.168.0.100
13. 下一个实验:启动一个带复制功能的ttserver,以前面启动的ttserver为master
ttserver -host 192.168.0.100 -port 20001 -mhost 192.168.0.100 -mport 20000 -rcc -rts $(pwd)/temp_1/test_1.rts -thnum 4 -dmn -ulim 1024m -ulog $(pwd)/temp_1/ -log $(pwd)/temp_1/test_1.log -pid $(pwd)/temp_1/test_1.pid -sid 10 $(pwd)/temp_1/test_1.tch#bnum=1000#rcnum=0#xmsiz=0m
14. 再启动一个普通的ttserver,不与其他服务器相关
ttserver -host 192.168.0.100 -port 20002 -thnum 4 -dmn -ulim 1024m -ulog $(pwd)/ulog_2/ -log $(pwd)/temp_2/test_2.log -pid $(pwd)/temp_2/test_2.pid -sid 11 $(pwd)/temp_2/test_data_2.tch#bnum=1000#rcnum=0#xmsiz=0m
15. 修改某个ttserver的master
tcrmgr setmst -port 20001 -mport 20002 192.168.0.100 192.168.0.100
注意:只有以复制方式启动的ttserver,修改master后才能从新的数据库复制数据
注意:每个ttserver只能有一个master,修改后,不能再从以前的master复制数据
10/06
23
#!/bin/sh
for F in `ls user_post/config.*.php`
do
/php/bin/php-cgi $F > /dev/null &
done
for F in `ls user_post/config.*.php`
do
/php/bin/php-cgi $F > /dev/null &
done
放到后台运行的方法
nohup jasonyu.php &






