Rise的自留地

记录生活中的点滴,分享编程技术和游戏开发经验。

0%

美国传播学家艾伯特梅拉比安曾提出一个公式:

  信息的全部表达=7%语调+38%声音+55%肢体语言

  我们把声音和肢体语言都作为非语言交往的符号,那么人际交往和销售过程中信息沟通就只有7%是由言语进行的。

定时机制是指在程序运行当中间隔特定的时间引发指定的事件。在DOS下编程时,主要依靠时钟中断Int 8及其调用中断 Int 1cH来实现,应用程序通过修改这些系统中断来达到实现定时触发。而在Windows下,若想象在DOS下肆无忌惮的修改系统是不现实的,那么应当如何实现定时机制呢?下面在下就在学习当中的几点体会谈谈这个问题,提出几种方案供大家参考。

第一种方案是大家熟悉的截获定时消息的途径。在Windows提供给我们使用的系统资源当中,有一种称为“定时器(Timer)”的特殊资源,在申请了这类资源的程序当中每间隔一段时间会接收到值为WM_TIMER的消息。需要定时执行的代码可以放在该消息的处理部分。如果在VC中,我们可以具体按照以下步骤实现这一目的:

  1. 利用MFC AppWizard创建一个标准的工程,接受所有缺省选项。名为s1
  2. 在Classview中选中“CMainFrame”类,然后按Ctrl+W激活ClassWizard,在“Message Map”选项卡中Class Name选“CMainFrame”,接着在“Message”中选“WM_TIMER”,最后按下“Add Funcation”。以上步骤加入了对WM_TIMER消息的映射处理。
  3. 回到Classview中,双击“OnCreate”成员函数,在函数的末尾添加申请Timer的语句:
    SetTimer(100,1000,NULL);//申请一个标识值为100的Timer,定时间隔为1000毫秒(1秒)。
  4. 在“Classview”中双击OnTimer函数,输入要定时实现的代码。本例子中为:
    MessageBeep(1000);;//每隔一秒发出通告声
  5. 编译并执行之,我们可以每隔一秒就听到声音。这正是我们在OnTimer函数内要求执行的。

实际当中,我们可以将“MessageBeep(1000);”换成任何我们想完成的任务,譬如定时存盘等。

第二种方案也利用Timer资源,但却是采用已经编写好的代码��我们可以加入一个具有定时功能的组件至当前工程当中。这种方法特别适用于基于对话框的工程。具体步骤如下:

  1. 利用MFC AppWizard创建一个基于对话框的工程,其余接受所有缺省选项。名为s2。
  2. 在ResourceView中,双击IDD_S2_DIALOG,显示对话框,将其中的“To do:”改为“定时触发演示的例子”,表明工程的作用。
  3. 右击对话框编辑区,在弹出的右键菜单中选择“Insert ActiveX Control”,从弹出的列表框中选择“Timer Object”,确定后会在对话框内出现一个Timer对象。
  4. 我们右击Timer对象,从弹出的菜单中选择“Properties”,接着选“All”选项卡,将其中的Interval值设为5000,即每隔5秒发生一次Timer事件。
  5. 回到对话框编辑界面,双击Timer,产生一个CS2Dlg::OnTimerTimer1成员函数,接受缺省值,并在函数实现部分输入:
    MessageBox("定时触发消息框","定时演示" ,MB_OK);
  6. 编译并运行此工程,将会在产生的对话框运行期间,每隔5秒弹出一个消息框。

同样,我们可以以任何自己的代码来替换5中的消息框语句。详细见附例s2。

