﻿<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:trackback="http://madskills.com/public/xml/rss/module/trackback/" xmlns:wfw="http://wellformedweb.org/CommentAPI/" xmlns:slash="http://purl.org/rss/1.0/modules/slash/"><channel><title>C++博客-奇奇的空间-文章分类-VC++</title><link>http://www.cppblog.com/jjbird/category/704.html</link><description>奇奇的空间</description><language>zh-cn</language><lastBuildDate>Tue, 20 May 2008 08:00:39 GMT</lastBuildDate><pubDate>Tue, 20 May 2008 08:00:39 GMT</pubDate><ttl>60</ttl><item><title>多屏编程之如何获取显卡信息判断是否为多屏</title><link>http://www.cppblog.com/jjbird/articles/15701.html</link><dc:creator>奇奇</dc:creator><author>奇奇</author><pubDate>Mon, 27 Nov 2006 08:18:00 GMT</pubDate><guid>http://www.cppblog.com/jjbird/articles/15701.html</guid><wfw:comment>http://www.cppblog.com/jjbird/comments/15701.html</wfw:comment><comments>http://www.cppblog.com/jjbird/articles/15701.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/jjbird/comments/commentRss/15701.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/jjbird/services/trackbacks/15701.html</trackback:ping><description><![CDATA[
		<table cellspacing="0" cellpadding="0" width="98%" align="center" border="0">
				<tbody>
						<tr>
								<td height="20">
								</td>
						</tr>
						<tr>
								<td height="5">
								</td>
						</tr>
						<tr>
								<td align="right" height="20">
										<font color="#cccccc">
										</font>
								</td>
						</tr>
						<tr>
								<td class="reply2">
										<br />在stdafx.h<br />中加入：<br />#undef WINVER <br />#define WINVER 0x0500<br /><br />*************************************************************<br /><br />#ifndef SM_CMONITORS<br /><br />typedef HANDLE HMONITOR;<br /><br />#endif<br />#ifndef DISPLAY_DEVICE_PRIMARY_DEVICE<br /><br />typedef struct _DISPLAY_DEVICE {<br />DWORD cb;<br />TCHAR DeviceName[32];<br />TCHAR DeviceString[128];<br />DWORD StateFlags;<br />} DISPLAY_DEVICE, *PDISPLAY_DEVICE, *LPDISPLAY_DEVICE;<br />#define DISPLAY_DEVICE_ATTACHED_TO_DESKTOP 0x00000001<br />#define DISPLAY_DEVICE_MULTI_DRIVER 0x00000002<br />#define DISPLAY_DEVICE_PRIMARY_DEVICE 0x00000004<br />#define DISPLAY_DEVICE_VGA 0x00000010<br /><br />#endif<br /><br />typedef BOOL (WINAPI* pEnumDisplayDevices)(PVOID,DWORD,PVOID,DWORD);<br />typedef BOOL (WINAPI* pEnumDisplaySettings)(PVOID,DWORD,PVOID);<br /><br />pEnumDisplayDevices pStartEnumDisplayDevices;<br />pEnumDisplaySettings pStartEnumDisplaySettings;<br /><br /><br />*********************************************************************<br /><br /><br />int iCount;<br /><br />pStartEnumDisplayDevices = (pEnumDisplayDevices)GetProcAddress(LoadLibrary("USER32"), "EnumDisplayDevicesA");<br />pStartEnumDisplaySettings = (pEnumDisplaySettings)GetProcAddress(LoadLibrary("USER32"), "EnumDisplaySettingsA");<br /><br /><br />if (pStartEnumDisplayDevices &amp;&amp; pStartEnumDisplaySettings)<br />{<br />DISPLAY_DEVICE dd;<br />DEVMODE dv;<br /><br />ZeroMemory(&amp;dv, sizeof(dv));<br />ZeroMemory(&amp;dd, sizeof(dd));<br /><br />dv.dmSize = sizeof(dv);<br />dd.cb = sizeof(dd);<br /><br />for (iCount=0; (*pStartEnumDisplayDevices)(NULL, iCount, &amp;dd, 0); iCount++)<br />{<br />(*pStartEnumDisplaySettings)(dd.DeviceName,ENUM_CURRENT_SETTINGS,&amp;dv);<br />if((dd.StateFlags &amp; DISPLAY_DEVICE_ATTACHED_TO_DESKTOP) <br />&amp;&amp;(dd.StateFlags &amp; DISPLAY_DEVICE_PRIMARY_DEVICE))<br />{<br />CString strDispName,strScreen,strColor,strFreq;<br /><br />strDispName.Format(_T("Device Name: %s "),dd.DeviceString);<br />SetDlgItemText(IDC_STATIC_DISP_NAME,strDispName);<br /><br />strScreen.Format(_T("%d×%d "),dv.dmPelsWidth,dv.dmPelsHeight);<br />SetDlgItemText(IDC_STATIC_FREQ,strScreen);<br /><br />strColor.Format(_T("%d Bit"),dv.dmBitsPerPel);<br />SetDlgItemText(IDC_STATIC_COLOR,strColor);<br /><br />strFreq.Format(_T("%d Hz"),dv.dmDisplayFrequency);<br />SetDlgItemText(IDC_STATIC_REFRESH_FREQ,strFreq);<br />}<br />}<br />}<br /></td>
						</tr>
				</tbody>
		</table>
