hp 0706精华月刊

版权声明:

1.本刊中所刊登的文章版权均为原作者所有,经作者同意,本刊仅为交流之目的进行转载;

2.使用本刊中所刊登的文章须经作者同意,法律规定可以不经许可的除外;

3.本刊内的稿件来源于HP iPortal(惠普企业解决方案合作伙伴门户),由门户用户提供或推荐。如果对任何稿件的版权有争议,请与我们联系,我们将尽快取下稿件,并协助您与稿件提供者取得联系;

4.稿件提供者承诺和保证版权的合法性,并有权同意HP iPortal为交流之目的进行转载,所有有关稿件版权的纠纷将由稿件提供者负责解决并承担相应的责任。

免责条款:

1.本刊不声明或保证所刊登文章的内容之正确性或可靠性;读者因接受并信赖任何文章内容所产生之风险应自行承担。

2.本刊是基于用户提供或推荐的稿件现状进行转载,而且本刊明确地表示拒绝对于转载的文章给予任何明示或暗示之保证,包括但不限于,得为商业使用或适合于特定目的之保证。本刊对于读者依赖转载文章所发生之任何直接、间接、附带的或因此而导致之衍生性损失概不负责。

Table of contents

MSVC6.0的远程调试技术 (4)

配置分为两大步骤 (4)

MYSQL数据库大数据量的导入 (10)

整个操作过程如下: (10)

Windows平台面向对象定时器的设计 (12)

1.设想定时器对象 (12)

2.Windows定时器技术 (12)

3.定时器的设计 (14)

4.定时器的实现 (15)

5.定时器的使用 (18)

6.总结 (21)

7.参考 (22)

关于批处理中的延迟变量理解 (23)

发挥品牌服务器优势建国家软件公共服务平台 (24)

后关系型数据库Caché安装体验 (29)

Proliant系列服务器优势分析 (34)

Non Stop的背景及起该名字的意义 (37)

一、 Non Stop的背景 (37)

二、 “Non Stop”名字的意义 (37)

MSVC6.0的远程调试技术

weiyun 假想一个MSVC6.0开发的服务器程序,上线后运行在一台HP PC SERVER上,这台PC SERVER放置在嘈杂的机房里。现在程序出了点小问题,经理让我调试解决这个问题。如果我要直接到机房的PC SERVER 上调试程序,那么还要在PC SERVER上安装一套Microsoft Visual C++,而且机房也不是耐心的调试程序、静心的思考问题的理想场所。但是要在我的个人PC机上直接调试,又有一些其他问题:一是我的PC机上是开发环境,与运行环境有差别,要把运行环境的有关数据挪到开发环境中需要费一番周折;二是在开发环境里又要运行IDE环境(Visual C++)、又要运行服务器程序和数据库,PC机的负担比较重,响应速度会变慢。

这时候,理想的解决办法是使用远程调试技术。网上有相关的文章介绍MSVC6.0的远程调试技术,但是都比较简略,有些的地方还有错漏。我参考了一些文章,并根据自己的实际调试经历做了总结。下面说一下利用MSVC6.0进行远程调试的详细步骤。

我们把HP PC SERVER叫做“远程机”,自己调试用的个人PC为“本地机”。远程机和本地机通过局域网相连,能够ping通以保证互相访问,假设远程机的IP为192.168.12.111,本地机的IP为192.168.12.98。

配置分为两大步骤

第一:

要在远程机上运行起监听程序。这个程序叫做Visual C++ Debug Monitor(Msvcmon.exe),远程机上如果安装了MSVC6.0,那么直接到MSDEV.exe所在的目录下即可找到这个程序。如果远程机没有安装Microsoft Visual C++,那么到本地机的MSVC6.0环境下找到Msvcmon.exe(我的机器上的目录是C:\Program Files\Microsoft Visual Studio\Common\MSDev98\Bin),复制传送到远程机的某个目录下,同时还要保证远程机上有以下文件:Msvcrt.dll、Tln0t.dll、Dm.dll、Msvcp60.dll、Msdis100.dll。网上有文章这么写的,但我感觉可能不是每一个DLL都是必需的,我懒得一个个删除、再运行Msvcmon.exe、试验哪些不是必需的,有些可能已经在系统目录存在了。我只复制了Msvcmon.exe即可运行。双击Msvcmon.exe 后运行出如下界面:

