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;
}
10/07 10

Emacs Lisp语言 不指定

jason , 08:10 , 我的收藏 » Other , 评论(0) , 引用(0) , 阅读(1627) , Via 本站原创
http://www.neu.edu.cn/cxsj/online/C1/Lisp%E8%AF%AD%E8%A8%80.htm
http://www.lisperati.com/casting.html
Tags:
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不做解释,直接输入
Tags:
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
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复制数据
Tags:
10/06 23
#!/bin/sh
for F in `ls user_post/config.*.php`
do
         /php/bin/php-cgi $F > /dev/null &
done



放到后台运行的方法
nohup jasonyu.php &
分页: 4/24 第一页 上页 1 2 3 4 5 6 7 8 9 10 下页 最后页 [ 显示模式: 摘要 | 列表 ]