<img src ="http://www.cppblog.com/jjbird/aggbug/15701.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/jjbird/" target="_blank">奇奇</a> 2006-11-27 16:18 <a href="http://www.cppblog.com/jjbird/articles/15701.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>iocore 连续运行21天后出错问题的分析</title><link>http://www.cppblog.com/jjbird/articles/15292.html</link><dc:creator>奇奇</dc:creator><author>奇奇</author><pubDate>Fri, 17 Nov 2006 05:08:00 GMT</pubDate><guid>http://www.cppblog.com/jjbird/articles/15292.html</guid><wfw:comment>http://www.cppblog.com/jjbird/comments/15292.html</wfw:comment><comments>http://www.cppblog.com/jjbird/articles/15292.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/jjbird/comments/commentRss/15292.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/jjbird/services/trackbacks/15292.html</trackback:ping><description><![CDATA[
		<p class="MsoNormal" style="MARGIN: 0cm 0cm 0pt">
				<span lang="EN-US">iocore </span>
				<span style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">连续运行</span>
				<span lang="EN-US">21</span>
				<span style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">天后程序出错，地址为</span>
				<span lang="EN-US">0X10212AD0</span>
				<span style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">，报错为</span>
				<span lang="EN-US">“unknown  software  exception  (0X80000003),</span>
				<span style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">位置为</span>
				<span lang="EN-US">0X10212AD0”</span>
				<span style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">，经过分析为</span>
				<span lang="EN-US">new</span>
				<span style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">调用时出错，</span>
		</p>
		<p class="MsoNormal" style="MARGIN: 0cm 0cm 0pt">
				<span lang="EN-US">
						<?xml:namespace prefix = v ns = "urn:schemas-microsoft-com:vml" /?>
						<v:shapetype id="_x0000_t75" stroked="f" filled="f" path="m@4@5l@4@11@9@11@9@5xe" o:preferrelative="t" o:spt="75" coordsize="21600,21600">
								<v:stroke joinstyle="miter">
								</v:stroke>
								<v:formulas>
										<v:f eqn="if lineDrawn pixelLineWidth 0">
										</v:f>
										<v:f eqn="sum @0 1 0">
										</v:f>
										<v:f eqn="sum 0 0 @1">
										</v:f>
										<v:f eqn="prod @2 1 2">
										</v:f>
										<v:f eqn="prod @3 21600 pixelWidth">
										</v:f>
										<v:f eqn="prod @3 21600 pixelHeight">
										</v:f>
										<v:f eqn="sum @0 0 1">
										</v:f>
										<v:f eqn="prod @6 1 2">
										</v:f>
										<v:f eqn="prod @7 21600 pixelWidth">
										</v:f>
										<v:f eqn="sum @8 21600 0">
										</v:f>
										<v:f eqn="prod @7 21600 pixelHeight">
										</v:f>
										<v:f eqn="sum @10 21600 0">
										</v:f>
								</v:formulas>
								<v:path o:connecttype="rect" gradientshapeok="t" o:extrusionok="f">
								</v:path>
								<?xml:namespace prefix = o ns = "urn:schemas-microsoft-com:office:office" /?>
								<o:lock aspectratio="t" v:ext="edit">
								</o:lock>
						</v:shapetype>
						<v:shape id="_x0000_i1025" style="WIDTH: 303pt; HEIGHT: 105pt" o:ole="" type="#_x0000_t75">
								<v:imagedata o:title="" src="file:///C:/DOCUME~1/ADMINI~1/LOCALS~1/Temp/msoclip1/01/clip_image001.png">
								</v:imagedata>
						</v:shape>
				</span>
		</p>
		<p class="MsoNormal" style="MARGIN: 0cm 0cm 0pt">
				<span style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">跟踪程序到</span>
				<span lang="EN-US">_heap_alloc_dbg</span>
		</p>
		<p class="MsoNormal" style="MARGIN: 0cm 0cm 0pt">
				<span lang="EN-US" style="COLOR: #115e94; FONT-FAMILY: Tahoma">void * __cdecl _heap_alloc_dbg(<br />        size_t nSize,<br />        int nBlockUse,<br />        const char * szFileName,<br />        int nLine<br />        )<br />{<br />        long lRequest;<br />        size_t blockSize;<br />        int fIgnore = FALSE;<br />        _CrtMemBlockHeader * pHead;<br /><br />        /* verify heap before allocation */<br />        if (_crtDbgFlag &amp; _CRTDBG_CHECK_ALWAYS_DF)<br />            _ASSERTE(_CrtCheckMemory());<br /><br />        lRequest = _lRequestCurr;<br /><br />        /* break into debugger at specific memory allocation */<br />        if (lRequest == _crtBreakAlloc)<br />            _CrtDbgBreak();<br />// here is the place were the app stops<br /><br />// ... function continuous<br style="mso-special-character: line-break" /><br style="mso-special-character: line-break" /><o:p></o:p></span>
		</p>
		<p class="MsoNormal" style="MARGIN: 0cm 0cm 0pt">
				<span lang="EN-US" style="COLOR: #115e94; FONT-FAMILY: Tahoma"> <o:p></o:p></span>
		</p>
		<p class="MsoNormal" style="MARGIN: 0cm 0cm 0pt">
				<span lang="EN-US" style="FONT-FAMILY: Tahoma">_crtBreakAlloc </span>
				<span style="FONT-FAMILY: 宋体; mso-ascii-font-family: Tahoma; mso-hansi-font-family: Tahoma; mso-bidi-font-family: Tahoma">为</span>
				<span lang="EN-US" style="FONT-FAMILY: Tahoma">-1</span>
				<span style="FONT-FAMILY: 宋体; mso-ascii-font-family: Tahoma; mso-hansi-font-family: Tahoma; mso-bidi-font-family: Tahoma">。</span>
				<span lang="EN-US" style="FONT-FAMILY: Tahoma">lRequest</span>
				<span style="FONT-FAMILY: 宋体; mso-ascii-font-family: Tahoma; mso-hansi-font-family: Tahoma; mso-bidi-font-family: Tahoma">为</span>
				<span lang="EN-US" style="FONT-FAMILY: Tahoma">long</span>
				<span style="FONT-FAMILY: 宋体; mso-ascii-font-family: Tahoma; mso-hansi-font-family: Tahoma; mso-bidi-font-family: Tahoma">型，每</span>
				<span lang="EN-US" style="FONT-FAMILY: Tahoma">new</span>
				<span style="FONT-FAMILY: 宋体; mso-ascii-font-family: Tahoma; mso-hansi-font-family: Tahoma; mso-bidi-font-family: Tahoma">一次，</span>
				<span lang="EN-US" style="FONT-FAMILY: Tahoma">_lRequestCurr</span>
				<span style="FONT-FAMILY: 宋体; mso-ascii-font-family: Tahoma; mso-hansi-font-family: Tahoma; mso-bidi-font-family: Tahoma">每调用一次</span>
				<span lang="EN-US" style="FONT-FAMILY: Tahoma">new</span>
				<span style="FONT-FAMILY: 宋体; mso-ascii-font-family: Tahoma; mso-hansi-font-family: Tahoma; mso-bidi-font-family: Tahoma">，自动加</span>
				<span lang="EN-US" style="FONT-FAMILY: Tahoma">1</span>
				<span style="FONT-FAMILY: 宋体; mso-ascii-font-family: Tahoma; mso-hansi-font-family: Tahoma; mso-bidi-font-family: Tahoma">，当</span>
				<span lang="EN-US" style="FONT-FAMILY: Tahoma">2147483647</span>
				<span style="FONT-FAMILY: 宋体; mso-ascii-font-family: Tahoma; mso-hansi-font-family: Tahoma; mso-bidi-font-family: Tahoma">再加</span>
				<span lang="EN-US" style="FONT-FAMILY: Tahoma">1</span>
				<span style="FONT-FAMILY: 宋体; mso-ascii-font-family: Tahoma; mso-hansi-font-family: Tahoma; mso-bidi-font-family: Tahoma">变成</span>
				<span lang="EN-US" style="FONT-FAMILY: Tahoma">-2147483648</span>
				<span style="FONT-FAMILY: 宋体; mso-ascii-font-family: Tahoma; mso-hansi-font-family: Tahoma; mso-bidi-font-family: Tahoma">，一直累加到</span>
				<span lang="EN-US" style="FONT-FAMILY: Tahoma">-1</span>
				<span style="FONT-FAMILY: 宋体; mso-ascii-font-family: Tahoma; mso-hansi-font-family: Tahoma; mso-bidi-font-family: Tahoma">，然后调用</span>
				<span lang="EN-US" style="FONT-FAMILY: Tahoma">_CrtDbgBreak()</span>
				<span style="FONT-FAMILY: 宋体; mso-ascii-font-family: Tahoma; mso-hansi-font-family: Tahoma; mso-bidi-font-family: Tahoma">，出错。看似是这个原因，为了进一步确认，编写测试程序</span>
				<span lang="EN-US" style="FONT-FAMILY: Tahoma">
						<o:p>
						</o:p>
				</span>
		</p>
		<p class="MsoNormal" style="MARGIN: 0cm 0cm 0pt">
				<span lang="EN-US" style="FONT-FAMILY: Tahoma">class AA<o:p></o:p></span>
		</p>
		<p class="MsoNormal" style="MARGIN: 0cm 0cm 0pt">
				<span lang="EN-US" style="FONT-FAMILY: Tahoma">{<o:p></o:p></span>
		</p>
		<p class="MsoNormal" style="MARGIN: 0cm 0cm 0pt">
				<span lang="EN-US" style="FONT-FAMILY: Tahoma">
						<span style="mso-tab-count: 1">       </span>char aa1[10];<o:p></o:p></span>
		</p>
		<p class="MsoNormal" style="MARGIN: 0cm 0cm 0pt">
				<span lang="EN-US" style="FONT-FAMILY: Tahoma">
						<span style="mso-tab-count: 1">       </span>char aa2[12];<o:p></o:p></span>
		</p>
		<p class="MsoNormal" style="MARGIN: 0cm 0cm 0pt">
				<span lang="EN-US" style="FONT-FAMILY: Tahoma">};<o:p></o:p></span>
		</p>
		<p class="MsoNormal" style="MARGIN: 0cm 0cm 0pt">
				<span lang="EN-US" style="FONT-FAMILY: Tahoma"> <o:p></o:p></span>
		</p>
		<p class="MsoNormal" style="MARGIN: 0cm 0cm 0pt">
				<span lang="EN-US" style="FONT-FAMILY: Tahoma">int main(int argc, char* argv[])<o:p></o:p></span>
		</p>
		<p class="MsoNormal" style="MARGIN: 0cm 0cm 0pt">
				<span lang="EN-US" style="FONT-FAMILY: Tahoma">{<o:p></o:p></span>
		</p>
		<p class="MsoNormal" style="MARGIN: 0cm 0cm 0pt">
				<span lang="EN-US" style="FONT-FAMILY: Tahoma">
						<span style="mso-tab-count: 1">       </span>AA *pp;<o:p></o:p></span>
		</p>
		<p class="MsoNormal" style="MARGIN: 0cm 0cm 0pt">
				<span lang="EN-US" style="FONT-FAMILY: Tahoma">
						<span style="mso-tab-count: 1">       </span>while(1)<o:p></o:p></span>
		</p>
		<p class="MsoNormal" style="MARGIN: 0cm 0cm 0pt">
				<span lang="EN-US" style="FONT-FAMILY: Tahoma">
						<span style="mso-tab-count: 1">       </span>{<o:p></o:p></span>
		</p>
		<p class="MsoNormal" style="MARGIN: 0cm 0cm 0pt">
				<span lang="EN-US" style="FONT-FAMILY: Tahoma">
						<span style="mso-tab-count: 2">              </span>pp=new AA;<o:p></o:p></span>
		</p>
		<p class="MsoNormal" style="MARGIN: 0cm 0cm 0pt">
				<span lang="EN-US" style="FONT-FAMILY: Tahoma">
						<span style="mso-tab-count: 2">              </span>delete pp;<o:p></o:p></span>
		</p>
		<p class="MsoNormal" style="MARGIN: 0cm 0cm 0pt">
				<span lang="EN-US" style="FONT-FAMILY: Tahoma">
						<span style="mso-tab-count: 1">       </span>}<o:p></o:p></span>
		</p>
		<p class="MsoNormal" style="MARGIN: 0cm 0cm 0pt">
				<span lang="EN-US" style="FONT-FAMILY: Tahoma">}<o:p></o:p></span>
		</p>
		<p class="MsoNormal" style="MARGIN: 0cm 0cm 0pt">
				<span style="FONT-FAMILY: 宋体; mso-ascii-font-family: Tahoma; mso-hansi-font-family: Tahoma; mso-bidi-font-family: Tahoma">几个小时后复现了此现象。</span>
				<br />
				<br />网上查找<span lang="EN-US">0X10212AD0</span> 有如下线索：<br /><a href="http://www.experts-exchange.com/Programming/Programming_Languages/MFC/Q_21088390.html">http://www.experts-exchange.com/Programming/Programming_Languages/MFC/Q_21088390.html</a></p>
<img src ="http://www.cppblog.com/jjbird/aggbug/15292.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/jjbird/" target="_blank">奇奇</a> 2006-11-17 13:08 <a href="http://www.cppblog.com/jjbird/articles/15292.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>为了防止发生内存泄漏须遵循的编程规范</title><link>http://www.cppblog.com/jjbird/articles/11602.html</link><dc:creator>奇奇</dc:creator><author>奇奇</author><pubDate>Wed, 23 Aug 2006 02:21:00 GMT</pubDate><guid>http://www.cppblog.com/jjbird/articles/11602.html</guid><wfw:comment>http://www.cppblog.com/jjbird/comments/11602.html</wfw:comment><comments>http://www.cppblog.com/jjbird/articles/11602.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/jjbird/comments/commentRss/11602.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/jjbird/services/trackbacks/11602.html</trackback:ping><description><![CDATA[为了防止发生内存泄漏这样棘手的故障，在VC编程时应当注意遵循几个规范：其一，如果一个类包含有指针并且分配了指针值，那么就需要构造相应的析构函数以删除该指针；其二，如果一个函数分配了一块内存并把该内存块返回给调用它的函数使用，那么它返回的必须是一个指针而非一个引用，因为引用不能被程序删除；其三，即使一个函数分配了一段内存并在同一函数的稍后部分删除了该内存段，也要尽可能将内存块分配到堆栈中；最后，就是决不要试图改变一个指针值，除非已经删除指针所指的对象或通过数组指向了该指针所指向的内存，而且也不要对new返回的指针进行加1运算。<br /><img src ="http://www.cppblog.com/jjbird/aggbug/11602.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/jjbird/" target="_blank">奇奇</a> 2006-08-23 10:21 <a href="http://www.cppblog.com/jjbird/articles/11602.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>VC Studio 使用技巧、VC项目文件说明</title><link>http://www.cppblog.com/jjbird/articles/11597.html</link><dc:creator>奇奇</dc:creator><author>奇奇</author><pubDate>Wed, 23 Aug 2006 01:02:00 GMT</pubDate><guid>http://www.cppblog.com/jjbird/articles/11597.html</guid><wfw:comment>http://www.cppblog.com/jjbird/comments/11597.html</wfw:comment><comments>http://www.cppblog.com/jjbird/articles/11597.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/jjbird/comments/commentRss/11597.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/jjbird/services/trackbacks/11597.html</trackback:ping><description><![CDATA[
		<table cellspacing="0" bordercolordark="#ffffff" cellpadding="2" width="500" bordercolorlight="#3399ff" border="1">
				<tbody>
						<tr>
								<td width="100%" bgcolor="#3399ff">
										<font color="#00ffff">1.检测程序中的括号是否匹配</font>
								</td>
						</tr>
						<tr>
								<td width="100%">  把光标移动到需要检测的括号（如大括号{}、方括号[]、圆括号（）和尖括号&lt;&gt;）前面，键入快捷键“Ctrl＋]”。如果括号匹配正确，光标就跳到匹配的括号处，否则光标不移动，并且机箱喇叭还会发出一声警告声。</td>
						</tr>
						<tr>
								<td width="100%" bgcolor="#3399ff">
										<font color="#00ffff">2.查看一个宏（或变量、函数）的宏定义</font>
								</td>
						</tr>
						<tr>
								<td width="100%">  把光标移动到你想知道的一个宏上，就比如说最常见的DECLARE_MAP_MESSAGE上按一下F12(或右键菜单中的Go To Defition Of …),如果没有建立Browse files，会出现提示对话框，确定，然后就会跳到定义那些东西的地方。</td>
						</tr>
						<tr>
								<td width="100%" bgcolor="#3399ff">
										<font color="#00ffff">3.格式化一段乱七八糟的源代码</font>
								</td>
						</tr>
						<tr>
								<td width="100%">  选中那段源代码，按ATL+F8。</td>
						</tr>
						<tr>
								<td width="100%" bgcolor="#3399ff">
										<font color="#00ffff">4.在编辑状态下发现成员变量或函数不能显示</font>
								</td>
						</tr>
						<tr>
								<td width="100%">  删除该项目扩展名为.ncb文件，重新打开该项目。</td>
						</tr>
						<tr>
								<td width="100%" bgcolor="#3399ff">
										<font color="#00ffff">5.如何整理ClassView视图中大量的类</font>
								</td>
						</tr>
						<tr>
								<td width="100%">  可以在classview 视图中右键新建文件夹（new folder），再把具有相近性质的类拖到对应的文件夹中，使整个视图看上去清晰明了.</td>
						</tr>
						<tr>
								<td width="100%" bgcolor="#3399ff">
										<font color="#00ffff">6.定位预处理指定</font>
								</td>
						</tr>
						<tr>
								<td width="100%">在源文件中定位光标到对称的#if, #endif,使用Ctrl+K.</td>
						</tr>
						<tr>
								<td width="100%" bgcolor="#3399ff">
										<font color="#00ffff">7.如何添加系统中Lib到当前项目</font>
								</td>
						</tr>
						<tr>
								<td width="100%">  在Project | Settings | Link | Object/library modules：输入Lib名称，不同的Lib之间用空格格开.</td>
						</tr>
						<tr>
								<td width="100%" bgcolor="#3399ff">
										<font color="#00ffff">8.如何添加系统中的头文件(.h)到当前项目.</font>
								</td>
						</tr>
						<tr>
								<td width="100%">  #include &lt;FileName.h&gt;,告诉编译到VC系统目录去找;使用#include "FileName.h"，告诉编译在当前目录找.</td>
						</tr>
						<tr>
								<td width="100%" bgcolor="#3399ff">
										<font color="#00ffff">9.如何在Studio使用汇编调试</font>
								</td>
						</tr>
						<tr>
								<td width="100%">  在WorkBench的Debugger状态下按CTRL+F7.</td>
						</tr>
						<tr>
								<td width="100%" bgcolor="#3399ff">
										<font color="#00ffff">10.怎样处理ClassZiard找不到的系统消息</font>
								</td>
						</tr>
						<tr>
								<td width="100%">  如果要在ClassWizard中处理WM_NCHITTEST等系统消息，请在ClassWizard中Class Info页中将Message filter改为Window就有了.</td>
						</tr>
						<tr>
								<td width="100%" bgcolor="#3399ff">
										<font color="#00ffff">11.如何干净的删除一个类</font>
								</td>
						</tr>
						<tr>
								<td width="100%">  先从Workspace中的FileView中删除对应的.h和.cpp文件,再关闭项目，从实际的文件夹中删除对应的.h和.cpp文件与.clw文件。</td>
						</tr>
						<tr>
								<td width="100%" bgcolor="#3399ff">
										<font color="#00ffff">12.如果让控制台应用程序支持mfc类库</font>
								</td>
						</tr>
						<tr>
								<td width="100%">  可以在控制台应用程序中include 来引入mfc库，但是控制台应用程序缺省是单线程的，mfc是多线程的，为解决该矛盾，在project setting-&gt;c/c++ 选项，选择code generation,在use run-time library 下拉框中选择debug multithread。</td>
						</tr>
						<tr>
								<td width="100%" bgcolor="#3399ff">
										<font color="#00ffff">13.如何汉化只有可执行代码的.exe 文件</font>
								</td>
						</tr>
						<tr>
								<td width="100%">  在nt 下利用vc open file 以resources方式打开*.exe 文件，直接修改资源文件，然后保存即可。</td>
						</tr>
						<tr>
								<td width="100%" bgcolor="#3399ff">
										<font color="#00ffff">附：VC项目文件说明</font>
								</td>
						</tr>
						<tr>
								<td width="100%">
										<table cellspacing="0" bordercolordark="#ffffff" width="100%" bordercolorlight="#3399ff" border="1">
												<tbody>
														<tr>
																<td width="14%">.opt</td>
																<td width="86%">工程关于开发环境的参数文件。如工具条位置等信息；</td>
														</tr>
														<tr>
																<td width="14%">.aps</td>
																<td width="86%">(AppStudio File),资源辅助文件,二进制格式,一般不用去管他.</td>
														</tr>
														<tr>
																<td width="14%">.clw</td>
																<td width="86%">ClassWizard信息文件,实际上是INI文件的格式,有兴趣可以研究一下.有时候ClassWizard出问题,手工修改CLW文件可以解决.如果此文件不存在的话,每次用ClassWizard的时候绘提示你是否重建.</td>
														</tr>
														<tr>
																<td width="14%">.dsp</td>
																<td width="86%">(DeveloperStudio Project):项目文件,文本格式,不过不熟悉的话不要手工修改.DSW(DeveloperStudio Workspace)是工作区文件,其他特点和DSP差不多.</td>
														</tr>
														<tr>
																<td width="14%">.plg</td>
																<td width="86%">是编译信息文件,编译时的error和warning信息文件（实际上是一个html文件）,一般用处不大.在Tools-&gt;Options里面有个选项可以控制这个文件的生成.</td>
														</tr>
														<tr>
																<td width="14%">.hpj</td>
																<td width="86%">(Help Project)是生成帮助文件的工程,用microsfot  Help Compiler可以处理.</td>
														</tr>
														<tr>
																<td width="14%">.mdp</td>
																<td width="86%">(Microsoft DevStudio Project)是旧版本的项目文件,如果要打开此文件的话,会提示你是否转换成新的DSP格式.</td>
														</tr>
														<tr>
																<td width="14%">.bsc</td>
																<td width="86%">是用于浏览项目信息的,如果用Source Brower的话就必须有这个文件.如果不用这个功能的话,可以在Project Options里面去掉Generate Browse Info File,可以加快编译速度.</td>
														</tr>
														<tr>
																<td width="14%">.map</td>
																<td width="86%">是执行文件的映像信息纪录文件,除非对系统底层非常熟悉,这个文件一般用不着.</td>
														</tr>
														<tr>
																<td width="14%">.pch</td>
																<td width="86%">(Pre-Compiled File)是预编译文件,可以加快编译速度,但是文件非常大.</td>
														</tr>
														<tr>
																<td width="14%">.pdb</td>
																<td width="86%">(Program Database)记录了程序有关的一些数据和调试信息,在调试的时候可能有用.</td>
														</tr>
														<tr>
																<td width="14%">.exp</td>
																<td width="86%">只有在编译DLL的时候才会生成,记录了DLL文件中的一些信息.一般也没什么用.</td>
														</tr>
														<tr>
																<td width="14%">.ncb</td>
																<td width="86%">无编译浏览文件(no compile browser)。当自动完成功能出问题时可以删除此文件。build后会自动生成。</td>
														</tr>
												</tbody>
										</table>
								</td>
						</tr>
				</tbody>
		</table>
		<br />
		<div id="con_a_b">
		</div>
		<br />