第三种方法是采用线程技术。众所周知,Windows 9X是一个基于多线程的多任务操作系统,在内核中以线程作为调度的基本单位,由系统分时间片进行调度。利用这一点,我们可以在程序当中创建一个“司职”计时的线程,通过线程间的同步来定时触发我们要完成的任务的代码。不象前两种方法需要至少有一个窗口作为接受消息的主窗口,采用线程技术实现定时触发将免去创建窗口的麻烦以及带来的系统各种资源的消耗。下面我们来举一个例子来说明这个问题:我们在CmyApp类的Initstance成员中不建立主窗口而是创建一个工作线程,该线程休眠一定的时间后,自动调用主线程的SomeThing函数。为了支持线程的运行,我们需要给CmyApp类增加相应的线程函数。下面,我们还是一步一步的实现:

  1. 利用MFC AppWizard创建一个标准工程,其中为不产生多余的代码,不选文档/视图支持,并选择单文档。工程名为S3。
  2. 在CS3App:: InitInstance()中用“/* … */”注释掉“return TRUE;”之前的所有代码。这是为了不建立窗口。并添加以下代码:
    ExitFlag=TRUE;//是否结束主线程的循环的标志变量。因为子线程严重依赖主线程,所以在本例子中为了避免没有主窗口而提前结束应用程序,从而使子线程无法存在,所以给主线程一个循环,知道全局变量ExitFlag在子线程退出前被设置成FALSE为止.
    StartThread();//启动线程
    do{}while(ExitFlag);//直到结束子线程
    ::MessageBox(NULL,"主线程结束!","定时触发演示",MB_OK);
    return TRUE;
  3. 在Globals中增加一标志变量“ExitFlag”,类型为BOOL。它被主线程用来判断是否结束自身运行。
  4. 通过ClassView在CS3App的Public部分声明以下函数:
    void StartThread(void); //启动线程
    static UINT ThreadFunction(void); //主要执行代码的函数
    static UINT StaticThreadFunc(LPVOID lpparam);//设置线程时用到的函数
    需要特别指出的是,用AfxBeginThread进行线程设置时,第一参数必须象本例所指出的那样声明为Static ,不然参数转换的错误会扰得你不得安宁。
  5. 在StartThread中输入如下代码:
    AfxBeginThread(StaticThreadFunc,this);//建立并启动线程
  6. 在StaticThreadFunc中输入如下代码:
    return ThreadFunc();//调用完成主要线程代码的函数,注意一定要是Static.
  7. 实现ThreadFunction:
    int i;
    i=5;//触发5次
    while(i--)
    {
    Sleep(5000);//间隔5秒
    ::MessageBox (NULL,"我被定时触发了!","定时触发演示",MB_OK);
    }
    ExitFlag=FALSE;//ExitFlag是一全局变量,通知主线程结束运行。
    return 0;
    }
  8. 编译并运行工程,将看不到应用程序窗口,但可以看到每隔5秒,桌面上出现一个消息框,5次后弹出主线程结束的消息框。

以上即本人在学习当中解决 Windows下实现定时触发而采取的一些办法,各自方法的特点也在介绍当中指出。希望所述能给大家一点帮助,更希望能得到大家的指正。如果您有什么意见和设想,欢迎发E-Mail给我(yangshanhe@21cn.com)。

==

很早之前2000年的拙作,集在一起,免得自己都不清楚干过什么。

将STLPort解压出来。为STLPort注册环境变量。

在VC安装目录里搜索“vcvars32.bat”文件出来。找到INCLUDE这个键,将$(STLPort)\stlport注册进去。然后在机器中的环境变量中注册INCLUDE这个变量,将此目录也注册好。

开始编译,进入$(STLPort)\src目录。
copy vc71.mak makefile
这一步就是将一个vc71版本的mak做为makefile文件。
然后使用nmake开始编译。
也可以使用这个来做
nmake -f vc71.mak

这里我做了一个例子来来说明使用这个STLPort..

#include <stl/_config.h>
#include <stl/_vector.h>
#include <iostream>

using namespace _STLP_STD;
using namespace std;
void main()
{
vector<int> arrInt;
for (int i=0;i<100;i++)
arrInt.push_back(i);
for (i=0;i<100;i++)
  cout<<arrInt[i]<<endl;
return ;
}