点击settings后填写本地机的IP:

点击OK按钮后,再点击Connect按钮,出现如下界面:

表明监听程序已经运行,等待着本地机来连接调试。

假设远程机上服务器程序所在的工作目录为d:\testremote\,该目录下应该有一套服务器程序的可执行文件和DLL文件,要和本地机的开发环境下的对应文件一致,要求版本相同,我用的都是release 版,release版也可以设断点调试的,以为不能的看法是误区。现在,我们要调试的服务器程序都是release 版的,因为调好了debug版也不能保证release版没问题,所以不如一步到位。

第二:

本地机上调试环境的配置。在Microsoft Visual C++的开发环境下点击build?Debugger Remote Connection,如下图:

之后选Network(TCP/IP),点击Settings,将远程机的IP地址填写进去。然后一路点OK。

下面进行有关的工程设置,在Microsoft Visual C++开发环境的主界面下点击Project?Settings

在debug页签下填写本地的服务器程序路径(Executable for debug session)和远程机的服务器程序路径(Remote executable path and file name),如下图:

点击OK按钮后,本地的开发环境就已经处在远程调试的状态,点击Build?Start Debug?Go(快捷键是F5),即开始调试了。可能会弹出对话框,要求我们指出一些对应DLL文件的本地路径,点击Browse按钮选取指明即可。网上有的文章说在远程机上启动服务器程序,应该是不对的,应该是通过本地机的开始调试来激发远程机上的程序。

至此,本地机的开发环境停在了服务器程序的断点处,

远程机的服务器程序控制台已经启动(如下图),现在可以找一些别的计算机运行客户端程序开始连接服务器,我们就可以开始在本地的计算机上调试服务器程序了,而服务器程序和数据库都在远程机上运行着。由于本地机没什么额外负担,所以调试程序的反应速度会很快,也避免了身陷如战斗机引擎轰鸣般的机房。哈哈。

MYSQL数据库大数据量的导入

yqlhero

近日帮用户导入一个数据量非常大的数据,用户使用的数据库类型为MYSQL,表名为stat_unit,大概有80万条数据,保存在一个文本文件(文件名为addr.txt)中,文件中每条数据为一行,字段以逗号分隔。

整个操作过程如下:

1.清除数据库表中原有数据

DELETE FROM stat_unit;

2.从数据文件中导入数据到表stat_unit

LOAD DATA INFILE 'c:\\addr.txt' into table stat_unit fields terminated by ','

LINES TERMINATED BY '\n' (stat_unit_code, stat_unit_name, stat_unit_desc, a, b, c);

说明:'c:\\addr.txt'为数据文件名称,包含路径。

'a, b, c'为新加的字段名称,导入时根据设定替换。

3.查询导入数据

SELECT COUNT(*) FROM stat_unit;

4.根据规则修改item_level

UPDATE stat_unit SET item_level = '1'

WHERE LENGTH(TRIM(TRAILING '0' FROM stat_unit_code))<3;

UPDATE stat_unit SET item_level = '2'

WHERE LENGTH(TRIM(TRAILING '0' FROM stat_unit_code))>2 and LENGTH(TRIM(TRAILING '0' FROM stat_unit_code))<5;

UPDATE stat_unit SET item_level = '3'

WHERE LENGTH(TRIM(TRAILING '0' FROM stat_unit_code))>4 and LENGTH(TRIM(TRAILING '0' FROM stat_unit_code))<7;

UPDATE stat_unit SET item_level = '4'

WHERE LENGTH(TRIM(TRAILING '0' FROM stat_unit_code))>6 and LENGTH(TRIM(TRAILING '0'

FROM stat_unit_code))<10;

UPDATE stat_unit SET item_level = '5'

WHERE LENGTH(TRIM(TRAILING '0' FROM stat_unit_code))>9;