<img src ="http://www.cppblog.com/jjbird/aggbug/11597.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/jjbird/" target="_blank">奇奇</a> 2006-08-23 09:02 <a href="http://www.cppblog.com/jjbird/articles/11597.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>基于IP Multicast的传输和实现</title><link>http://www.cppblog.com/jjbird/articles/6334.html</link><dc:creator>奇奇</dc:creator><author>奇奇</author><pubDate>Wed, 26 Apr 2006 09:33:00 GMT</pubDate><guid>http://www.cppblog.com/jjbird/articles/6334.html</guid><wfw:comment>http://www.cppblog.com/jjbird/comments/6334.html</wfw:comment><comments>http://www.cppblog.com/jjbird/articles/6334.html#Feedback</comments><slash:comments>1</slash:comments><wfw:commentRss>http://www.cppblog.com/jjbird/comments/commentRss/6334.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/jjbird/services/trackbacks/6334.html</trackback:ping><description><![CDATA[
		<table cellspacing="0" cellpadding="0" width="90%" border="0">
				<tbody>
						<tr>
								<td>
										<p align="center">
												<font size="6">基于IP Multicast的传输和实现</font>
												<br />
										</p>
										<p>
												<b>
												</b> </p>
										<table cellspacing="6" cellpadding="0" width="100%" border="0">
												<tbody>
														<tr>
																<td class="news">
																		<p>周承芳 汪志兵 唐昆 杜文</p>
																		<br />
																		<p>　　摘要：设计并实现了一个基于IP Multicast技术的远程数字音视频监控系统，提出了分布式音视频接入共享的解决方案，能够有效地节省网络带宽；针对视频、音频语音和控制数据的不同传输特点，对其所采用的传输技术作了详细探讨，给出了具体实现方法。 <br />　　关键词：监控系统 分布式接入共享 网络传输 IP组播 Windows套接字 <br />　　随着计算机网络技术、多媒体技术、计算机视觉与模式识别技术的发燕尾服，一种以数字化、智能化为特点的多媒体远程数字监控系统应运而生，即基于IP的数字监控系统，实现了由传统的模拟监控到数字监控质的飞跃。与传统的模拟监控系统相比较，数字远程监控系统几个最主要的优势是：可以借助网络实现远程监控；在远程不同地点的分控中心或同个分控中心可同时调看某一个或者几个监控现场的音视频数据，从而实现分布式的音频频接入和音视频数据共享，同时，可以与监控现场人员进行对讲；可以对远程监控现场的云台、摄像机等外围设备进行控制。视频、音频的实时、分布式传输及控制指令的可靠传输是远程数字监控系统的一个关键问题。本文设计并实现了远程数字音频频监控系统，采用IP Multicast技术作为分布式音视频执着入和共享的解决方案，并针对视频、音频语音和控制数据不同的特点，对其所采用的不同传输技术进行了探讨，给出了具体实现方法。 </p>
																		<br />
																		<p>　　1 系统的总体结构 </p>
																		<br />
																		<p>　　远程监控系统一般包括三部分：前端监控现场、通信设备和后端分控中心。整个系统基于Client/Server（客户机/服务器）模式。总体结构如图1所示。 </p>
																		<br />
																		<p>
																		</p>
																		<table cellspacing="5" cellpadding="0" align="center" border="0">
																				<tbody>
																						<tr>
																								<td>
																										<img src="http://www.cntele.com/yellow/video/UploadFiles/2004116114816794.gif" border="0" />
																										<a href="http://www.videosky.com/newsimg/1494-1.gif" target="_blank">
																										</a>
																								</td>
																						</tr>
																				</tbody>
																		</table>
																		<br />　　（1）前端监控现场由监控现场主机及一些外围设备组成。外围设备包括摄像机、电动镜头、云台、防护罩、监视器、多功能解码器及报警器。监控现场主机运行客户前端软件，实现视频、音频数据的实时采集、压缩、解压缩（音频）（视频传输 单向的，音频传输是双向的）及打包传送；对压缩的视（音）频数据进行经存储（也可在分近中心进行）。存储方式为循环存储、定时存储、手动存储及运动视频检测启动存储。接收来自分控中心的控制指令（也可在本地实施），对云台动作（上、下、左、右及自动）电动镜头的三可变（光圈、焦距和聚焦）。 <br />　　（2）通信设备是指所采用的传输信道和相关设备，通信网络为LAN及WAN。 <br />　　（3）后端设备由若干分控中心计算机组成。各分控计算机运行服务器端软件，接收来自前端压缩视（音）频、显示（播放）；通过网络对前端云台、摄像机进行控制；采用组播技术，实现分布式视频执着入和分丰式视频共享：每个分控中心主机可以同时监控多个前端，即“一点对多点”；不同分控心也可以同时监控同一前端，即“多点对一点”。 <br /><p>　　2 网络传输模块的设计与实现 </p><br /><p>　　2.1 系统传输数据类型的特点及通信协议的选择 </p><br /><p>　　系统传输数据有：控制数据、音频、视频数据、后端分控中心通过网络向监控现场主机外围设备云台及摄像机发送控制信号，实现云台动作（上、下、左、右、自动）摄像机光圈、焦距及聚焦三可变，要求控制信号的传输准确无误；音频、视频是连续，数据量大，允许传输中存在一定的数据错误率及数据丢失率，但实时性要求很高。此外，在监控系统中，要实现音视频的分布式接入和数据共享，必须进行音视频的多点传输。样实现上述目标？首先是通信协议的选择，TCP/IP协议是广泛使用的网协议，其网络模型定义了四层（即网络接口层、网络层、传输层、应用层）网络通信协议。传输层包含两个协议：传输控制协议（TCP）和用户数据报协议（UDP）。IP是国际互联协议，位于网络层。TCP协议是面向连接的，提供可靠的流服务；UDP是无连接的，提供数据报服务；TCP采用提供确认与超时重发、滑动窗口机制等措施来保证传输的可靠性，正是这些措施增加了网络的开销。如果用TCP传输视（音）频数据，大量的数据容量引起重传。，使得网络负载大并会加大延迟；UDP协议是最简单的传输协议，不提供可靠性保证，正因为UDP协议不进行数据确认与重传国，大大提高了传输效率，具有高效快速的特点；Ipv4定义了三种IP数据包的传输：单播、广播及组播。要系统中实现视（音）频数据的多点传输，若采用单播，则同样的音、视频数据要发送多次，这样导致发送者负担重、延迟长、网络拥塞；若用广播，网络中的每个站点都将接收到数据，不管该结点否需要数据，增加了非接收者的开销；组播是一种允许一个或多个发送者（组播源）发送单一的数据包到多个接收者（一次的、同时的）的网络技术。组播源把数据包发送到特定组播组，而只有属于该组播组的地址才能接收到数据包。由于无论有多少个目的地址，在整个网络的任何一条链路上都只传送单一的数据包。因此组播提高了网络传输的效率，极大地节省了网络传输。组播方式只适用于UDP。综上所述，采用TCP/IP传输控制信号，即信令通道；采用UDP/IP传输音视频信号，即数据通道。 <br />　　IP组播依赖一个特殊的地址组——“移播址”，即D类地址。范围在224.0.0.0-239.255.255.255之间（其中224.0.0.0-224.0.0.255是被保留的地址），D类地址是动态分配和恢复的瞬态地址。组播地址只能作为信宿地址使用，而不能出现在任何信源地址中。每一个组播组对应于动态分配 的一个D类地址。组播的特点：组播组的成员是动态的，主机可以任何时间加入或离开组播组，主机组中的成员在位置上和数量 旧没有限制的。 </p><br /><p>　　2.2 Windows下，IP组播的Winsock2实现 </p><br /><p>　　Windows环境下组播通信是基于WindowsSocket的。Windows Socket提供两种不同IP组播的实现方法：Windows Socket提供两种不同的IP组播的实现方法：Winsock1与Winsock2。在Windows2000平台实现VC++6.0开发工具，在本系统中实现了基于Winsock2的组播通信编程。 <br />　　发送端（前端、客户端）实现步骤： <br />　　(1)加载Winsock2库，完成Winsock2的初始化： <br />　　WSAStarup(MAKEWORD(2,2),&amp;wsaData)；(2)建立本地套接字（UDP）： <br />m_socket=WSASocke(AF_INET,SOCK_DGRAM,IPPROTO_UDP,NULL,0,<br />WSA_FLAG_MULTIPOINT_C_LEAF|WSA_FLAG_MULTIPOINT_D_LEAF); <br />　　//组播通信具有两个层面的重要特征：控制层面和数据层面。控制层面决定一个多播组建立通信的方式，数据层面决定通信成员间数据传输的方式。每一个层面有两种形式，一种是“有限的”，另一种是“无根的”；数据报IP组播在两个层面上都是“无根”的。任一用户发送的数据都将被传送到组中所有其它成员。最后一个参数表明新创建的套接字在控制层面与数据层面都是“无根的”。 <br />可以通过setsocket函数设置套接字的属性，如地址重用，缓冲区是接收还是发送。 <br />　　M_localAddr.sin_family = AF_INET; <br />　　M_localAddr.sin_port=m_iPort;//本地端口号 <br />　　M_localAddr..sin _addr.S_un.S_addr=m_uLocalIP;//本地IP地址； </p><br /><p>　　（3）绑定（将新创建的套字节与本地插口地址进行绑定）： <br />　　bind(m_socket,(PSOCKADDR)&amp;(m_localAddr),sizeof(m_localAddr); </p><br /><p>　　(4)设置生存时间（即数据包最多允许路由多少个网段）： <br />　　WSAIoctl(m_socket,SIO_MULTICAST_SCOPE,//设置数据报生存时间； <br />　　&amp;iMcastTTL,//生存时间大小； <br />　　sizeof(iMcastTTL),NULL,0,&amp;cbRet,NULL,NULL)； </p><br /><p>　　（5）配置Loopback,以决定组播数据帧是否回送： </p><br /><p>　　int bLoopback=FALSE; <br />　　WSAIoct(m_socket,SIO_MULTIPOINT_LOOPBACK,//允许或禁止组播数据帧回送； <br />　　&amp;bLoopback,sizeof(bLoopback)，NULL,0,&amp;cbRet,NULL,NULL); </p><br /><p>　　(6)收发数据： </p><br /><p>　　在发送方（前端、客户端）响应发送的消息函数中调用下面函数： <br />　　WSASendTo (m_socket,&amp;stWSABuf,&amp;cbRet,0,(struct sockaddr*)&amp;stDestAddr,//发送的目的地址； <br />　　sizeof(struct(sockaddr),NULL,NULL); <br />　　在发送方（前端、客户端）响应接收消息函数中调用下面函数： <br />　　WSARecvFrom(m_socket,&amp;stWSABuf,1,&amp; cbRet,&amp;Flag,(struct sockaddr*)&amp;stSrcAddr,//源地址； <br />　　&amp;iLen，NULL，NULL）； </p><br /><p>　　（7）将组播套接字设置为异步I/O工作模式，在该套节字上接收事件为基础的网络事件通知： </p><br /><p>　　WSAEventSelect(m_socket，m_hNetworkEvent，//网络事件句柄；将此套字节与该事件句柄并联在一起； <br />　　FD_WRITE|FD_READ）；//发生此两个事件之一，则将m_hNetworkEvent置为有信号状态； </p><br /><p>　　（8）在工作线程中设置： </p><br /><p>　　WSAWaitForMultipleEvent（3，//等待事件的个数）； <br />　　p-&gt;m_eventArray,//存放事件句柄的数组； <br />　　FALSE，WSA_INFINITE，FALSE）； </p><br /><p>　　（9）关闭组播套字节： </p><br /><p>　　closesocket(m_socket)； </p><br /><p>　　接收端（后端、服务器端）实现步骤： </p><br /><p>　　（1）-（3）与发送端（客户端）相同； <br />　　（4）调用WSAJLoinLeaf加入组播组： </p><br /><p>　　SOCKET NetSock=WSAJoinLeaf(sock,//必须为组播标志进行创建，否则调用失败； <br />　　（PSOCKADDR）&amp;（m_stDestAddr,//组播导址，与发送方的目的地址相同； <br />　　sizeof(m_stDestAddr),UNLL,NULL,NULL,NULL, <br />　　JL_BOTH));//允许接收和发送； </p><br /><p>　　（5）与客户端（6）相同；（6）与客户端（7）相同；（7）与客户端（8）相同；（8）离开组播组；closesocket(NewSock)；//NewSock是调用WSAoinLeaf（）返回的套节字。 </p><br /><p>　　2.3 在监控系统中网络传输模块的设计 </p><br /><p>　　网络传输模块流程如图2所示。</p><br /><p></p><table cellspacing="5" cellpadding="0" align="center" border="0"><tbody><tr><td><img src="http://www.cntele.com/yellow/video/UploadFiles/2004116114825491.gif" border="0" /><a href="http://www.videosky.com/newsimg/1494-2.gif" target="_blank"></a></td></tr></tbody></table> <br />　　发送端（前端监控现场主机、客户端）监控主机运行客户端程序。在主线程中，启动视同、音频两个线程分别对视频及音频进行采集，放入视（音）频缓冲区；视频在本地回放；同时，监听分控中心的连接请求，收到连接请求，TCP三次握手，建立TCP连接（信令通道）；通过信令通道，向分控心发送二组组播地址及端口号（对应视频及音频，音频两个线程；分别在视（音）频线程中完成；利用Winsock2建立视（音）频数据通道（UDP）（源码前已述及）；对视（音）频进行压缩编码、组播发送；音频线程接收分控中心的音频数据包，解码并播放；实现视频的单向传输和音频的双向传输。 <br />　　接收端（后端分控中心、服务器端）分控中心主机运行服务器端程序，在主线程中向前端监控现场主机发出连接请求（CALL），三次握手建立TCP连接（信令通道）；后端接收到组播地址及端口号后，启动视（音）频两个线程，完成；利用Winsock2建立视（音）频数据通道（UDP），加入视（音）频组播组，接收压缩视（音）频包，并解码显示（播放）；其中音频线程，还要完成音频数据包解码显示（播放）；其中音频线程，还要完成音频数据包的压缩、发送；实现视频的单向传输、音频的双向传输。 <br />　　一个后端分控中心可同时监控12路前端视频及音频信号，在设计服务器端监控程序时，采用多线程技术，每建立一对前端监控主机与后端分控中心（服务器）的TCP连接，就开两个接收线程（一个接收视频线程；一个接收音频线程），视频线程接收视频数据包进行解压缩及回放；音频线程接收音频数据包进行解压缩及播放。对云台及摄像机的控制指令通过信令通道传输。 <br />　　本系统运行在Win2000平台上，用VC++6.0开发工具开发。 <br />　　在远程数字音视频监控系统的传输模块设计中，根据音频频数据传输及控制信号传输的特点，运用IP组播技术极大地减轻了网络负担，避免了资源的浪费，节省了网络带宽；利用TCP/IP协议设计了信令通道（TCP）和数据通道（UDP）；运用Windows多线程机制实现了音频数据的实时、多点传输和控制信号的可靠传，提高了程序运行的效率。在该系统的基础上进行改造和扩展，可以应用到远程教学、远程医疗等多种多媒体通信；因此，远程数字音视频监控系统的设计和实现不仅具有监控方面的现实意义，而且对于许多应用都有借鉴和参考价值。 </td>
														</tr>
												</tbody>
										</table>
								</td>
						</tr>
				</tbody>
		</table>