前言: 
错误处理和socket释放, 是IOCP编程中的一大难点. 本文试图就IOCP设计中经常遇到的这个难题展开论述并寻找其解决方案, 事实上, 文中所述的解决方式不仅仅适用于IOCP, 它同样适用于EPOLL等多种服务器编程的网络模型中, 前提是: 领会这种处理方式的实质.
正文:
在使用IOCP开发时, 大家经常遇到的一个难题是与socket相关的缓冲区释放不当带来的错误, 这种错误通常是由于多次对同一个指针执行了delete操作引起的. 比如, 当在执行wsasend或wsarecv返回了非pending的错误信息时, 我们就要对此错误进行处理, 通常情况下, 我们会想到执行这两步操作:
a. 释放此次操作使用的缓冲区数据(如果不释放可能造成内存泄漏);
b. 关闭当前操作所使用的socket.
而另一方面, 我们可能也会在get函数(GetQueuedCompletionStatus)的处理中, 当get函数返回值为FALSE时也作这两步相同的操作.  此时, 就会造成对同一缓冲区的重复释放, 问题由此产生.
解决的方法, 可以有这几种:
1. 对数据缓冲区使用引用计数机制;
2. 在clientsock的对象设计机制上使释放操作线性化.
关于这两种方法, 任何一种如果要详细说清, 可能篇幅都会比较长, 笔者并无耐心和精力将每一个细节都一一道来, 在此仅选第2种方案的关键步骤和核心思想来与大家分享.
由前面对问题的描述可以看出, 造成多次释放的原因可能是在执行收发操作和GET函数返回值为FALSE时, 我们重复执行了释放操作. 很自然地, 我们会想到,  能不能把这两次释放合并成一次释放,  这样不就没问题了吗?  yes,  这个思路是没问题的.  但要想让这个思路能变成现实,  需要在设计机制上对这个思路进行一定的支持.
首先,  我们假设, 是在get函数返回时统一进行相应的释放和关闭操作.
如果在执行wsasend操作时, 发生了非pending错误(io操作正在进行中), 而此时我们如果不释放资源, 那至少得让IOCP在GET返回时得知这个错误和发生错误时的缓冲区指针. 通知IOCP的方式, 是使用post函数(PostQueuedCompletionStatus)向IOCP抛一个特殊标志的消息, 这个特殊标志可以通过get函数的第二个参数, 即: 传送字节数来表示, 可以选择任何一个不可能出现的值, 比如任何一个跟它的初始值不相等的负数.  当然, 如果你通过单句柄数据或单IO数据来传递也是可以的. 而发生错误的这个缓冲区指针, 我们是必须要通过单句柄数据或单IO数据来传递的. 但是, 从整个缓冲区的管理机制上来说, 我不推荐这样的离散缓冲区机制, 我的建议是: 把收发缓冲区或数据队列与相应的clientsocket对象相绑定, 释放操作写在该对象的析构函数里, 这样当释放clientsocket对象时就释放了这些缓冲区.
ok, 这样一来, 在get函数里, 有三种情况需要执行释放逻辑:
1. get的返回值为FALSE;
2. 传送字节数为0;
3. 接收到刚才我们post的那个错误类型消息.
把释放操作全放在get函数里以后, 对释放操作的处理, 就比较统一了. 当然, 为了实现真正的线性化和元子化, 在释放操作的最终执行逻辑上, 还需要对释放代码加锁以实现线程互斥(当然, 这是在你开了多个工作者线程的情况下).