5.查询修改后的数据数量

SELECT item_level, COUNT(*) FROM stat_unit

GROUP BY item_level

6.根据规则修改父代码

UPDATE stat_unit SET PAR_stat_unit_code = '000000000000'

WHERE item_level = '1';

UPDATE stat_unit SET PAR_stat_unit_code CONCAT(SUBSTRING(stat_unit_code,1,2),'0000000000')

= WHERE item_level = '2';

UPDATE stat_unit SET PAR_stat_unit_code = CONCAT(SUBSTRING(stat_unit_code,1,4),'00000000') WHERE item_level = '3';

UPDATE stat_unit SET PAR_stat_unit_code = CONCAT(SUBSTRING(stat_unit_code,1,6),'000000') WHERE item_level = '4';

UPDATE stat_unit SET PAR_stat_unit_code = CONCAT(SUBSTRING(stat_unit_code,1,9),'000')

WHERE item_level = '5';

按照上述步骤执行后,将用户数据完整导入到数据库表中,实现了用户的预期。

Windows平台面向对象定时器的设计

fibbery

1.设想定时器对象

面向对像的编程思想在编程过程中被广泛的应用,并且是软件开发的一个里程碑。

对于定时器,Windows操作系统提供了相关的API函数来实现系统中所需的定时器,但是在编程的今天,我们一般都会将各个功能模块封装成类以备将来模块重用,这样不但使程序的结构看上去条理很清晰,更利于程序的维护和代码重用节省软件开发与维护的成本。

从面向对象的角度来分析,一个定时器对象应该是什么样子呢?一个定时器至少应该具有Start、Stop 方法,来实现定时器的启动和停止;如果我们在创建了定时器对象后,想更改定时器的定时间隔,那么我们还应该有一个设置时间间隔的方法;最重要的,当定时器到时的时候,应该执行的动作,我们假定为Run 方法。对于一个定时器来说,这可能已经足够了。

那么我们下面将依据Windows系统提供的定时器功能设计我们的面向对象的定时器。

2.Windows定时器技术

Windows提供了设置和取消定时器的API函数,本章将介绍这两个函数,以及Windows提供的定时器函数的局限性。

2.1.SetTimer

API函数SetTimer,用于创建一个系统的定时器。其原型如下:

UINT SetTimer(

HWND hWnd,

UINT nIDEvent,

UINT uElapse,

TIMERPROC lpTimerFunc

);

参数hWnd是与定时器id关联的窗口句柄,如果一个定时器每有所关联的窗口句柄,该参数需要传递NULL值,当该参数为NULL时,nIDEvent参数将被忽略掉。

参数nIDEvent是窗口中的一个Timerid,如果hWnd参数值为NULL,该参数将被忽略掉。

参数uElapse是定时器到期的时间,单位为千分秒。

参数lpTimerFunc是定时器到期调用的回调函数。当该参数为NULL时,系统将发送WM_TIMER消息触发该事件的处理函数,可以在该事件处理函数中使用case语句来处理不同的定时器的到期事件(所有的定时器都发送该消息,可以从该消息的参数中辨别当前的消息是哪个定时器触发的);如果该参数不为NULL,则我们需要在执行的线程中分派该消息,系统会自动掉用该回调函数。对于该回调含数的详细说明见下一节。

SetTimer函数的返回0表示创建定时器失败,非0表示定时器创建成功,该数值为定时器的id,可以将定时器id的值传递给KillTimer函数来停止一个定时器。

2.2.Timer事件处理函数

事件处理函数,当时间被触发时需要执行的动作。Timer事件处理函数原型为:

void CALLBACK TimerProc(

HWND hWnd,

UINT uMsg,

UINT idEvent,

DWORD dwTime

);

参数hWnd为定时器所关联的窗口句柄,该参数的值与使用SetTimer创建定时器时传入的hWnd值相同。

参数uMsg值为WM_TIMER

参数idEvent标识该事件由哪一个定时器触发,即定时器id

参数dwTime值为从计算机启动到现在所经历的时间,单位为千分秒,这个值与使用GetTickCount函数所得到的值一样。

