﻿<?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++博客-小站随笔-文章分类-MFC开发</title><link>http://www.cppblog.com/batstying/category/13805.html</link><description>大道至简，知易行难</description><language>zh-cn</language><lastBuildDate>Fri, 22 Jul 2011 22:31:48 GMT</lastBuildDate><pubDate>Fri, 22 Jul 2011 22:31:48 GMT</pubDate><ttl>60</ttl><item><title>SIO_KEEPALIVE_VALS 用户异常掉线检测[转贴]</title><link>http://www.cppblog.com/batstying/articles/151220.html</link><dc:creator>batstying</dc:creator><author>batstying</author><pubDate>Sun, 17 Jul 2011 03:23:00 GMT</pubDate><guid>http://www.cppblog.com/batstying/articles/151220.html</guid><wfw:comment>http://www.cppblog.com/batstying/comments/151220.html</wfw:comment><comments>http://www.cppblog.com/batstying/articles/151220.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/batstying/comments/commentRss/151220.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/batstying/services/trackbacks/151220.html</trackback:ping><description><![CDATA[<span class="Apple-style-span" style="border-collapse: collapse; color: #333333; font-family: verdana, sans-serif; font-size: 13px; line-height: 19px; -webkit-border-horizontal-spacing: 2px; -webkit-border-vertical-spacing: 2px; "><br />本文来自CSDN博客，转载请标明出处：<a href="http://blog.csdn.net/jimaliu/archive/2009/11/30/4908658.aspx" style="color: #444444; text-decoration: none; ">http://blog.csdn.net/jimaliu/archive/2009/11/30/4908658.aspx</a></span><span class="Apple-style-span" style="border-collapse: collapse; color: #333333; font-family: verdana, sans-serif; font-size: 13px; line-height: 19px; -webkit-border-horizontal-spacing: 2px; -webkit-border-vertical-spacing: 2px; "><p><br />用户异常掉线检测<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; David.Zhu&nbsp; 2005/6/1<br />&nbsp;&nbsp; 目前主要有三种方法来实现用户掉线检测：SO_KEEPALIVE ,SIO_KEEPALIVE_VALS 和Heart-Beat线程。<br />下面我就上面的三种方法来做一下介绍。<br />(1)SO_KEEPALIVE 机制&nbsp;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 这是socket库提供的功能，设置接口是setsockopt API：<br />&nbsp;&nbsp; BOOL&nbsp; bSet=TRUE;<br />&nbsp;&nbsp; setsockopt(hSocket,SOL_SOCKET,SO_KEEPALIVE,(const char*)&amp;bSet,sizeof(BOOL));</p><p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 根据MSDN的文档，如果为socket设置了KEEPALIVE选项，TCP/IP栈在检测到对方掉线后，<br />&nbsp;&nbsp; 任何在该socket上进行的调用(发送/接受调用)就会立刻返回，错误号是WSAENETRESET ；<br />&nbsp;&nbsp; 同时，此后的任何在该socket句柄的调用会立刻失败，并返回WSAENOTCONN错误。</p><p>&nbsp;&nbsp; 该机制的缺点也很明显：<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 默认设置是空闲2小时才发送一个&#8220;保持存活探测分节&#8221;，不能保证实时检测！<br />&nbsp;&nbsp; 当然也可以修改时间间隔参数，但是会影响到所有打开此选项的套接口！<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 关联了完成端口的socket可能会忽略掉该套接字选项。</p><p><br />(2)SIO_KEEPALIVE_VALS 机制&nbsp;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 这是从彭博兄那里学到一个机制拉，设置接口是WSAIoctl API:<br />&nbsp;&nbsp;&nbsp;&nbsp; DWORD dwError = 0L ;<br />&nbsp;&nbsp;&nbsp;&nbsp; tcp_keepalive sKA_Settings = {0}, sReturned = {0} ;<br />&nbsp;&nbsp;&nbsp;&nbsp; sKA_Settings.onoff = 1 ;<br />&nbsp;&nbsp;&nbsp;&nbsp; sKA_Settings.keepalivetime = 5500 ; // Keep Alive in 5.5 sec.<br />&nbsp;&nbsp;&nbsp;&nbsp; sKA_Settings.keepaliveinterval = 3000 ; // Resend if No-Reply&nbsp;<br />&nbsp;&nbsp;&nbsp;&nbsp; if (WSAIoctl(skNewConnection, SIO_KEEPALIVE_VALS, &amp;sKA_Settings,<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; sizeof(sKA_Settings), &amp;sReturned, sizeof(sReturned), &amp;dwBytes,<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; NULL, NULL) != 0)<br />&nbsp;&nbsp;&nbsp;&nbsp; {<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; dwError = WSAGetLastError() ;<br />&nbsp;&nbsp;&nbsp;&nbsp; }<br />&nbsp;&nbsp;&nbsp;&nbsp; 实现时需要添加tcp_keepalive and SIO_KEEPALIVE_VALS的定义文件MSTCPiP.h&nbsp;<br />&nbsp;&nbsp;&nbsp;&nbsp; 该选项不同于SO_KEEPALIVE 机制的就是它是针对单个连接的，对系统其他的套接<br />&nbsp;&nbsp;&nbsp;&nbsp; 口并不影响。<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 针对完成端口的socket,设置了SIO_KEEPALIVE_VALS后，激活包由TCP STACK来负责。<br />&nbsp;&nbsp;&nbsp;&nbsp; 当网络连接断开后，TCP STACK并不主动告诉上层的应用程序，但是当下一次RECV或者SEND操作<br />&nbsp;&nbsp;&nbsp;&nbsp; 进行后，马上就会返回错误告诉上层这个连接已经断开了.如果检测到断开的时候，在这个连接<br />&nbsp;&nbsp;&nbsp;&nbsp; 上有正在PENDING的IO操作，则马上会失败返回.</p><p><br />&nbsp;&nbsp;&nbsp;&nbsp; 该机制的缺点：<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 不通用啦。MS的API只能用于Windows拉。不过，呵呵用彭博兄的评论就是：<br />&nbsp;&nbsp;&nbsp;&nbsp; 优雅一些^_^.<br />&nbsp;&nbsp;&nbsp;&nbsp;<br />(3)Heart-Beat线程<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 没说的。自己写一个后台线程，实现Heart-Beat包，客户端受到该包后，立刻返回相应的反馈&nbsp; 包。</p><p>&nbsp;&nbsp;&nbsp; 该方法的好处是通用，但缺点就是会改变现有的通讯协议！</p><p>有开发网络应用经历的人都知道，网络中的接收和发送数据都是使用WINDOWS中的SOCKET进行实现。但是如果此套接字已经断开，那发送数据和接收数据的时候就一定会有问题。可是如何判断这个套接字是否还可以使用呢？</p><p>　　有人一定想到使用Send函数中的返回结果来进行判断。如果返回的长度和自己发送出去的长度一致，那就说明这个套接字是可用的，否则此套接字一定出现了问题。但是我们并不是无时无刻的发送数据呀。如何解决呢？</p><p>　　其实TCP中已经为我们实现了一个叫做心跳的机制。如果你设置了心跳，那TCP就会在一定的时间（比如你设置的是３秒钟）内发送你设置的次数的心跳（比如说２次），并且此信息不会影响你自己定义的协议。</p><p>　　在VC中实现心跳的例子很多，可是在DLEPHI中一直没有相应的代码。下面我是我使用DELPHI编写的关于心跳的代码（以IOCP为例），希望对大家有帮助。</p><p>定义心跳常量</p><p>const<br />&nbsp; IOC_IN&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; =$80000000;<br />&nbsp; IOC_VENDOR&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; =$18000000;<br />&nbsp; IOC_out&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; =$40000000;<br />&nbsp; SIO_KEEPALIVE_VALS&nbsp;&nbsp; =IOC_IN or IOC_VENDOR or 4;</p><p>var</p><p>&nbsp; inKeepAlive,OutKeepAlive:TTCP_KEEPALIVE;</p><p>实现代码是在Acceptsc:= WSAAccept(Listensc, nil, nil, nil, 0);代码的后面加入：</p><p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; opt:=1;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if setsockopt(Acceptsc,SOL_SOCKET,SO_KEEPALIVE,@opt,sizeof(opt))=SOCKET_ERROR then<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; begin<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; closesocket(Acceptsc);<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; end;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; inKeepAlive.onoff:=1;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; //设置３秒钟时间间隔</p><p>　 inKeepAlive.keepalivetime:=3000;</p><p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; //设置每３秒中发送１次的心跳<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; inKeepAlive.keepaliveinterval:=1;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; insize:=sizeof(TTCP_KEEPALIVE);<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; outsize:=sizeof(TTCP_KEEPALIVE);<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if WSAIoctl(Accept,SIO_KEEPALIVE_VALS,@inKeepAlive,insize,@outKeepAlive,outsize,@outByte,nil,nil)=SOCKET_ERROR then<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; begin<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; closesocket(Acceptsc);<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; end;</p><p>如果加入以上的代码以后，系统会每３秒中加入一次的心跳。并且如果客户端断线以后（网线断），函数GetQueuedCompletionStatus会返回FALSE。</p><p>if (GetQueuedCompletionStatus(CompletionPort, BytesTransferred,DWORD(PerHandleData), POverlapped(PerIoData), INFINITE) = False) then<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; begin<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; //在这里处理客户端断线信息。</p><p>　　　continue;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; end;</p><p>以上就是我使用心跳的方法，此方法我已经在我的网络游戏中使用。情况稳定！</p><p>&nbsp;</p><p>本文来自CSDN博客，转载请标明出处：<a href="http://blog.csdn.net/jimaliu/archive/2009/11/30/4908658.aspx" style="color: #444444; text-decoration: none; ">http://blog.csdn.net/jimaliu/archive/2009/11/30/4908658.aspx</a></p></span><img src ="http://www.cppblog.com/batstying/aggbug/151220.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/batstying/" target="_blank">batstying</a> 2011-07-17 11:23 <a href="http://www.cppblog.com/batstying/articles/151220.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>CComboBoxEx控件使用</title><link>http://www.cppblog.com/batstying/articles/115297.html</link><dc:creator>batstying</dc:creator><author>batstying</author><pubDate>Thu, 13 May 2010 09:15:00 GMT</pubDate><guid>http://www.cppblog.com/batstying/articles/115297.html</guid><wfw:comment>http://www.cppblog.com/batstying/comments/115297.html</wfw:comment><comments>http://www.cppblog.com/batstying/articles/115297.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/batstying/comments/commentRss/115297.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/batstying/services/trackbacks/115297.html</trackback:ping><description><![CDATA[<p>最近学习常用的控件，其中CComboBoxEx控件的参考较少。以下第一部分的内容来自MSDN</p>
<p>第一部分：</p>
<p>创建扩展组合框 (Extended Combo Box) 控件的方式取决于是在对话框中使用该控件还是在非对话框窗口中创建此控件。</p>
<p>直接在对话框中使用 CComboBoxEx </p>
<p>在对话框编辑器中，将&#8220;Extended Combo Box&#8221;控件添加到对话框模板资源。指定其控件 ID。 <br>使用扩展组合框 (Extended Combo Box) 控件的&#8220;属性&#8221;对话框指定任何所需样式。 <br>使用添加成员变量向导添加带 Control 属性的 CComboBoxEx 类型的成员变量。可以使用此成员调用 CComboBoxEx 成员函数。 <br>对于任何需要处理的扩展组合框 (Extended Combo Box) 控件通知消息，使用&#8220;属性&#8221;窗口在对话框类中映射处理函数（请参见将消息映射到函数）。 <br>在 OnInitDialog 中为 CComboBoxEx 对象设置任何附加样式。 <br>在非对话框窗口中使用 CComboBoxEx </p>
<p>在视图或窗口类中定义此控件。 <br>调用控件的 Create 成员函数，可能在 OnInitialUpdate 中，也可能与父窗口的 OnCreate 处理函数一样早。设置此控件的样式。 <br>扩展组合框 (Extended Combo Box) 控件的主要功能是可以将图像列表中的图像与组合框 (ComboBox) 控件中的各项相关联。每项都可以显示三个不同的图像：一个是选定状态的图像，一个是未选定状态的图像，第三个是覆盖图像的图像。</p>
<p>下面的过程将图像列表与扩展组合框 (Extended Combo Box) 控件相关联：</p>
<p>将图像列表与扩展组合框 (Extended Combo Box) 控件相关联 </p>
<p>通过使用 CImageList 构造函数并存储所得到的指针来构造新的图像列表（或者使用现有图像列表对象）。 <br>通过调用 CImageList::Create 初始化新的图像列表对象。以下代码是此调用的一个示例。 <br>m_pImageList-&gt;Create(16, 16, ILC_COLOR, 2, 2);为每个可能的状态添加可选图像：选定、未选定或覆盖。下面的代码添加三个预定义图像。 <br>m_pImageList-&gt;Add(pApp-&gt;m_hIconSelected);<br>m_pImageList-&gt;Add(pApp-&gt;m_hIconNotSelected);<br>m_pImageList-&gt;Add(pApp-&gt;m_hIconOverlay);通过调用 CComboBoxEx::SetImageList 将图像列表与控件相关联。 <br>扩展组合框项所使用的不同类型的图像由 COMBOBOXEXITEM 结构的 iImage、iSelectedImage 和 iOverlay 成员中的值确定。每个值是关联控件图像列表中的图像索引。默认情况下，这些成员设置为 0，使控件不显示项的图像。如果要将图像用于特定项，可以在插入组合框项时相应地修改结构，或通过修改现有组合框项相应地修改结构。</p>
<p>设置新项的图像<br>如果正在插入新项，请使用正确的值初始化 iImage、iSelectedImage 和 iOverlay 结构成员，然后通过调用 CComboBoxEx::InsertItem 插入项。</p>
<p>下面的示例将新扩展组合框项 (cbi) 插入扩展组合框 (Extended Combo Box) 控件 (m_comboEx)，并为所有三个图像状态提供索引：</p>
<p>COMBOBOXEXITEM&nbsp;&nbsp;&nbsp;&nbsp; cbi;<br>CString&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; str;<br>int&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; nItem;</p>
<p>cbi.mask = CBEIF_IMAGE | CBEIF_INDENT | CBEIF_OVERLAY |<br>&nbsp;&nbsp;&nbsp;&nbsp; CBEIF_SELECTEDIMAGE | CBEIF_TEXT;</p>
<p>cbi.iItem = i;<br>str.Format(_T("Item %02d"), i);<br>cbi.pszText = (LPTSTR)(LPCTSTR)str;<br>cbi.cchTextMax = str.GetLength();<br>cbi.iImage = 0;<br>cbi.iSelectedImage = 1;<br>cbi.iOverlay = 2;<br>cbi.iIndent = (i &amp; 0x03);&nbsp;&nbsp; //Set indentation according<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; //to item position</p>
<p>nItem = m_comboEx.InsertItem(&amp;cbi);<br>ASSERT(nItem == i);设置现有项的图像<br>如果正在修改现有项，需要使用 COMBOBOXEXITEM 结构的 mask 成员。</p>
<p>修改现有项以使用图像 </p>
<p>声明 COMBOBOXEXITEM 结构并将 mask 数据成员设置为要修改的值。 <br>使用此结构调用 CComboBoxEx::GetItem。 <br>使用适当的值修改刚返回的结构的 mask、iImage 和 iSelectedImage 成员。 <br>传入已修改的结构来调用 CComboBoxEx::SetItem。 <br>下面的示例通过交换第三个扩展组合框项的选定和未选定图像说明此过程：</p>
<p>COMBOBOXEXITEM&nbsp;&nbsp;&nbsp;&nbsp; cbi;<br>CString&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; str;<br>int&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; nItem;</p>
<p>cbi.mask = CBEIF_IMAGE | CBEIF_SELECTEDIMAGE;<br>cbi.iItem = 2;<br>m_comboEx.GetItem(&amp;cbi);</p>
<p>cbi.iImage = 1;<br>cbi.iSelectedImage = 0;<br>nItem = m_comboEx.SetItem(&amp;cbi);<br>ASSERT(nItem != 0);第二部分：获取CComboBoxEX控件中项目的文字部分：1.若样式为Drop DownCEdit* pedit=m_comboEx.GetEditCtrl();CString str;pedit-&gt;GetWindowText(str);2.若样式为Drop ListCString str；int i=m_comboEx.GetCurSel();int n=m_comboEx.GetLBTextLen(i);m_comboEx.GetLBText(i,str);</p>
<p>本文来自CSDN博客，转载请标明出处：<a href="http://blog.csdn.net/jh2005/archive/2007/07/10/1684644.aspx">http://blog.csdn.net/jh2005/archive/2007/07/10/1684644.aspx</a></p>
<img src ="http://www.cppblog.com/batstying/aggbug/115297.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/batstying/" target="_blank">batstying</a> 2010-05-13 17:15 <a href="http://www.cppblog.com/batstying/articles/115297.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item></channel></rss>