在WinSock上使用IOCP
本文章假设你已经理解WindowsNT的I/O模型以及I/O完成端口(IOCP),并且比较熟悉将要用到的API,如果你打算学习IOCP,请参考Jeffery Richter的Advanced Windows(第三版),第15章I/O设备,里面有极好的关于完成端口的讨论以及对即将使用API的说明。
IOCP提供了一个用于开发高效率和易扩展程序的模型。Winsock2提供了对IOCP的支持,并在WindowsNT平台得到了完整的实现。然而IOCP是所有WindowsNT I/O模型中最难理解和实现的,为了帮助你使用IOCP设计一个更好的Socket服务,本文提供了一些诀窍。
Tip 1:使用Winsock2 IOCP函数例如WSASend和WSARecv,如同Win32文件I/O函数,例如WriteFile和ReadFile。
微软提供的Socket句柄是一个可安装文件系统(IFS)句柄,因此你可以使用Win32的文件I/O函数调用这个句柄,然而,将Socket句柄和文件系统联系起来,你不得不陷入很多的Kernal/User模式转换的问题中,例如线程的上下文转换,花费的代价还包括参数的重新排列导致的性能降低。
因此你应该使用只被Winsock2中IOCP允许的函数来使用IOCP。在ReadFile和WriteFile中会发生的额外的参数重整以及模式转换只会发生在一种情况下,那就是如果句柄的提供者并没有将自己的WSAPROTOCOL_INFO结构中的DwServiceFlags1设置为XP1_IFS_HANDLES。
注解:即使使用WSASend和WSARecv,这些提供者仍然具有不可避免的额外的模式转换,当然ReadFile和WriteFile需要更多的转换。
TIP 2: 确定并发工作线程数量和产生的工作线程总量。
并发工作线程的数量和工作线程的数量并不是同一概念。你可以决定IOCP使用最多2个的并发线程以及包括10个工作线程的线程池。工作线程池拥有的线程多于或者等于并发线程的数量时,工作线程处理队列中一个封包的时候可以调用win32的Wait函数,这样可以无延迟的处理队列中另外的封包。
如果队列中有正在等待被处理的封包,系统将会唤醒一个工作线程处理他,最后,第一个线程确认正在休眠并且可以被再次调用,此时,可调用线程数量会多于IOCP允许的并发线程数量(例如,NumberOFConcurrentThreads)。然而,当下一个线程调用GetQueueCompletionStatus并且进入等待状态,系统不会唤醒他。一般来说,系统会试图保持你设定的并发工作线程数量。
一般来讲,每拥有一个CPU,在IOCP中你可以使用一个并发工作线程,要做到这点,当你第一次初始化IOCP的时候,可以在调用CreateIOCompletionPort的时候将NumberOfConcurrentThreads设置为0。
TIP 3:将一个提交的I/O操作和完成封包的出列联系起来。
当对一个封包进行出列,可以调用GetQueuedCompletionStatus返回一个完成Key和一个复合的结构体给I/O。你可以分别的使用这两个结构体来返回一个句柄和一个I/O操作信息,当你将IOCP提供的句柄信息注册给Socket,那么你可以将注册的Socket句柄当做一个完成Key来使用。为每一个I/O的"extend"操作提供一个包含你的应用程序IO状态信息的复合结构体。当然,必须确定你为每个的I/O提供的是唯一的复合结构体。当I/O完成的时候,会返回一个指向结构体的指针。
TIP 4:I/O完成封包队列的行为
IOCP中完成封包队列的等待次序并不决定于Winsock2 I/O调用产生的顺序。如果一个Winsock2的I/O调用返回了SUCCESS或者IO_PENDING,那么他保证当I/O操作完成后,完成封包会进入IOCP的等待队列,而不管Socket句柄是否已经关闭。如果你关闭了socket句柄,那么将来调用WSASend,WSASendTo,WSARecv和WSARecvFrom会失败并返回一个不同于SUCCES或者IO_PENDING的代码,这时将不会产生一个完成封包。而在这种情况下,前一次使用GetQueuedCompletionStatus提交的I/O操作所得到的完成封包,会显示一个失败的信息。
如果你删除了IOCP本身,那么不会有任何I/O请求发送给IOCP,因为IOCP的句柄已经不可用,尽管系统底层的IOCP核心结构并不会在所有已提交I/O请求完成之前被移除。
TIP5:IOCP的清除
很重要的一件事是使用复合I/O时候的IOCP清除:如果一个I/O操作尚未完成,那么千万不要释放该操作创建的复合结构体。HasOverlappedIoCompleted函数可以帮助你检查一个I/O操作是否已经完成。
关闭服务一般有两种情况,第一种你并不关心尚未结束的I/O操作的完成状态,你只希望尽可能快的关闭他。第二种,你打算关闭服务,但是你需要获知未结束I/O操作的完成状态。
第一种情况你可以调用PostQueueCompletionStatus(N次,N等于你的工作线程数量)来提交一个特殊的完成封包,他通知所有的工作线程立即退出,关闭所有socket句柄和他们关联的复合结构体,然后关闭完成端口(IOCP)。在关闭复合结构体之前使用HasOverlappedIOCompleted检查他的完成状态。如果一个socket关闭了,所有基于他的未结束的I/O操作会很快的完成。
在第二种情况,你可以延迟工作线程的退出来保证所有的完成封包可以被适当的出列。你可以首先关闭所有的socket句柄和IOCP。可是,你需要维护一个未完成I/O的数字,以便你的线程可以知道可以安全退出的时间。尽管当队列中有很多完成封包在等待的时候,活动的工作线程不能立即退出,但是在IOCP服务中使用全局I/O计数器并且使用临界区保护他的代价并不会象你想象的那样昂贵。
INFO: Design Issues When Using IOCP in a Winsock Server
适用于
This article was previously published under Q192800
SUMMARY
This article assumes you already understand the I/O model of the Windows NT I/O Completion Port (IOCP) and are familiar with the related APIs. If you want to learn IOCP, please see Advanced Windows (3rd edition) by Jeffery Richter, chapter 15 Device I/O for an excellent discussion on IOCP implementation and the APIs you need to use it.
An IOCP provides a model for developing very high performance and very scalable server programs. Direct IOCP support was added to Winsock2 and is fully implemented on the Windows NT platform. However, IOCP is the hardest to understand and implement among all Windows NT I/O models. To help you design a better socket server using IOCP, a number of tips are provided in this article.
MORE INFORMATION
TIP 1: Use Winsock2 IOCP-capable functions, such as WSASend and WSARecv, over Win32 file I/O functions, such as WriteFile and ReadFile.
Socket handles from Microsoft-based protocol providers are IFS handles so you can use Win32 file I/O calls with the handle. However, the interactions between the provider and file system involve many kernel/user mode transition, thread context switches, and parameter marshals that result in a significant performance penalty. You should use only Winsock2 IOCP- capable functions with IOCP.
The additional parameter marshals and mode transitions in ReadFile and WriteFile only occur if the provider does not have XP1_IFS_HANDLES bit set in dwServiceFlags1 of its WSAPROTOCOL_INFO structure.
NOTE: These providers have an unavoidable additional mode transition, even in the case of WSASend and WSARecv, although ReadFile and WriteFile will have more of them.
TIP 2: Choose the number of the concurrent worker threads allowed and the total number of the worker threads to spawn.
The number of worker threads and the number of concurrent threads that the IOCP uses are not the same thing. You can decide to have a maximum of 2 concurrent threads used by the IOCP and a pool of 10 worker threads. You have a pool of worker threads greater than or equal to the number of concurrent threads used by the IOCP so that a worker thread handling a dequeued completion packet can call one of the Win32 "wait" functions without delaying the handling of other queued I/O packets.
If there are completion packets waiting to be dequeued, the system will wake up another worker thread. Eventually, the first thread satisfies it's Wait and it can be run again. When this happens, the number of the threads that can be run is higher than the concurrency allowed on the IOCP (for example, NumberOfConcurrentThreads). However, when next worker thread calls GetQueueCompletionStatus and enters wait status, the system does not wake it up. In other words, the system tries to keep your requested number of concurrent worker threads.
Typically, you only need one concurrent worker thread per CPU for IOCP. To do this, enter 0 for NumberOfConcurrentThreads in the CreateIoCompletionPort call when you first create the IOCP.
TIP 3: Associate a posted I/O operation with a dequeued completion packet.
GetQueuedCompletionStatus returns a completion key and an overlapped structure for the I/O when dequeuing a completion packet. You should use these two structures to return per handle and per I/O operation information, respectively. You can use your socket handle as the completion key when you register the socket with the IOCP to provide per handle information. To provide per I/O operation "extend" the overlapped structure to contain your application-specific I/O-state information. Also, make sure you provide a unique overlapped structure for each overlapped I/O. When an I/O completes, the same pointer to the overlapped I/O structure is returned.
TIP 4: I/O completion packet queuing behavior.
The order in which I/O completion packets are queued in the IOCP is not necessarily the same order the Winsock2 I/O calls were made. Additionally, if a Winsock2 I/O call returns SUCCESS or IO_PENDING, it is guaranteed that a completion packet will be queued to the IOCP when the I/O completes, regardless of whether the socket handle is closed. After you close a socket handle, future calls to WSASend, WSASendTo, WSARecv, or WSARecvFrom will fail with a return code other than SUCCESS or IO_PENDING, which will not generate a completion packet. The status of the completion packet retrieved by GetQueuedCompletionStatus for I/O previously posted could indicate a failure in this case.
If you delete the IOCP itself, no more I/O can be posted to the IOCP because the IOCP handle itself is invalid. However, the system's underlying IOCP kernel structures do not go away until all successfully posted I/Os are completed.
TIP 5: IOCP cleanup.
The most important thing to remember when performing ICOP cleanup is the same when using overlapped I/O: do not free an overlapped structure if the I/O for it has not yet completed. The HasOverlappedIoCompleted macro allows you to detect if an I/O has completed from its overlapped structure.
There are typically two scenarios for shutting down a server. In the first scenario, you do not care about the completion status of outstanding I/Os and you just want to shut down as fast as you can. In the second scenario, you want to shut down the server, but you do need to know the completion status of each outstanding I/O.
In the first scenario, you can call PostQueueCompletionStatus (N times, where N is the number of worker threads) to post a special completion packet that informs the worker thread to exit immediately, close all socket handles and their associated overlapped structures, and then close the completion port. Again, make sure you use HasOverlappedIoCompleted to check the completion status of an overlapped structure before you free it. If a socket is closed, all outstanding I/O on the socket eventually complete quickly.
In the second scenario, you can delay exiting worker threads so that all completion packets can be properly dequeued. You can start by closing all socket handles and the IOCP. However, you need to maintain a count of the number of outstanding I/Os so that your worker thread can know when it is safe to exit the thread. The performance penalty of having a global I/O counter protected with a critical section for an IOCP server is not as bad as might be expected because the active worker thread does not switch out if there are more completion packets waiting in the queue.

当然TCP方式的模型还有事件选择模型。
就是把所有的网络事件和我们的一个程序里定义的事件梆定。
这个有它的好处,可能可以让我们更好的写一个线程来管理
接收与发送。
现在来讲一下一个完成端口模型。

TinyXML 指南

 

这是什么?

这份指南有一些关于如何有效地使用TinyXML的技巧和建议。

我也会尝试讲一些诸如怎样使字符串与整型数相互转化的C++技巧。这与TinyXML本身没什么关系,但它也许会对你的项目有所帮助,所以我还是把它加进来了。