该回调函数没有返回值。

2.3.KillTimer

KillTimer API函数用来销毁一个定时器。在销毁指定定时器时系统会检查消息队列,同时清除在消息队列中与该定时器相关的WM_TIMER消息。

该函数的原型:

BOOL KillTimer(

HWND hWnd,

UINT uIDEvent

);

参数hWnd是与要销毁的定时器相关联的窗口句柄,与使用SetTimer创建该定时器时指定的hWnd参数相同。

参数uIDEvent是将要被销毁的定时器id。如果在使用SetTimer函数创建定时器时,hWnd被指定为一个有效的窗口句柄,那么该uIDEvent参数必须与SetTimer的uIDEvent参数一致;如果,使用SetTimer创建定时器时hWnd参数被指定为NULL,那么该uIDEvent参数需使用SetTimer返回值,即定时器id。

当成功销毁定时器时,该API函数返回非0值,否则返回0。

2.4.局限性

从与定时器相关的三个函数来看,Windows定时器似乎都与窗口相关。可以理解,Windows操作系统嘛,但是,在Windows平台,我们也会经常编写一些后台服务程序,或者控制台程序,也可能使用到定时器的功能。从前面结个小节的介绍,我们知道,定时器也可以应用于无窗口的应用开发中,即,在使用SetTimer 函数创建定时器时将hWnd参数指定为NULL。

我们进一步来看定时器到期执行的回调函数,如果创建定时器时没有与之关联的窗口,那么,TimerProc 回调函数将只剩下两个有效的参数:定时器id和计算机启动到执行该回调函数的时间。所以,对于Windows 定时器来说,如果没有与之关联的窗口句柄,TimerProc函数接受数据的能力将非常的差,这样直接导致定时器的功能十分有限。因为,函数的基本功能就是对数据的处理。在后续章节中将给出解决该问题的方法。

作为一个回调函数,一般都被设计为void cbfunc(… , void * userdata);这样回调函数有处理任意数量数据的能力,而定时器的回调函数的设计并不是这样的,从这一点来看,Unix平台的定时器回调函数要比

Windows的好得多了。

3.定时器的设计

3.1.CTimer类的基本功能

从对定时器的设想到C++的面向对象开发方法,我们总结CTimer类应该具有如下的功能:

1、构造函数:可以直接启动定时器

2、析构函数:停止定时器

3、启动定时器:Start函数,用来启动一个定时器

4、停止定时器:Stop函数,用来停止一个定时器

5、定时执行的操作:Run函数,定时执行的指令序列,用于完成特定的任务

6、定时器启动失败:Failed函数,用于判断定时器是否成功启动

3.2.Run函数的定时调用

3.2.1定时器回调函数的应用

我们的目标是设计一个CTimer类,定时执行其成员函数Run。这个功能,我们必须要借助第二章《Windows定时器技术》所描述的定时器回调函数。

由于Windows定时器回调函数不支持从参数中传递额外的用户需要处理的数据,所以我们必须要解决如何向TimerProc传递数据的问题。

回调函数必须是全局函数或者是静态成员函数。如果使用全局函数,那么,我们势必要在程序中声名全局变量或者文件范围内的静态变量,显然,后者要比前者存在优势,这样不影响数据的隐藏,不至于将该变量暴露在程序范围内;如果使用静态成员函数,显然,我们将系统的回调函数暴露在了CTimer类的声名头文件中,这样会影响CTimer的美观(编程是门艺术),然而,更重要的是,作为静态成员函数同样需要使用全局变量或者文件范围内的静态变量来实现额外的用户数据的传递,因为静态成员函数无法直接访问类的成员。

根据上面的讨论,选择全局函数,并且将该全局函数声名为静态函数,这样可以实现该函数在应用范围内的隐藏。

定时器回调函数在CTimer的设计中将扮演着定时调用Run函数的角色。

3.2.2 Map容器的应用