<img src ="http://www.cppblog.com/jjbird/aggbug/6334.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/jjbird/" target="_blank">奇奇</a> 2006-04-26 17:33 <a href="http://www.cppblog.com/jjbird/articles/6334.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>WSAEventSelect（开发网络通信程序入门的继续--转自CSDN）</title><link>http://www.cppblog.com/jjbird/articles/6333.html</link><dc:creator>奇奇</dc:creator><author>奇奇</author><pubDate>Wed, 26 Apr 2006 09:30:00 GMT</pubDate><guid>http://www.cppblog.com/jjbird/articles/6333.html</guid><wfw:comment>http://www.cppblog.com/jjbird/comments/6333.html</wfw:comment><comments>http://www.cppblog.com/jjbird/articles/6333.html#Feedback</comments><slash:comments>3</slash:comments><wfw:commentRss>http://www.cppblog.com/jjbird/comments/commentRss/6333.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/jjbird/services/trackbacks/6333.html</trackback:ping><description><![CDATA[
		<table cellspacing="0" cellpadding="0" width="95%" border="0">
				<tbody>
						<tr>
								<td align="middle" width="100%">
										<h2>
												<b>WSAEventSelect（开发网络通信程序入门的继续--转自CSDN）</b>
										</h2>
								</td>
						</tr>
						<tr>
								<td width="100%">
										<p>
										</p>
										<p>   前面讨论的开发网络通信的经典入门采用的是WSAAsyncSelect的异步I/O模型，本文将讨论WSAEventSelect异步I/O模型。 
</p>
										<p>       WSAEventSelect模型有点类似WSAAsyncSelect模型，不同的是他不是用消息映射的方式来响应网络事件，而是用等待多重事件的方式来响应网络事件。下面是用WSAEventSelect模型和多线程机制做的一个简单的服务器程序的.cpp和.h文件，应用程序基于MFC的标准对话框。实现接受多个客户端的连接请求，并记录下所有客户端的相关信息，显示在列表框中。</p>
										<p>// serverDlg.cpp : implementation file<br />//</p>
										<p>#include "stdafx.h"<br />#include "server.h"<br />#include "serverDlg.h"</p>
										<p>#ifdef _DEBUG<br />#define new DEBUG_NEW<br />#undef THIS_FILE<br />static char THIS_FILE[] = __FILE__;<br />#endif</p>
										<p>SOCKET Accept; <a href="file://用/"><font color="#336699">file://用</font></a>于新的一个连接通信的套接字<br />WSAEVENT NewEvent; <a href="file://对/"><font color="#336699">file://对</font></a>应于新的套接字的新事件<br />SOCKET Socket[WSA_MAXIMUM_WAIT_EVENTS];  <a href="file://存/"><font color="#336699">file://存</font></a>放所有生成的套接字<br />WSAEVENT Event[WSA_MAXIMUM_WAIT_EVENTS]; <a href="file://存/"><font color="#336699">file://存</font></a>放所有生成的事件对象<br />int EventTotal; <a href="file://创/"><font color="#336699">file://创</font></a>建的事件总数<br />int Index;      <a href="file://等/"><font color="#336699">file://等</font></a>待多重事件函数的返回值<br />WSANETWORKEVENTS NetworkEvents; <a href="file://用/"><font color="#336699">file://用</font></a>于接收套接字上发生的网络事件类型以及可能出现的错<br />误代码</p>
										<p>/////////////////////////////////////////////////////////////////////////////<br />// CAboutDlg dialog used for App About</p>
										<p>class CAboutDlg : public CDialog<br />{<br />public:<br /> CAboutDlg();</p>
										<p>// Dialog Data<br /> <a href="file://{{/"><font color="#336699">file://{{</font></a>AFX_DATA(CAboutDlg)<br /> enum { IDD = IDD_ABOUTBOX };<br /> <a href="file://}}/"><font color="#336699">file://}}</font></a>AFX_DATA</p>
										<p> // ClassWizard generated virtual function overrides<br /> <a href="file://{{/"><font color="#336699">file://{{</font></a>AFX_VIRTUAL(CAboutDlg)<br /> protected:<br /> virtual void DoDataExchange(CDataExchange* pDX);    // DDX/DDV support<br /> <a href="file://}}/"><font color="#336699">file://}}</font></a>AFX_VIRTUAL</p>
										<p>// Implementation<br />protected:<br /> <a href="file://{{/"><font color="#336699">file://{{</font></a>AFX_MSG(CAboutDlg)<br /> <a href="file://}}/"><font color="#336699">file://}}</font></a>AFX_MSG<br /> DECLARE_MESSAGE_MAP()<br />};</p>
										<p>CAboutDlg::CAboutDlg() : CDialog(CAboutDlg::IDD)<br />{<br /> <a href="file://{{/"><font color="#336699">file://{{</font></a>AFX_DATA_INIT(CAboutDlg)<br /> <a href="file://}}/"><font color="#336699">file://}}</font></a>AFX_DATA_INIT<br />}</p>
										<p>void CAboutDlg::DoDataExchange(CDataExchange* pDX)<br />{<br /> CDialog::DoDataExchange(pDX);<br /> <a href="file://{{/"><font color="#336699">file://{{</font></a>AFX_DATA_MAP(CAboutDlg)<br /> <a href="file://}}/"><font color="#336699">file://}}</font></a>AFX_DATA_MAP<br />}</p>
										<p>BEGIN_MESSAGE_MAP(CAboutDlg, CDialog)<br /> <a href="file://{{/"><font color="#336699">file://{{</font></a>AFX_MSG_MAP(CAboutDlg)<br />  // No message handlers<br /> <a href="file://}}/"><font color="#336699">file://}}</font></a>AFX_MSG_MAP<br />END_MESSAGE_MAP()</p>
										<p>/////////////////////////////////////////////////////////////////////////////<br />// CServerDlg dialog</p>
										<p>CServerDlg::CServerDlg(CWnd* pParent /*=NULL*/)<br /> : CDialog(CServerDlg::IDD, pParent)<br />{<br /> <a href="file://{{/"><font color="#336699">file://{{</font></a>AFX_DATA_INIT(CServerDlg)<br />  // NOTE: the ClassWizard will add member initialization here<br /> <a href="file://}}/"><font color="#336699">file://}}</font></a>AFX_DATA_INIT<br /> // Note that LoadIcon does not require a subsequent DestroyIcon in Win32<br /> m_Connectnum = 0;<br /> m_NetworkID = 0;<br /> EventTotal = 0;<br />    for(int i = 0; i &lt; MAX_CLIENT_NUM; i++)<br /> {<br />  ZeroMemory(&amp;m_ClientInfo[i], sizeof(client_info));<br /> }</p>
										<p> m_hIcon = AfxGetApp()-&gt;LoadIcon(IDR_MAINFRAME);<br />}</p>
										<p>void CServerDlg::DoDataExchange(CDataExchange* pDX)<br />{<br /> CDialog::DoDataExchange(pDX);<br /> <a href="file://{{/"><font color="#336699">file://{{</font></a>AFX_DATA_MAP(CServerDlg)<br />  // NOTE: the ClassWizard will add DDX and DDV calls here<br /> <a href="file://}}/"><font color="#336699">file://}}</font></a>AFX_DATA_MAP<br />}</p>
										<p>BEGIN_MESSAGE_MAP(CServerDlg, CDialog)<br /> <a href="file://{{/"><font color="#336699">file://{{</font></a>AFX_MSG_MAP(CServerDlg)<br /> ON_WM_SYSCOMMAND()<br /> ON_WM_PAINT()<br /> ON_WM_QUERYDRAGICON()<br /> ON_WM_TIMER()<br /> <a href="file://}}/"><font color="#336699">file://}}</font></a>AFX_MSG_MAP<br />END_MESSAGE_MAP()</p>
										<p>/////////////////////////////////////////////////////////////////////////////<br />// CServerDlg message handlers</p>
										<p>BOOL CServerDlg::OnInitDialog()<br />{<br /> CDialog::OnInitDialog();</p>
										<p> // Add "About..." menu item to system menu.</p>
										<p> // IDM_ABOUTBOX must be in the system command range.<br /> ASSERT((IDM_ABOUTBOX &amp; 0xFFF0) == IDM_ABOUTBOX);<br /> ASSERT(IDM_ABOUTBOX &lt; 0xF000);</p>
										<p> CMenu* pSysMenu = GetSystemMenu(FALSE);<br /> if (pSysMenu != NULL)<br /> {<br />  CString strAboutMenu;<br />  strAboutMenu.LoadString(IDS_ABOUTBOX);<br />  if (!strAboutMenu.IsEmpty())<br />  {<br />   pSysMenu-&gt;AppendMenu(MF_SEPARATOR);<br />   pSysMenu-&gt;AppendMenu(MF_STRING, IDM_ABOUTBOX, strAboutMenu);<br />  }<br /> }</p>
										<p> // Set the icon for this dialog.  The framework does this automatically<br /> //  when the application's main window is not a dialog<br /> SetIcon(m_hIcon, TRUE);   // Set big icon<br /> SetIcon(m_hIcon, FALSE);  // Set small icon<br /> <br /> // TODO: Add extra initialization here</p>
										<p> WSADATA wsaData;<br /> int ret;</p>
										<p> ret = WSAStartup(MAKEWORD(2,2), &amp;wsaData);<br /> if(ret != 0)<br /> {<br />  MessageBox("初始化套接字失败!");<br />  return FALSE;<br /> }</p>
										<p> <a href="file://创/"><font color="#336699">file://创</font></a>建一个套接字<br /> m_ListenSocket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);<br />        if(m_ListenSocket == INVALID_SOCKET)<br /> {<br />  MessageBox("创建套接字失败!");<br />  closesocket(m_ListenSocket);<br />  WSACleanup();<br />  return FALSE;<br /> }</p>
										<p> <a href="file://绑/"><font color="#336699">file://绑</font></a>定到指定的端口上<br /> sockaddr_in localaddr;<br /> localaddr.sin_family = AF_INET;<br /> localaddr.sin_port = htons(1688);<br /> localaddr.sin_addr.s_addr = 0;</p>
										<p> if(bind(m_ListenSocket, (const struct sockaddr*)&amp;localaddr, sizeof(sockaddr))<br />                                                                 == SOCKET_ERROR)<br /> {<br />  MessageBox("绑定地址失败!");<br />  closesocket(m_ListenSocket);<br />  WSACleanup();<br />  return FALSE;<br /> }<br /> <br /> NewEvent = WSACreateEvent(); <a href="file://创/"><font color="#336699">file://创</font></a>建一个新的事件对象</p>
										<p> <a href="file://将/"><font color="#336699">file://将</font></a>创建的事件对象与前面创建的套接字关联在一起,并注册网络事件类型<br />        if(WSAEventSelect(m_ListenSocket, NewEvent, FD_ACCEPT | FD_CLOSE) == SOCKET_ERROR)<br /> {<br />  MessageBox("注册网络事件失败!");<br />  closesocket(m_ListenSocket);<br />  WSACleanup();<br />  return FALSE;<br /> }</p>
										<p> <a href="file://让/"><font color="#336699">file://让</font></a>创建的套接字处于监听状态<br /> listen(m_ListenSocket, 5);</p>
										<p> Event[EventTotal] = NewEvent;<br /> Socket[EventTotal] = m_ListenSocket;<br /> EventTotal++;</p>
										<p>        <a href="file://设/"><font color="#336699">file://设</font></a>置List控件的图象列表<br /> HICON hIcon;</p>
										<p> m_imagelist.Create(16, 16, 0, 4, 4); // 32, 32 for large icons<br /> hIcon = AfxGetApp()-&gt;LoadIcon(IDI_CLIENT_INFO);<br /> <br /> m_imagelist.SetBkColor (RGB(248,232,224));<br /> m_imagelist.Add(hIcon);</p>
										<p> pList = (CListCtrl*)GetDlgItem(IDC_CLIENT_INFO);<br /> pList-&gt;SetImageList(&amp;m_imagelist, LVSIL_SMALL);<br /> pList-&gt;SetBkColor(RGB(248,232,224));<br /> pList-&gt;SetTextBkColor(RGB(248,232,224));</p>
										<p> pList-&gt;InsertColumn(0,"   客户名",LVCFMT_CENTER,90, 0);<br /> pList-&gt;InsertColumn(1,"网络ID",LVCFMT_CENTER,50,1);<br /> pList-&gt;InsertColumn(2,"IP地址",LVCFMT_CENTER,100,2);<br /> pList-&gt;InsertColumn (3,"登录时间",LVCFMT_CENTER,120,3);<br /> pList-&gt;InsertColumn (4,"在线时间",LVCFMT_CENTER,100,4);</p>
										<p> SetTimer(1, 1000, NULL);</p>
										<p> <a href="file://启/"><font color="#336699">file://启</font></a>动核心处理线程<br /> AfxBeginThread(KernelWorkThread,this,THREAD_PRIORITY_NORMAL);</p>
										<p> return TRUE;  // return TRUE  unless you set the focus to a control<br />}</p>
										<p>void CServerDlg::OnSysCommand(UINT nID, LPARAM lParam)<br />{<br /> if ((nID &amp; 0xFFF0) == IDM_ABOUTBOX)<br /> {<br />  CAboutDlg dlgAbout;<br />  dlgAbout.DoModal();<br /> }<br /> else<br /> {<br />  CDialog::OnSysCommand(nID, lParam);<br /> }<br />}</p>
										<p>// If you add a minimize button to your dialog, you will need the code below<br />//  to draw the icon.  For MFC applications using the document/view model,<br />//  this is automatically done for you by the framework.</p>
										<p>void CServerDlg::OnPaint() <br />{<br /> if (IsIconic())<br /> {<br />  CPaintDC dc(this); // device context for painting</p>
										<p>  SendMessage(WM_ICONERASEBKGND, (WPARAM) dc.GetSafeHdc(), 0);</p>
										<p>  // Center icon in client rectangle<br />  int cxIcon = GetSystemMetrics(SM_CXICON);<br />  int cyIcon = GetSystemMetrics(SM_CYICON);<br />  CRect rect;<br />  GetClientRect(&amp;rect);<br />  int x = (rect.Width() - cxIcon + 1) / 2;<br />  int y = (rect.Height() - cyIcon + 1) / 2;</p>
										<p>  // Draw the icon<br />  dc.DrawIcon(x, y, m_hIcon);<br /> }<br /> else<br /> {<br />  CDialog::OnPaint();<br /> }<br />}</p>
										<p>// The system calls this to obtain the cursor to display while the user drags<br />//  the minimized window.<br />HCURSOR CServerDlg::OnQueryDragIcon()<br />{<br /> return (HCURSOR) m_hIcon;<br />}</p>
										<p>
												<a href="file://核/">
														<font color="#336699">file://核</font>
												</a>心处理线程, 响应并处理各种网络事件<br />UINT KernelWorkThread(LPVOID pParam)<br />{<br /> int len = sizeof(sockaddr);</p>
										<p> CServerDlg* dlg;<br /> dlg = (CServerDlg*)pParam;</p>
										<p> while(1)<br /> {<br />         Index = WSAWaitForMultipleEvents(EventTotal, Event, FALSE, WSA_INFINITE, FALSE);</p>
										<p>  WSAEnumNetworkEvents(Socket[Index - WSA_WAIT_EVENT_0], <br />                     Event[Index - WSA_WAIT_EVENT_0],<br />                                     &amp;NetworkEvents);<br /> <br />  if(NetworkEvents.lNetworkEvents &amp; FD_ACCEPT)<br />  <a href="file://连/"><font color="#336699">file://连</font></a>接事件<br />  {<br />   if(NetworkEvents.iErrorCode[FD_ACCEPT_BIT] != 0)<br />   {<br />    dlg-&gt;MessageBox("接受连接事件失败!");<br />    break;<br />   }</p>
										<p>   Accept = accept(Socket[Index - WSA_WAIT_EVENT_0], <br />                                 (struct sockaddr*)&amp;(dlg-&gt;clientaddr), &amp;len);<br />   if(Accept == INVALID_SOCKET)<br />   {<br />    dlg-&gt;MessageBox("接受连接失败!");<br />    break;<br />   }<br />   <br />   if(EventTotal &gt; WSA_MAXIMUM_WAIT_EVENTS)<br />   {<br />    dlg-&gt;MessageBox("连接个数溢出,拒绝接受!");<br />    break;<br />   }</p>
										<p>   NewEvent = WSACreateEvent();</p>
										<p>   if(WSAEventSelect(Accept, NewEvent, FD_READ | FD_WRITE | FD_CLOSE)<br />                                                                   == SOCKET_ERROR)<br />   {<br />    dlg-&gt;MessageBox("注册网络事件失败!");<br />    closesocket(Accept);<br />    break;<br />   }</p>
										<p>   Event[EventTotal] = NewEvent;<br />   Socket[EventTotal] = Accept;<br />   EventTotal ++;<br />  }</p>
										<p>  if(NetworkEvents.lNetworkEvents &amp; FD_READ)<br />  <a href="file://读/"><font color="#336699">file://读</font></a>取数据事件<br />  {<br />   if(NetworkEvents.iErrorCode[FD_READ_BIT] != 0)<br />   {<br />    dlg-&gt;MessageBox("读事件失败!");<br />    break;<br />   }</p>
										<p>   if(dlg-&gt;OnReceive(Socket[Index - WSA_WAIT_EVENT_0]) == FALSE)<br />   {<br />    dlg-&gt;MessageBox("读取数据失败!");<br />    break;<br />   }<br />  }</p>
										<p>  if(NetworkEvents.lNetworkEvents &amp; FD_CLOSE)<br />  <a href="file://关/"><font color="#336699">file://关</font></a>闭套接字事件<br />  {<br />   if(NetworkEvents.iErrorCode[FD_CLOSE_BIT] != 0)<br />   {<br />    dlg-&gt;MessageBox("关闭事件失败!");<br />    break;<br />   }</p>
										<p>   if(dlg-&gt;OnClose(Socket[Index - WSA_WAIT_EVENT_0]) == FALSE)<br />   {<br />    dlg-&gt;MessageBox("关闭套接字失败!");<br />    break;<br />   }<br />  }<br /> }</p>
										<p> return 0;<br />}</p>
										<p>BOOL CServerDlg::OnClose(SOCKET pSocket)<br />{<br /> int i, exitnum;<br /> <br /> for(i = 0; i &lt; m_Connectnum; i++)<br /> {<br />  if(m_ClientInfo[i].Client_Socket == pSocket)<br />  {<br />   exitnum = i;<br />  }<br /> }<br /> for(i = exitnum; i &lt; m_Connectnum; i++)<br /> {<br />  memcpy(&amp;m_ClientInfo[i], &amp;m_ClientInfo[i+1], sizeof(client_info));<br /> }</p>
										<p> m_Connectnum --;</p>
										<p> <a href="file://向/"><font color="#336699">file://向</font></a>所有客户端发送在线客户信息的报文<br /> cmd_client_info ClientInfo;<br /> ClientInfo.cmd_type = CMD_CLIENT_INFO;<br /> ClientInfo.client_num = m_Connectnum;<br /> <br /> for(i=0; i&lt;=m_Connectnum; i++)<br /> {<br />  ClientInfo.Networks_ID[i] = m_ClientInfo[i].Network_ID;<br />  strcpy(ClientInfo.users_name[i], m_ClientInfo[i].User_Name);<br />  strcpy(ClientInfo.clients_ipaddr[i], inet_ntoa(m_ClientInfo[i].Client_Addr.sin_addr));<br /> }<br /> for(i=0; i&lt;=m_Connectnum; i++)<br /> {<br />  send(m_ClientInfo[i].Client_Socket, (char*)&amp;ClientInfo, sizeof(cmd_client_info), NULL);<br /> }<br /> closesocket(pSocket);</p>
										<p>        pList-&gt;DeleteItem(exitnum);<br /> <br /> return TRUE;<br />}</p>
										<p>BOOL CServerDlg::OnReceive(SOCKET pSocket)<br />{<br />    static char rcvbuf[65535];   <a href="file://接/"><font color="#336699">file://接</font></a>收缓冲区<br />    int ret;<br /> int offset=0;<br /> find_type* pFindType;<br /> int i = 0;<br /> CTime m_current_time=CTime::GetCurrentTime ();<br /> CString strTime = m_current_time.Format("%c");<br /> CString networkid; <a href="file://列/"><font color="#336699">file://列</font></a>表框的网络ID项</p>
										<p>    ret = recv(pSocket, rcvbuf, 65535, 0);<br /> if(ret == OPERATION_ERROR)<br />  return FALSE;</p>
										<p> while(offset &lt; ret)<br /> {<br />  pFindType = (find_type*)(rcvbuf+offset);<br />  switch(pFindType-&gt;cmd_type)<br />  {<br />  case CMD_HELLO:  <br />   cmd_hello Hello;<br />   memcpy(&amp;Hello, rcvbuf+offset, sizeof(cmd_hello));<br />   offset+=sizeof(cmd_hello);</p>
										<p>   cmd_hello_resp HelloResp;<br />   m_NetworkID ++;<br />   HelloResp.cmd_type = CMD_HELLO_RESP;<br />   HelloResp.Network_ID = m_NetworkID;<br />   strcpy(HelloResp.user_name, Hello.user_name);</p>
										<p>   memcpy((struct sockaddr*)&amp;(m_ClientInfo[m_Connectnum].Client_Addr),<br />    (const struct sockaddr*)&amp;clientaddr, sizeof(sockaddr));<br />   m_ClientInfo[m_Connectnum].Client_Socket = Accept;<br />   strcpy(m_ClientInfo[m_Connectnum].User_Name, HelloResp.user_name);<br />   m_ClientInfo[m_Connectnum].Network_ID = m_NetworkID;<br />   m_ClientInfo[m_Connectnum].Login_Time = m_current_time;<br />   send(pSocket, (char*)&amp;HelloResp, sizeof(cmd_hello_resp), NULL);</p>
										<p>   <a href="file://向/"><font color="#336699">file://向</font></a>登录的客户端发送回应报文<br />   Sleep(200);</p>
										<p>   cmd_client_info ClientInfo;<br />   ClientInfo.cmd_type = CMD_CLIENT_INFO;<br />   ClientInfo.client_num = m_Connectnum +1;</p>
										<p>   for(i=0; i&lt;=m_Connectnum; i++)<br />   {<br />    ClientInfo.Networks_ID[i] = m_ClientInfo[i].Network_ID;<br />    strcpy(ClientInfo.users_name[i], m_ClientInfo[i].User_Name);<br />                                strcpy(ClientInfo.clients_ipaddr[i], <br />                                       inet_ntoa(m_ClientInfo[i].Client_Addr.sin_addr));<br />   }</p>
										<p>   <a href="file://向/"><font color="#336699">file://向</font></a>所有在线客户端发送在线客户信息报文<br />   for(i=0; i&lt;=m_Connectnum; i++)<br />   {<br />    send(m_ClientInfo[i].Client_Socket, (char*)&amp;ClientInfo,<br />                                     sizeof(cmd_client_info), NULL);<br />   }</p>
										<p>   <a href="file://刷/"><font color="#336699">file://刷</font></a>新客户端信息列表<br />   networkid.Format("%d", m_NetworkID);</p>
										<p>   LVITEM lvinsert;<br />   lvinsert.mask=LVIF_TEXT|LVIF_IMAGE|LVIF_PARAM;<br />   lvinsert.iItem=m_Connectnum;<br />   lvinsert.iSubItem=0;<br />   lvinsert.cchTextMax=20;<br />   lvinsert.pszText=HelloResp.user_name;<br />   lvinsert.iImage = 0;<br />   pList-&gt;InsertItem (&amp;lvinsert);<br />   pList-&gt;SetItemText (m_Connectnum,1,networkid);<br />   pList-&gt;SetItemText(m_Connectnum,2,<br />                               inet_ntoa(m_ClientInfo[m_Connectnum].Client_Addr.sin_addr));<br />   pList-&gt;SetItemText (m_Connectnum,3,strTime);</p>
										<p>   m_Connectnum ++;</p>
										<p>   break;<br />  case CMD_ASK:<br />   cmd_ask Ask;<br />   cmd_ask_resp AskResp;<br />   memcpy(&amp;Ask,rcvbuf+offset,sizeof(cmd_ask));<br />   offset+=sizeof(cmd_ask);<br />   AskResp.cmd_type = CMD_ASK_RESP;<br />   AskResp.Network_ID = Ask.Network_ID;<br />   for(i=0; i&lt;m_Connectnum; i++)<br />   {<br />    if(m_ClientInfo[i].Network_ID == Ask.Network_ID)<br />    {<br />     strcpy(AskResp.pData1,m_ClientInfo[i].User_Name);<br />     strcat(AskResp.pData1, ":");<br />    }<br />   }<br />   strcpy(AskResp.pData2, Ask.pData);<br />   for(i=0; i&lt;m_Connectnum; i++)<br />   {<br />    send(m_ClientInfo[i].Client_Socket, (char*)&amp;AskResp, sizeof(AskResp), 0);<br />   }</p>
										<p>   break;<br />  case CMD_GOODBYE:<br />   closesocket(pSocket);<br />   break;<br />  default:<br />   break;<br />  }<br /> }</p>
										<p> return TRUE;<br />}<br />BOOL CServerDlg::OnSend(SOCKET pSocket)<br />{<br /> return TRUE;<br />}</p>
										<p>void CServerDlg::OnOK() <br />{<br /> closesocket(m_ListenSocket);<br /> WSACleanup();<br /> CDialog::OnOK();<br />}</p>
										<p>void CServerDlg::OnTimer(UINT nIDEvent) <br />{<br /> CTime m_current_time = CTime::GetCurrentTime();<br /> CTimeSpan logintimes;<br /> CString login_times;<br /> CString networkid; <a href="file://列/"><font color="#336699">file://列</font></a>表框的网络ID项<br /> <br /> for(int i=0; i&lt;m_Connectnum; i++)<br /> {<br />  logintimes = m_current_time - m_ClientInfo[i].Login_Time;<br />  login_times.Format("%d小时%d分%d秒", logintimes.GetHours(),<br />                                    logintimes.GetMinutes(),<br />            logintimes.GetSeconds());<br /> <br />  pList-&gt;SetItemText (i,4,login_times);<br /> }</p>
										<p> CDialog::OnTimer(nIDEvent);<br />}</p>
										<p>
												<br />// serverDlg.h : header file<br />//</p>
										<p>#if !defined(AFX_SERVERDLG_H__B0AA0367_C1F4_11D4_AB1C_0080C8D6FEA5__INCLUDED_)<br />#define AFX_SERVERDLG_H__B0AA0367_C1F4_11D4_AB1C_0080C8D6FEA5__INCLUDED_</p>
										<p>#if _MSC_VER &gt; 1000<br />#pragma once<br />#endif // _MSC_VER &gt; 1000</p>
										<p>#include "global.h"</p>
										<p>/////////////////////////////////////////////////////////////////////////////<br />// CServerDlg dialog</p>
										<p>class CServerDlg : public CDialog<br />{<br /><a href="file://全/"><font color="#336699">file://全</font></a>局函数<br />    friend UINT KernelWorkThread(LPVOID pParam);<br />// Construction<br />public:<br /> CListCtrl* pList; <a href="file://客/"><font color="#336699">file://客</font></a>户端在线信息列表框对象<br /> CImageList m_imagelist; </p>
										<p> SOCKET m_ListenSocket; <a href="file://用/"><font color="#336699">file://用</font></a>于监听端口的套接字<br /> client_info m_ClientInfo[MAX_CLIENT_NUM]; <a href="file://保/"><font color="#336699">file://保</font></a>存在线客户端信息的结构体数组<br /> sockaddr_in clientaddr; <a href="file://保/"><font color="#336699">file://保</font></a>存发起连接的客户端地址<br /> int m_Connectnum; <a href="file://在/"><font color="#336699">file://在</font></a>线客户端个数<br /> int m_NetworkID;  <a href="file://返/"><font color="#336699">file://返</font></a>回给客户端的网络ID号</p>
										<p> BOOL OnSend(SOCKET pSocket);    <a href="file://发/"><font color="#336699">file://发</font></a>送数据网络事件的响应函数 <br /> BOOL OnReceive(SOCKET pSocket); <a href="file://接/"><font color="#336699">file://接</font></a>收数据网络事件的响应函数 <br /> BOOL OnClose(SOCKET pSocket);   <a href="file://关/"><font color="#336699">file://关</font></a>闭套接字网络事件的响应函数</p>
										<p> CServerDlg(CWnd* pParent = NULL); // standard constructor</p>
										<p>// Dialog Data<br /> <a href="file://{{/"><font color="#336699">file://{{</font></a>AFX_DATA(CServerDlg)<br /> enum { IDD = IDD_SERVER_DIALOG };<br />  // NOTE: the ClassWizard will add data members here<br /> <a href="file://}}/"><font color="#336699">file://}}</font></a>AFX_DATA</p>
										<p> // ClassWizard generated virtual function overrides<br /> <a href="file://{{/"><font color="#336699">file://{{</font></a>AFX_VIRTUAL(CServerDlg)<br /> protected:<br /> virtual void DoDataExchange(CDataExchange* pDX); // DDX/DDV support<br /> <a href="file://}}/"><font color="#336699">file://}}</font></a>AFX_VIRTUAL</p>
										<p>// Implementation<br />protected:<br /> HICON m_hIcon;</p>
										<p> // Generated message map functions<br /> <a href="file://{{/"><font color="#336699">file://{{</font></a>AFX_MSG(CServerDlg)<br /> virtual BOOL OnInitDialog();<br /> afx_msg void OnSysCommand(UINT nID, LPARAM lParam);<br /> afx_msg void OnPaint();<br /> afx_msg HCURSOR OnQueryDragIcon();<br /> virtual void OnOK();<br /> afx_msg void OnTimer(UINT nIDEvent);<br /> <a href="file://}}/"><font color="#336699">file://}}</font></a>AFX_MSG<br /> DECLARE_MESSAGE_MAP()<br />};</p>
								</td>
						</tr>
				</tbody>
		</table>
<img src ="http://www.cppblog.com/jjbird/aggbug/6333.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/jjbird/" target="_blank">奇奇</a> 2006-04-26 17:30 <a href="http://www.cppblog.com/jjbird/articles/6333.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>setsockopt()选项改善程序的健壮性</title><link>http://www.cppblog.com/jjbird/articles/6221.html</link><dc:creator>奇奇</dc:creator><author>奇奇</author><pubDate>Tue, 25 Apr 2006 01:14:00 GMT</pubDate><guid>http://www.cppblog.com/jjbird/articles/6221.html</guid><wfw:comment>http://www.cppblog.com/jjbird/comments/6221.html</wfw:comment><comments>http://www.cppblog.com/jjbird/articles/6221.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/jjbird/comments/commentRss/6221.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/jjbird/services/trackbacks/6221.html</trackback:ping><description><![CDATA[写出我在网络编程中的一点心得体会，希望对他(^_^也对大家)有帮助:<br />1. 如果在已经处于 ESTABLISHED状态下的socket(一般由端口号和标志符区分）调用<br />closesocket（一般不会立即关闭而经历TIME_WAIT的过程）后想继续重用该socket：<br />BOOL bReuseaddr=TRUE;<br />setsockopt(s,SOL_SOCKET ,SO_REUSEADDR,(const char*)&amp;bReuseaddr,sizeof(BOOL));<br />2. 如果要已经处于连接状态的soket在调用closesocket后强制关闭，不经历<br />TIME_WAIT的过程：<br />BOOL bDontLinger = FALSE; <br />setsockopt(s,SOL_SOCKET,SO_DONTLINGER,(const char*)&amp;bDontLinger,sizeof(BOOL));<br />3.在send(),recv()过程中有时由于网络状况等原因，发收不能预期进行,而设置收发时限：<br />int nNetTimeout=1000;//1秒<br />//发送时限<br />setsockopt(socket，SOL_S0CKET,SO_SNDTIMEO，(char *)&amp;nNetTimeout,sizeof(int));<br />//接收时限<br />setsockopt(socket，SOL_S0CKET,SO_RCVTIMEO，(char *)&amp;nNetTimeout,sizeof(int));<br />4.在send()的时候，返回的是实际发送出去的字节(同步)或发送到socket缓冲区的字节<br />(异步);系统默认的状态发送和接收一次为8688字节(约为8.5K)；在实际的过程中发送数据<br />和接收数据量比较大，可以设置socket缓冲区，而避免了send(),recv()不断的循环收发：<br />// 接收缓冲区<br />int nRecvBuf=32*1024;//设置为32K<br />setsockopt(s,SOL_SOCKET,SO_RCVBUF,(const char*)&amp;nRecvBuf,sizeof(int));<br />//发送缓冲区<br />int nSendBuf=32*1024;//设置为32K<br />setsockopt(s,SOL_SOCKET,SO_SNDBUF,(const char*)&amp;nSendBuf,sizeof(int));<br />5. 如果在发送数据的时，希望不经历由系统缓冲区到socket缓冲区的拷贝而影响<br />程序的性能：<br />int nZero=0;<br />setsockopt(socket，SOL_S0CKET,SO_SNDBUF，(char *)&amp;nZero,sizeof(nZero));<br />6.同上在recv()完成上述功能(默认情况是将socket缓冲区的内容拷贝到系统缓冲区)：<br />int nZero=0;<br />setsockopt(socket，SOL_S0CKET,SO_RCVBUF，(char *)&amp;nZero,sizeof(int));<br />7.一般在发送UDP数据报的时候，希望该socket发送的数据具有广播特性：<br />BOOL bBroadcast=TRUE; <br />setsockopt(s,SOL_SOCKET,SO_BROADCAST,(const char*)&amp;bBroadcast,sizeof(BOOL));<br />8.在client连接服务器过程中，如果处于非阻塞模式下的socket在connect()的过程中可<br />以设置connect()延时,直到accpet()被呼叫(本函数设置只有在非阻塞的过程中有显著的<br />作用，在阻塞的函数调用中作用不大)<br />BOOL bConditionalAccept=TRUE;<br />setsockopt(s,SOL_SOCKET,SO_CONDITIONAL_ACCEPT,(const char*)&amp;bConditionalAccept,sizeof(BOOL));<br />9.如果在发送数据的过程中(send()没有完成，还有数据没发送)而调用了closesocket(),以前我们<br />一般采取的措施是"从容关闭"shutdown(s,SD_BOTH),但是数据是肯定丢失了，如何设置让程序满足具体<br />应用的要求(即让没发完的数据发送出去后在关闭socket)？<br />struct linger {<br />u_short l_onoff;<br />u_short l_linger;<br />};<br />linger m_sLinger;<br />m_sLinger.l_onoff=1;//(在closesocket()调用,但是还有数据没发送完毕的时候容许逗留)<br />// 如果m_sLinger.l_onoff=0;则功能和2.)作用相同;<br />m_sLinger.l_linger=5;//(容许逗留的时间为5秒)<br />setsockopt(s,SOL_SOCKET,SO_LINGER,(const char*)&amp;m_sLinger,sizeof(linger));<br />Note:1.在设置了逗留延时，用于一个非阻塞的socket是作用不大的，最好不用;<br />2.如果想要程序不经历SO_LINGER需要设置SO_DONTLINGER，或者设置l_onoff=0；<br />10.还一个用的比较少的是在SDI或者是Dialog的程序中，可以记录socket的调试信息：<br />(前不久做过这个函数的测试，调式信息可以保存，包括socket建立时候的参数,采用的<br />具体协议，以及出错的代码都可以记录下来）<br />BOOL bDebug=TRUE;<br />setsockopt(s,SOL_SOCKET,SO_DEBUG,(const char*)&amp;bDebug,sizeof(BOOL));<br />11.附加：往往通过setsockopt()设置了缓冲区大小，但还不能满足数据的传输需求，<br />我的习惯是自己写个处理网络缓冲的类，动态分配内存;下面我将这个类写出，希望对<br />初学者有所帮助：<br /><br />//仿照String 改写而成<br />//==============================================================================<br />// 二进制数据，主要用于收发网络缓冲区的数据<br />// CNetIOBuffer 以 MFC 类 CString 的源代码作为蓝本改写而成，用法与 CString 类似，<br />// 但是 CNetIOBuffer 中存放的是纯粹的二进制数据，'\0' 并不作为它的结束标志。<br />// 其数据长度可以通过 GetLength() 获得，缓冲区地址可以通过运算符 LPBYTE 获得。<br /><br /><br />//==============================================================================<br />// Copyright (c) All-Vision Corporation. All rights reserved.<br />// Module: NetObject<br />// File: SimpleIOBuffer.h<br />// Author: gdy119<br />// Email : <a href="mailto:8751webmaster@126.com"><font color="#22229c">8751webmaster@126.com</font></a><br />// Date: 2004.11.26<br />//==============================================================================<br />// NetIOBuffer.h<br />#ifndef _NETIOBUFFER_H<br />#define _NETIOBUFFER_H<br />//=============================================================================<br />#define MAX_BUFFER_LENGTH 1024*1024<br />//=============================================================================<br />//主要用来处理网络缓冲的数据<br />class CNetIOBuffer <br />{<br />protected:<br />LPBYTE m_pbinData;<br />int m_nLength;<br />int m_nTotalLength;<br />CRITICAL_SECTIONm_cs;<br />void Initvalibers();<br />public:<br />CNetIOBuffer();<br />CNetIOBuffer(const LPBYTE lbbyte, int nLength);<br />CNetIOBuffer(const CNetIOBuffer&amp;binarySrc);<br />virtual ~CNetIOBuffer();<br />//=============================================================================<br />BOOL CopyData(const LPBYTE lbbyte, int nLength);<br />BOOL ConcatData(const LPBYTE lbbyte, int nLength);<br />void ResetIoBuffer();<br />int GetLength() const;<br />BOOL SetLength(int nLen);<br />LPBYTE GetCurPos();<br />int GetRemainLen();<br />BOOL IsEmpty() const;<br />operator LPBYTE() const;<br />static GetMaxLength() { return MAX_BUFFER_LENGTH; }<br />const CNetIOBuffer&amp; operator=(const CNetIOBuffer&amp; buffSrc);<br />};<br />#endif // <br />// NetOBuffer.cpp: implementation of the CNetIOBuffer class.<br />//======================================================================<br />#include "stdafx.h"<br />#include "NetIOBuffer.h"<br />//======================================================================<br />//=======================================================================<br />// Construction/Destruction<br />CNetIOBuffer::CNetIOBuffer()<br />{<br />Initvalibers();<br /><br />}<br />CNetIOBuffer::CNetIOBuffer(const LPBYTE lbbyte, int nLength)<br />{<br />Initvalibers();<br />CopyData(lbbyte, nLength);<br />}<br />CNetIOBuffer::~CNetIOBuffer()<br />{<br />delete []m_pbinData;<br />m_pbinData=NULL;<br />DeleteCriticalSection(&amp;m_cs);<br /><br />}<br />CNetIOBuffer::CNetIOBuffer(const CNetIOBuffer&amp;binarySrc)<br />{<br /><br />Initvalibers();<br />CopyData(binarySrc,binarySrc.GetLength());<br /><br />}<br />void CNetIOBuffer::Initvalibers()<br />{<br /><br />m_pbinData = NULL;<br />m_nLength = 0;<br />m_nTotalLength = MAX_BUFFER_LENGTH;<br />if(m_pbinData==NULL)<br />{<br />m_pbinData=new BYTE[m_nTotalLength];<br />ASSERT(m_pbinData!=NULL);<br />}<br />InitializeCriticalSection(&amp;m_cs);<br />}<br />void CNetIOBuffer::ResetIoBuffer()<br />{<br />EnterCriticalSection(&amp;m_cs);<br />m_nLength = 0;<br />memset(m_pbinData,0,m_nTotalLength);<br />LeaveCriticalSection(&amp;m_cs);<br />}<br /><br />BOOL CNetIOBuffer::CopyData(const LPBYTE lbbyte, int nLength)<br />{<br />if( nLength &gt; MAX_BUFFER_LENGTH )<br />return FALSE;<br /><br />ResetIoBuffer();<br />EnterCriticalSection(&amp;m_cs);<br />memcpy(m_pbinData, lbbyte, nLength );<br />m_nLength = nLength;<br />LeaveCriticalSection(&amp;m_cs);<br /><br />return TRUE;<br />}<br /><br />BOOL CNetIOBuffer::ConcatData(const LPBYTE lbbyte, int nLength)<br />{<br />if( m_nLength + nLength &gt; MAX_BUFFER_LENGTH )<br />return FALSE;<br /><br />EnterCriticalSection(&amp;m_cs);<br />memcpy(m_pbinData+m_nLength, lbbyte, nLength );<br />m_nLength += nLength;<br />LeaveCriticalSection(&amp;m_cs);<br /><br />return TRUE;<br />}<br /><br />int CNetIOBuffer::GetLength() const<br />{<br />return m_nLength;<br />}<br /><br />BOOL CNetIOBuffer::SetLength(int nLen)<br />{<br />if( nLen &gt; MAX_BUFFER_LENGTH )<br />return FALSE;<br /><br />EnterCriticalSection(&amp;m_cs);<br />m_nLength = nLen;<br />LeaveCriticalSection(&amp;m_cs);<br /><br />return TRUE;<br />}<br /><br />LPBYTE CNetIOBuffer::GetCurPos()<br />{<br /><br />if( m_nLength &lt; MAX_BUFFER_LENGTH )<br /><br />return (m_pbinData+m_nLength);<br /><br />else<br />return NULL;<br />}<br /><br />CNetIOBuffer:: operator LPBYTE() const<br />{<br />return m_pbinData;<br />}<br /><br />int CNetIOBuffer::GetRemainLen()<br />{<br /><br />return MAX_BUFFER_LENGTH - m_nLength;<br /><br />}<br />BOOL CNetIOBuffer::IsEmpty() const<br />{<br />return m_nLength == 0;<br />}<br /><br />const CNetIOBuffer&amp; CNetIOBuffer:: operator=(const CNetIOBuffer&amp; buffSrc)<br />{<br />if(&amp;buffSrc!=this)<br />{<br />CopyData(buffSrc, buffSrc.GetLength());<br /><br />}<br />return *this;<br /><br />}<br /><br /><br /><br />12.发送数据时候一般是系统缓冲区满以后才发送，现在设置为只要系统<br />缓冲区有数据就立刻发送： <br />BOOL bNodelay=TRUE;<br />SetSockOpt(s,IPPROTO_TCP,TCP_NODELAY,(const char*)&amp;bNodelayt,sizeof(BOOL));<br /><br />附加：<br />setoptsock（）这个函数 设置成端口复用的时候，很容易对一些没有进行单独bind模式的程序造成危害。<br />比如old的 ping icmp door，简单的sniffer后，收到包，然后设置setoptsock bind web服务，然后建立个cmd进程 bind再80端口。 <img src ="http://www.cppblog.com/jjbird/aggbug/6221.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/jjbird/" target="_blank">奇奇</a> 2006-04-25 09:14 <a href="http://www.cppblog.com/jjbird/articles/6221.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>回调机制</title><link>http://www.cppblog.com/jjbird/articles/3364.html</link><dc:creator>奇奇</dc:creator><author>奇奇</author><pubDate>Tue, 21 Feb 2006 02:26:00 GMT</pubDate><guid>http://www.cppblog.com/jjbird/articles/3364.html</guid><wfw:comment>http://www.cppblog.com/jjbird/comments/3364.html</wfw:comment><comments>http://www.cppblog.com/jjbird/articles/3364.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/jjbird/comments/commentRss/3364.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/jjbird/services/trackbacks/3364.html</trackback:ping><description><![CDATA[<BLOCKQUOTE>软件模块之间总是存在着一定的接口，从调用方式上，可以把他们分为三类：同步调用、回调和异步调用。同步调用是一种阻塞式调用，调用方要等待对方执行完毕才返回，它是一种单向调用；回调是一种双向调用模式，也就是说，被调用方在接口被调用时也会调用对方的接口；异步调用是一种类似消息或事件的机制，不过它的调用方向刚好相反，接口的服务在收到某种讯息或发生某种事件时，会主动通知客户方（即调用客户方的接口）。回调和异步调用的关系非常紧密，通常我们使用回调来实现异步消息的注册，通过异步调用来实现消息的通知。同步调用是三者当中最简单的，而回调又常常是异步调用的基础，因此，下面我们着重讨论回调机制在不同软件架构中的实现。</BLOCKQUOTE>
<P><A name=1><SPAN class=atitle2>1 什么是回调</SPAN></A><BR>软件模块之间总是存在着一定的接口，从调用方式上，可以把他们分为三类：同步调用、回调和异步调用。同步调用是一种阻塞式调用，调用方要等待对方执行完毕才返回，它是一种单向调用；回调是一种双向调用模式，也就是说，被调用方在接口被调用时也会调用对方的接口；异步调用是一种类似消息或事件的机制，不过它的调用方向刚好相反，接口的服务在收到某种讯息或发生某种事件时，会主动通知客户方（即调用客户方的接口）。回调和异步调用的关系非常紧密，通常我们使用回调来实现异步消息的注册，通过异步调用来实现消息的通知。同步调用是三者当中最简单的，而回调又常常是异步调用的基础，因此，下面我们着重讨论回调机制在不同软件架构中的实现。 </P>
<P><A name=N10050><B></B></A><BR><IMG alt="" src="http://www-128.ibm.com/developerworks/cn/linux/l-callback/images/image001.gif" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:dw="http://www.ibm.com/developerworks/"> </P>
<P>对于不同类型的语言（如结构化语言和对象语言）、平台（Win32、JDK）或构架（CORBA、DCOM、WebService），客户和服务的交互除了同步方式以外，都需要具备一定的异步通知机制，让服务方（或接口提供方）在某些情况下能够主动通知客户，而回调是实现异步的一个最简捷的途径。 </P>
<P>对于一般的结构化语言，可以通过回调函数来实现回调。回调函数也是一个函数或过程，不过它是一个由调用方自己实现，供被调用方使用的特殊函数。 </P>
<P>在面向对象的语言中，回调则是通过接口或抽象类来实现的，我们把实现这种接口的类成为回调类，回调类的对象成为回调对象。对于象C++或Object Pascal这些兼容了过程特性的对象语言，不仅提供了回调对象、回调方法等特性，也能兼容过程语言的回调函数机制。 </P>
<P>Windows平台的消息机制也可以看作是回调的一种应用，我们通过系统提供的接口注册消息处理函数（即回调函数），从而实现接收、处理消息的目的。由于Windows平台的API是用C语言来构建的，我们可以认为它也是回调函数的一个特例。 </P>
<P>对于分布式组件代理体系CORBA，异步处理有多种方式，如回调、事件服务、通知服务等。事件服务和通知服务是CORBA用来处理异步消息的标准服务，他们主要负责消息的处理、派发、维护等工作。对一些简单的异步处理过程，我们可以通过回调机制来实现。 </P>
<P>下面我们集中比较具有代表性的语言（C、Object Pascal）和架构（CORBA）来分析回调的实现方式、具体作用等。 </P>
<P><A name=2><SPAN class=atitle2>2 过程语言中的回调（C）</SPAN></A><BR></P>
<P><A name=N10072><SPAN class=atitle3>2.1 函数指针</SPAN></A><BR>回调在C语言中是通过函数指针来实现的,通过将回调函数的地址传给被调函数从而实现回调。因此，要实现回调，必须首先定义函数指针，请看下面的例子： </P>
<TABLE cellSpacing=0 cellPadding=5 width="100%" bgColor=#cccccc border=1>
<TBODY>
<TR>
<TD><PRE><CODE>
void Func(char *s)；// 函数原型
void (*pFunc) (char *);//函数指针
</CODE></PRE></TD></TR></TBODY></TABLE>
<P>可以看出，函数的定义和函数指针的定义非常类似。 </P>
<P>一般的化，为了简化函数指针类型的变量定义，提高程序的可读性，我们需要把函数指针类型自定义一下。 </P>
<TABLE cellSpacing=0 cellPadding=5 width="100%" bgColor=#cccccc border=1>
<TBODY>
<TR>
<TD><PRE><CODE>
typedef void(*pcb)(char *);
</CODE></PRE></TD></TR></TBODY></TABLE>
<P>回调函数可以象普通函数一样被程序调用，但是只有它被当作参数传递给被调函数时才能称作回调函数。 </P>
<P>被调函数的例子：</P>
<TABLE cellSpacing=0 cellPadding=5 width="100%" bgColor=#cccccc border=1>
<TBODY>
<TR>
<TD><PRE><CODE>
void GetCallBack(pcb callback)
{
/*do something*/
}
用户在调用上面的函数时，需要自己实现一个pcb类型的回调函数：
void fCallback(char *s) 
{
/* do something */
} 
然后，就可以直接把fCallback当作一个变量传递给GetCallBack,
GetCallBack（fCallback）;
</CODE></PRE></TD></TR></TBODY></TABLE>
<P>如果赋了不同的值给该参数，那么调用者将调用不同地址的函数。赋值可以发生在运行时，这样使你能实现动态绑定。 </P>
<P><A name=2.2><SPAN class=atitle2>2.2 参数传递规则</SPAN></A><BR>到目前为止，我们只讨论了函数指针及回调而没有去注意ANSI C/C++的编译器规范。许多编译器有几种调用规范。如在Visual C++中，可以在函数类型前加_cdecl，_stdcall或者_pascal来表示其调用规范（默认为_cdecl）。C++ Builder也支持_fastcall调用规范。调用规范影响编译器产生的给定函数名，参数传递的顺序（从右到左或从左到右），堆栈清理责任（调用者或者被调用者）以及参数传递机制（堆栈，CPU寄存器等）。 </P>
<P>将调用规范看成是函数类型的一部分是很重要的；不能用不兼容的调用规范将地址赋值给函数指针。例如： </P>
<TABLE cellSpacing=0 cellPadding=5 width="100%" bgColor=#cccccc border=1>
<TBODY>
<TR>
<TD><PRE><CODE>
// 被调用函数是以int为参数，以int为返回值
__stdcall int callee(int); 

// 调用函数以函数指针为参数
void caller( __cdecl int(*ptr)(int)); 

// 在p中企图存储被调用函数地址的非法操作
__cdecl int(*p)(int) = callee; // 出错
</CODE></PRE></TD></TR></TBODY></TABLE>
<P>指针p和callee()的类型不兼容，因为它们有不同的调用规范。因此不能将被调用者的地址赋值给指针p，尽管两者有相同的返回值和参数列 </P>
<P><A name=N100A9><SPAN class=atitle3>2.3 应用举例</SPAN></A><BR>C语言的标准库函数中很多地方就采用了回调函数来让用户定制处理过程。如常用的快速排序函数、二分搜索函数等。 </P>
<P>快速排序函数原型：</P>
<TABLE cellSpacing=0 cellPadding=5 width="100%" bgColor=#cccccc border=1>
<TBODY>
<TR>
<TD><PRE><CODE>
void qsort(void *base, size_t nelem, size_t width, int (_USERENTRY *fcmp)(const void *, const void *));
二分搜索函数原型：
void *bsearch(const void *key, const void *base, size_t nelem,
				 size_t width, int (_USERENTRY *fcmp)(const void *, const void *));
</CODE></PRE></TD></TR></TBODY></TABLE>
<P>其中fcmp就是一个回调函数的变量。 </P>
<P>下面给出一个具体的例子： </P>
<TABLE cellSpacing=0 cellPadding=5 width="100%" bgColor=#cccccc border=1>
<TBODY>
<TR>
<TD><PRE><CODE>
#include &lt;stdio.h&gt;
#include &lt;stdlib.h&gt;

int sort_function( const void *a, const void *b);
int list[5] = { 54, 21, 11, 67, 22 };

int main(void)
{
   int  x;

   qsort((void *)list, 5, sizeof(list[0]), sort_function);
   for (x = 0; x &lt; 5; x++)
      printf("%i\n", list[x]);
   return 0;
}

int sort_function( const void *a, const void *b)
{
   return *(int*)a-*(int*)b;
}
</CODE></PRE></TD></TR></TBODY></TABLE>
<P>2.4 面向对象语言中的回调（Delphi） </P>
<P>Dephi与C++一样，为了保持与过程语言Pascal的兼容性，它在引入面向对象机制的同时，保留了以前的结构化特性。因此，对回调的实现，也有两种截然不同的模式，一种是结构化的函数回调模式，一种是面向对象的接口模式。 </P>
<P><B xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:dw="http://www.ibm.com/developerworks/">2.4.1 回调函数</B> </P>
<P>回调函数类型定义： </P>
<TABLE cellSpacing=0 cellPadding=5 width="100%" bgColor=#cccccc border=1>
<TBODY>
<TR>
<TD><PRE><CODE>
type
   TCalcFunc=function (a:integer;b:integer):integer;
</CODE></PRE></TD></TR></TBODY></TABLE>
<P>按照回调函数的格式自定义函数的实现，如</P>
<TABLE cellSpacing=0 cellPadding=5 width="100%" bgColor=#cccccc border=1>
<TBODY>
<TR>
<TD><PRE><CODE>
function Add(a:integer;b:integer):integer
begin
  result:=a+b;
end;
function Sub(a:integer;b:integer):integer
begin
  result:=a-b;
end;
</CODE></PRE></TD></TR></TBODY></TABLE>
<P>回调的使用</P>
<TABLE cellSpacing=0 cellPadding=5 width="100%" bgColor=#cccccc border=1>
<TBODY>
<TR>
<TD><PRE><CODE>
function Calc(calc:TcalcFunc;a:integer;b:integer):integer
</CODE></PRE></TD></TR></TBODY></TABLE>
<P>下面，我们就可以在我们的程序里按照需要调用这两个函数了</P>
<TABLE cellSpacing=0 cellPadding=5 width="100%" bgColor=#cccccc border=1>
<TBODY>
<TR>
<TD><PRE><CODE>
c:=calc(add,a,b);//c=a+b
c:=calc(sub,a,b);//c=a-b
</CODE></PRE></TD></TR></TBODY></TABLE>
<P><B xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:dw="http://www.ibm.com/developerworks/">2.4.2 回调对象</B> </P>
<P>什么叫回调对象呢，它具体用在哪些场合？首先，让我们把它与回调函数对比一下，回调函数是一个定义了函数的原型，函数体则交由第三方来实现的一种动态应用模式。要实现一个回调函数，我们必须明确知道几点：该函数需要那些参数，返回什么类型的值。同样，一个回调对象也是一个定义了对象接口，但是没有具体实现的抽象类（即接口）。要实现一个回调对象，我们必须知道：它需要实现哪些方法，每个方法中有哪些参数，该方法需要放回什么值。 </P>
<P>因此，在回调对象这种应用模式中，我们会用到接口。接口可以理解成一个定义好了但是没有实现的类，它只能通过继承的方式被别的类实现。Delphi中的接口和COM接口类似，所有的接口都继承与IInterface（等同于IUnknow），并且要实现三个基本的方法QueryInterface, _AddRef, 和_Release。 </P>
<UL xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:dw="http://www.ibm.com/developerworks/">
<LI>定义一个接口 <BR>
<TABLE cellSpacing=0 cellPadding=5 width="80%" bgColor=#cccccc border=1>
<TBODY>
<TR>
<TD><PRE><CODE>
type IShape=interface(IInterface)
	procedure Draw;
end
</CODE></PRE></TD></TR></TBODY></TABLE>
<LI>实现回调类 <BR>
<TABLE cellSpacing=0 cellPadding=5 width="80%" bgColor=#cccccc border=1>
<TBODY>
<TR>
<TD><PRE><CODE>
type TRect=class(TObject,IShape)
	protected
      function QueryInterface(const IID: TGUID; out Obj): HResult; stdcall;
      function _AddRef: Integer; stdcall;
function _Release: Integer; stdcall;
    public
	  procedure Draw;
end;

type TRound=class(TObject,IShape)
	protected
      function QueryInterface(const IID: TGUID; out Obj): HResult; stdcall;
      function _AddRef: Integer; stdcall;
function _Release: Integer; stdcall;
    public
	  procedure Draw;
end;
</CODE></PRE></TD></TR></TBODY></TABLE>
<LI>使用回调对象 <BR>
<TABLE cellSpacing=0 cellPadding=5 width="80%" bgColor=#cccccc border=1>
<TBODY>
<TR>
<TD><PRE><CODE>
procedure MyDraw(shape:IShape);
var 
shape:IShape;
begin
shape.Draw; 
end;
</CODE></PRE></TD></TR></TBODY></TABLE></LI></UL>
<P>如果传入的对象为TRect，那么画矩形；如果为TRound，那么就为圆形。用户也可以按照自己的意图来实现IShape接口，画出自己的图形： </P>
<TABLE cellSpacing=0 cellPadding=5 width="100%" bgColor=#cccccc border=1>
<TBODY>
<TR>
<TD><PRE><CODE>
MyDraw(Trect.Create);
MyDraw(Tround.Create);
</CODE></PRE></TD></TR></TBODY></TABLE>
<P><B xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:dw="http://www.ibm.com/developerworks/">2.4.3 回调方法</B> </P>
<P>回调方法(Callback Method)可以看作是回调对象的一部分，Delphi对windows消息的封装就采用了回调方法这个概念。在有些场合，我们不需要按照给定的要求实现整个对象，而只要实现其中的一个方法就可以了，这是我们就会用到回调方法。 </P>
<P>回调方法的定义如下：</P>
<TABLE cellSpacing=0 cellPadding=5 width="100%" bgColor=#cccccc border=1>
<TBODY>
<TR>
<TD><PRE><CODE>
TNotifyEvent = procedure(Sender: TObject) of object; 
TMyEvent=procedure(Sender:Tobject;EventId:Integer) of object;
</CODE></PRE></TD></TR></TBODY></TABLE>
<P>TNotifyEvent 是Delphi中最常用的回调方法，窗体、控件的很多事件，如单击事件、关闭事件等都是采用了TnotifyEvent。回调方法的变量一般通过事件属性的方式来定义，如TCustomForm的创建事件的定义： </P>
<TABLE cellSpacing=0 cellPadding=5 width="100%" bgColor=#cccccc border=1>
<TBODY>
<TR>
<TD><PRE><CODE>
property OnCreate: TNotifyEvent read FOnCreate write FOnCreate stored IsForm;
</CODE></PRE></TD></TR></TBODY></TABLE>
<P>我们通过给事件属性变量赋值就可以定制事件处理器。</P>
<P>用户定义对象（包含回调方法的对象）：</P>
<TABLE cellSpacing=0 cellPadding=5 width="100%" bgColor=#cccccc border=1>
<TBODY>
<TR>
<TD><PRE><CODE>
type TCallback=Class
    procedure ClickFunc(sender:TObject);
end;
procedure Tcallback.ClickFunc(sender:TObject);
begin
  showmessage('the caller is clicked!');
end;
</CODE></PRE></TD></TR></TBODY></TABLE>
<P>窗体对象：</P>
<TABLE cellSpacing=0 cellPadding=5 width="100%" bgColor=#cccccc border=1>
<TBODY>
<TR>
<TD><PRE><CODE>
type TCustomFrm=class(TForm)
  public
	procedure RegisterClickFunc(cb:procedure(sender:Tobject) of object);
end;

procedure TcustomFrm..RegisterClickFunc(cb:TNotifyEvent);
begin
  self.OnClick=cb;
end;
</CODE></PRE></TD></TR></TBODY></TABLE>
<P>使用方法：</P>
<TABLE cellSpacing=0 cellPadding=5 width="100%" bgColor=#cccccc border=1>
<TBODY>
<TR>
<TD><PRE><CODE>
var
  frm:TcustomFrm;
begin
  frm:=TcustomFrm.Create(Application);
  frm.RegisterClickFunc(Tcallback.Create().ClickFunc);
end;
</CODE></PRE></TD></TR></TBODY></TABLE>
<P><A name=3><SPAN class=atitle2>3 回调在分布式计算中的应用（CORBA）</SPAN></A><BR></P>
<P><A name=N10154><SPAN class=atitle3>3.1 回调接口模型</SPAN></A><BR>CORBA的消息传递机制有很多种，比如回调接口、事件服务和通知服务等。回调接口的原理很简单，CORBA客户和服务器都具有双重角色，即充当服务器也是客户客户。 </P>
<P>回调接口的反向调用与正向调用往往是同时进行的，如果服务端多次调用该回调接口，那么这个回调接口就变成异步接口了。因此，回调接口在CORBA中常常充当事件注册的用途，客户端调用该注册函数时，客户函数就是回调函数，在此后的调用中，由于不需要客户端的主动参与，该函数就是实现了一种异步机制。 </P>
<P>从CORBA规范我们知道，一个CORBA接口在服务端和客户端有不同的表现形式，在客户端一般使用桩（Stub）文件，服务端则用到框架（Skeleton）文件，接口的规格采用IDL来定义。而回调函数的引入，使得服务端和客户端都需要实现一定的桩和框架。下面是回调接口的实现模型： </P>
<P><A name=N10165><B>3.1.1 范例</B></A><BR><IMG alt="" src="http://www-128.ibm.com/developerworks/cn/linux/l-callback/images/image002.gif" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:dw="http://www.ibm.com/developerworks/"> </P>
<P>下面给出了一个使用回调的接口文件，服务端需要实现Server接口的框架，客户端需要实现CallBack的框架：</P>
<TABLE cellSpacing=0 cellPadding=5 width="100%" bgColor=#cccccc border=1>
<TBODY>
<TR>
<TD><PRE><CODE>
module cb
{
	interface CallBack;
	interface Server;

interface CallBack 
{
    	void OnEvent(in long Source,in long msg);
};
  	interface Server 
{
    	long RegisterCB(in CallBack cb);
		void UnRegisterCB(in long hCb);
};
};
</CODE></PRE></TD></TR></TBODY></TABLE>
<P>客户端首先通过同步方式调用服务端的接口RegistCB，用来注册回调接口CallBack。服务端收到该请求以后，就会保留该接口引用，如果发生某种事件需要向客户端通知的时候就通过该引用调用客户方的OnEvent函数，以便对方及时处理。&nbsp;&nbsp;</P><img src ="http://www.cppblog.com/jjbird/aggbug/3364.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/jjbird/" target="_blank">奇奇</a> 2006-02-21 10:26 <a href="http://www.cppblog.com/jjbird/articles/3364.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>GRID中的事件</title><link>http://www.cppblog.com/jjbird/articles/3271.html</link><dc:creator>奇奇</dc:creator><author>奇奇</author><pubDate>Wed, 15 Feb 2006 09:21:00 GMT</pubDate><guid>http://www.cppblog.com/jjbird/articles/3271.html</guid><wfw:comment>http://www.cppblog.com/jjbird/comments/3271.html</wfw:comment><comments>http://www.cppblog.com/jjbird/articles/3271.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/jjbird/comments/commentRss/3271.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/jjbird/services/trackbacks/3271.html</trackback:ping><description><![CDATA[BEGIN_EVENTSINK_MAP(CDlgUserManager, CDialog)<BR>//{{AFX_EVENTSINK_MAP(CDlgUserManager)<BR>&nbsp;ON_EVENT(CDlgUserManager, IDC_USER_MANAGER_GRID, -600 /* Click */, OnClickUserManagerGrid, VTS_NONE)<BR>&nbsp;ON_EVENT(CDlgUserManager, IDC_USER_MANAGER_GRID, -601 /* DblClick */, OnDblClickUserManagerGrid, VTS_NONE)<BR>&nbsp;ON_EVENT(CDlgUserManager, IDC_USER_MANAGER_GRID, -602 /* KeyDown */, OnKeyDownUserManagerGrid, VTS_PI2 VTS_I2)<BR>&nbsp;ON_EVENT(CDlgUserManager, IDC_USER_MANAGER_GRID, 74 /* Compare */, OnCompareUserManagerGrid, VTS_I4 VTS_I4 VTS_PI2)<BR>&nbsp;ON_EVENT(CDlgUserManager, IDC_USER_MANAGER_GRID, -603 /* KeyPress */, OnKeyPressUserManagerGrid, VTS_PI2)<BR>&nbsp;ON_EVENT(CDlgUserManager, IDC_USER_MANAGER_GRID, -605 /* MouseDown */, OnMouseDownUserManagerGrid, VTS_I2 VTS_I2 VTS_I4 VTS_I4)<BR>}}//AFX_EVENTSINK_MAP<BR>END_EVENTSINK_MAP()<img src ="http://www.cppblog.com/jjbird/aggbug/3271.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/jjbird/" target="_blank">奇奇</a> 2006-02-15 17:21 <a href="http://www.cppblog.com/jjbird/articles/3271.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>mysql的一些语法和C API</title><link>http://www.cppblog.com/jjbird/articles/2993.html</link><dc:creator>奇奇</dc:creator><author>奇奇</author><pubDate>Mon, 23 Jan 2006 09:36:00 GMT</pubDate><guid>http://www.cppblog.com/jjbird/articles/2993.html</guid><wfw:comment>http://www.cppblog.com/jjbird/comments/2993.html</wfw:comment><comments>http://www.cppblog.com/jjbird/articles/2993.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/jjbird/comments/commentRss/2993.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/jjbird/services/trackbacks/2993.html</trackback:ping><description><![CDATA[<P>select ID,name,zi,hao into outfile "zuozhe.txt" fields optionally enclosed by '""' terminated by ',' </P>
<P>from gushizuozhe</P>
<P>load data infile "zuozhe.txt" into table gushizuozhe fields optionally enclosed by '\"' terminated by </P>
<P>',' (ID,name,zi,hao);</P>
<P>grant select on *.* to "<A href="mailto:public@192.168.%">public@192.168.%</A>" identified by 'public';</P>
<P>revoke select on *.* from "<A href="mailto:public@192.168.%">public@192.168.%</A>"<BR>revoke 并不能删除用户，delete from mysql.user where user like "public%"</P>
<P>flush flush_option[,flush_option]</P>
<P>kill thread_id&nbsp;&nbsp; unix/linux下进行<BR>show processlist<BR>select user()</P>
<P>set password for <A href='mailto:www@localhost=password("www'>www@localhost=password("www</A>")</P>
<P>set option_setting</P>
<P>写锁、读锁<BR>lock tables gushi write,gushizuozhe write;<BR>unlock tables</P>
<P><BR>MYSQL编程接口<BR>MYSQL C API<BR>1、数据类型<BR>1)my_ulonglong<BR>2)my_bool<BR>3)MYSQL_FIELD_OFFSET<BR>4)MYSQL<BR>5)MYSQL_RES<BR>6)MYSQL_ROW<BR>7)MYSQL_FIELD<BR>{<BR>char *name;<BR>char *table;<BR>char *def;<BR>enum enum_field_types type;//列的数据类型<BR>unsigned int length;//列定义的长度<BR>unsigned int max_length;//数据实际的最大长度<BR>unsigned int flags;//列的属性<BR>}<BR>8)unsigned int decimals//小数位数</P>
<P>2、函数<BR>1)my_bool mysql_change_user(MYSQL *mysql,const char *user,const char *password,const char *db)<BR>2)void mysql_close(MYSQL *mysql)<BR>3)MYSQL *mysql_init(MYSQL *mysql)<BR>4)int mysql_option(MYSQL *mysql,enum mysql_option,const char *arg)指定更精确的连接参数选项<BR>5)int mysql_ping(MYSQL *mysql)//检查连接是否正常<BR>6)MYSQL *mysql_real_connect(MYSQL *mysql,const char *host,const char *user,const char *password,const </P>
<P>char *db,unsiged int port,const char *unix_socket,unsigned int client_flag)<BR>7)int mysql_select_db(MYSQL *mysql,const char *db)//选择数据库db为当前数据库<BR>8)int mysql_query(MYSQL *mysql,const char *query)<BR>9)int mysql_real_query(MYSQL *mysql,const char *query,unsigned int length)<BR>10)char *mysql_info(MYSQL *mysql)//返回最后执行的一次操作的有关信息。<BR>11)MYSQL_RES *mysql_store_result(MYSQL *mysql)//读取一个查询的全部结果。<BR>12)MYSQL_RES *mysql_use_result(MYSQL *mysql)//初始化一个结果集，但是不把结果读到客户端，仍然保留在服务器</P>
<P>上<BR>13)void mysql_free_result(MYSQL_RES *result)//释放结果集使用的内存。<BR>14)my_ulonglong mysql_affected_rows(MYSQL *mysql)//返回最后一个update,delete,insert操作影响的记录数。<BR>15)my_ulonglong mysql_num_rows(MYSQL *mysql)//mysql_store_result()返回的结果集中的记录数。<BR>16)MYSQL_ROW_OFFSET mysql_row_seek(MYSQL_RES *result,MYSQL_ROW_OFFSET offset)<BR>17)MYSQL_ROW_OFFSET mysql_row_tell(MYSQL_RES *result)//当前光标位置<BR>18)MYSQL_ROW_OFFSET mysql_data_seek(MYSQL_RES *result,unsigned long long offset)<BR>19)MYSQL_ROW mysql_fetch_row(MYSQL_RES *result)//结果集中的下一条记录<BR>20)unsigned int mysql_fetch_lengths(MYSQL_RES *result)//结果集中当前记录的长度<BR>21)unsigned int mysql_num_fields(MYSQL_RES *result)//结果集中列的数目<BR>22)MYSQL_FIELD_OFFSET mysql_field_seek(MYSQL_RES *result,MYSQL_FIELD_OFFSET offset)<BR>23)MYSQL_FIELD_OFFSET mysql_field_tell(MYSQL_RES *result)//当前光标位置<BR>24)MYSQL_FIELD *mysql_fetch_field(MYSQL_RES *result)//结果集中当前列信息<BR>25)MYSQL_FIELD *mysql_fetch_fields(MYSQL_RES *result)//结果集中所有列信息<BR>26)MYSQL_RES *mysql_list_dbs(MYSQL *mysql,const char *wild)//与wild正则表达式匹配的数据库信息<BR>27)MYSQL_RES *mysql_list_tables(MYSQL *mysql,const char *wild)//与wild正则表达式匹配的表信息<BR>28)MYSQL_RES *mysql_list_fields(MYSQL *mysql,const char * table,const char *wild)//与当前表，匹配wild正</P>
<P>则表达式的所有列名的结果集<BR>29)MYSQL_RES *mysql_list_processes(MYSQL *mysql)<BR>30)char *mysql_stat(MYSQL *mysql)//当前服务器的信息<BR>31)char *mysql_get_server_info(MYSQL *mysql)<BR>32)char *mysql_get_client_info(MYSQL *mysql)<BR>33)char *mysql_get_host_info(MYSQL *mysql)<BR>34)char *mysql_get_proto_info(MYSQL *mysql)<BR>35)unsiged long mysql_thread_id(MYSQL *mysql)<BR>36)int mysql_kill(MYSQL *mysql,unsiged long pid)<BR>37)int mysql_shutdown(MYSQL *mysql)<BR>38)void mysql_debug(char *debug)<BR>39)int mysql_dump_debug_info(char *debug)<BR>40)unsigned int mysql_errno(MYSQL *mysql)<BR>41)char *mysql_error(MYSQL *mysql)</P><img src ="http://www.cppblog.com/jjbird/aggbug/2993.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/jjbird/" target="_blank">奇奇</a> 2006-01-23 17:36 <a href="http://www.cppblog.com/jjbird/articles/2993.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>收集的一些技巧</title><link>http://www.cppblog.com/jjbird/articles/2936.html</link><dc:creator>奇奇</dc:creator><author>奇奇</author><pubDate>Fri, 20 Jan 2006 09:01:00 GMT</pubDate><guid>http://www.cppblog.com/jjbird/articles/2936.html</guid><wfw:comment>http://www.cppblog.com/jjbird/comments/2936.html</wfw:comment><comments>http://www.cppblog.com/jjbird/articles/2936.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/jjbird/comments/commentRss/2936.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/jjbird/services/trackbacks/2936.html</trackback:ping><description><![CDATA[<P>ReverseFind用法<BR>// Example for CString::ReverseFind<BR>CString s( "abcabc" );<BR>ASSERT( s.ReverseFind( 'b' ) == 4 );</P>
<P>得到工具栏尺寸<BR>CSize sizeBar;<BR>CToolBar m_wndToolBar;<BR>m_wndToolBar.GetToolBarCtrl().GetMaxSize(&amp;sizeBar);</P>
<P>在工具栏上添加编辑框&nbsp;<BR>CRect rect;<BR>m_wndToolBar.SetButtonInfo(9,ID_SEPARATOR,TBBS_SEPARATOR,100);<BR>m_wndToolBar.GetItemRect(9,&amp;rect);</P>
<P>m_wndComboBox.Create(CBS_DROPDOWN|WS_VISIBLE|WS_VSCROLL|CBS_AUTOHSCROLL,rect, &amp;m_wndToolBar,105);</P>
<P>格式化一段源代码<BR>Alt+F8</P>
<P>定位预处理#if，#endif<BR>Ctrl+K</P>
<P>顶层窗口的实现<BR>在PreCreateWindow()中加入<BR>cs.dwExStyle=WS_EX_TOPMOST</P><img src ="http://www.cppblog.com/jjbird/aggbug/2936.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/jjbird/" target="_blank">奇奇</a> 2006-01-20 17:01 <a href="http://www.cppblog.com/jjbird/articles/2936.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item></channel></rss>