在与定时器回调函数有关的讨论中,我们谈到的额外的用户数据,事实上并非在定时器真正被使用时需要处理的用户数据,因为,现在我们要解决的问题实际上是:如何通过定时器回调函数实现定时对Run 函数的调用问题。所以,我们所要声名的静态变量必须能够保存CTimer的某些信息,以使定时器回调函数可以分辨当前的定时器到期事件是哪一个CTimer实例产生的。

在2.2“Timer事件处理函数”一节中说明了TimerProc回调函数的参数idEvent保存的是当前触发Timer 事件的定时器id,那么我们完全可以借助这个定时器id来区分目前是哪一个CTimer类实例触发了该Timer 事件,以致于TimerProc回调函数被系统调用。

既然我们已经可以知道当前Timer事件定时器id,那么我们只需要通过id能够查找到CTimer即可。所

以,我们采用在文件范围内声名全局的Map的map变量来保存,即,static map container;

使用map的好处之一查找迅速,100万个定时器中,要查找到当前的定时器不超过20次比较,另外,操作方便,也许这个好处是选定map容器的根本原因,因为一个系统内不可能会有那么多的定时器,否则,这个应用的设计肯定有不合理的地方。

3.3.Run函数的多态性设计

通过3.2节,我们已经可以定时调用Run函数了,通过map容器,保存定时器id和CTimer指针的Key-Value对,找到当前的CTimer指针,来调用Run函数。

在实际的应用中,我们调用的不应该是CTimer类的Run函数,应该是用户派生的定时器类,在派生类中用户需要重载Run函数来完成实际需要定时完成的任务。所以,Run函数应该是一个具有多态性的虚函数设计。

3.4Map容器的维护

很显然,当成功创建一个CTimer对象(实际上应该是其派生类对象)的时候,需要将定时器id和对象的地址指针对添加到map容器的变量中,保存新创建的CTimer对象与定时器id的关联关系;当需要释放CTimer对象所占用的资源时,需要将定时器id和对象的指针对在map容器中删除掉,以释放资源。

这样,我们需要在打开定时器(Open)时向容器添加定时器id与CTimer对象的对应关系,在停止定时器(Stop)时从容器中删除定时器id与CTimer对象的对应关系。

4.定时器的实现

4.1.CTimer的定义及部分函数的inline实现

以下内容为timer.h头文件的内容,尤为注意一下,Run函数被声名为虚函数,在CTimer的类中被定义为一个没有任何指令的空函数:

class CTimer

{

protected:

unsigned long mu_timer_id;

md_interval;

double

public:

CTimer():mu_timer_id(0),md_interval(0.0){}

CTimer(double ad_interval,int ai_start=1);

~CTimer();

virtual

int Failed()const{return mu_timer_id==0;}

int Start(double ad_interval);

int

Stop();

virtual void Run(const unsigned long au_time){};

};

4.2.CTimer的实现

以下为timer.cpp文件的内容,具体实现了CTimer类。

4.2.1.头文件

#include "Timer.h"

#include

#include

#include

4.2.2.容器变量的定义

static map sm_timer_container;

4.2.3.定时器回调函数的定义

在timer.cpp文件的前面声名回调函数,一般情况下,我个人的习惯将全局函数的声名与定义分别放在:

static VOID CALLBACK DispatchTimer(

HWND hwnd,

unsigned int au_msg,

unsigned int au_timerid,

unsigned long au_time)

{

//在容器中查找到时的timer

map::const_iterator lv_cit=sm_timer_container.find(au_timerid);

if(lv_cit==sm_timer_container.end())

return;

//执行timer的Run函数

unsigned long lu_time=time(NULL);

if(lv_cit->second)

{

lv_cit->second->Run(lu_time);

}

}

4.2.4.构造函数

CTimer::CTimer(double ad_interval,int ai_start)

{

mu_timer_id=0;

md_interval=ad_interval;

if(ai_start)

Start(md_interval);

}

4.2.5析构函数

CTimer::~CTimer()

{

if(mu_timer_id)

Stop();

}

4.2.6.启动定时器

int CTimer::Start(double ad_interval)

{

//定时器已经启动,再次调用Start函数无效

if(mu_timer_id)

return 1;

//创建定时器,保存定时器id到成员变量mu_timer_id中

mu_timer_id=::SetTimer(NULL,NULL,int(ad_interval*1000),&DispatchTimer);

if(!mu_timer_id)

{

//unsigned long lu_err=GetLastError();

//cout<<"SetTimer Error:"<

return -1;

}

//添加mu_timer_id、定时器对应对到容器中

sm_timer_container[mu_timer_id]=this;

//成功创建定时器

return 0;

}

4.2.7. 停止定时器

int CTimer::Stop()

{

//定时器未启动

if(!mu_timer_id)

return 1;

//停止定时器

if(::KillTimer(NULL,mu_timer_id)==0)

return -1;

//从定时器容器中删除当前定时器id与定时器配对

sm_timer_container.erase(mu_timer_id);

//复位mu_timer_id

mu_timer_id=0;

//成功返回

return 0;

}

5.定时器的使用

https://www.360docs.net/doc/6d15933345.html,ounterTimer

时间计数器,派生自CTimer,详细代码如下:

class CCounterTimer:public CTimer

{

public:

virtual void Run(const unsigned long au_time);

CCounterTimer():CTimer(){}

CCounterTimer(double ad_interval,int ai_start=1):CTimer(ad_interval,ai_start){} };

void CCounterTimer::Run(const unsigned long au_time)

{

static int i=0;

cout<<"Counter Timer is running : "<<++i<

}

该类主要是对给定的时间间隔不断的累积执行次数。

5.2.CAlarmClock

闹钟类,定时提醒当前的时间,代码如下:

class CAlarmClock:public CTimer

{

public:

CAlarmClock():CTimer(){}

CAlarmClock(double ad_interval,int ai_start=1):CTimer(ad_interval,ai_start){}

virtual void Run(const unsigned long au_time);

};

void CAlarmClock::Run(const unsigned long au_time)

{

time_t t=time(NULL);

cout<<"The time now is : "<

}

5.3.消息循环

由于我们是以控制台模式的程序来进行定时器的应用试验,即,我们的程序中没有窗口,所以,我们需要自行编写消息循环代码,具体代码如下:

MSG lv_msg;

bool bRet;

while( (bRet = GetMessage( &lv_msg, NULL, 0, 0 )) != 0)

{

if (bRet == -1)

{

// handle the error and possibly exit

}

else

{

TranslateMessage(&lv_msg);

DispatchMessage(&lv_msg);

}

}

5.4.完整测试代码

#include "Timer.h"

#include

#include

#include

using namespace std;

class CCounterTimer:public CTimer

{

public:

virtual void Run(const unsigned long au_time);

CCounterTimer():CTimer(){}

CCounterTimer(double ad_interval,int ai_start=1):CTimer(ad_interval,ai_start){}

};

void CCounterTimer::Run(const unsigned long au_time)

{

static int i=0;

cout<<"Counter Timer is running : "<<++i<

}

class CAlarmClock:public CTimer

{

public:

CAlarmClock():CTimer(){}

CAlarmClock(double ad_interval,int ai_start=1):CTimer(ad_interval,ai_start){}

virtual void Run(const unsigned long au_time);

};

void CAlarmClock::Run(const unsigned long au_time)

{

time_t t=time(NULL);

cout<<"The time now is : "<

}

int main(int argc, char * argv[])

{

CCounterTimer lv_tmr_counter(1);

CAlarmClock lv_tmr_alm(5);

MSG lv_msg;

bool bRet;

while( (bRet = GetMessage( &lv_msg, NULL, 0, 0 )) != 0)

{

if (bRet == -1)

{

// handle the error and possibly exit

}

else

{

TranslateMessage(&lv_msg);

DispatchMessage(&lv_msg);

}

}

return 0;

}

5.5.编译测试程序

我们将上面的完整代码保存为:test.cpp,使用vc在windows编译,编译命令如下:cl test.cpp timer.cpp /Link user32.lib

执行该编译命令后生成test.exe,执行该测试程序。

相关文档
最新文档