您的访问请求被拒绝 403 Forbidden - ITeye技术社区
您的访问请求被拒绝
亲爱的会员,您的IP地址所在网段被ITeye拒绝服务,这可能是以下两种情况导致:
一、您所在的网段内有网络爬虫大量抓取ITeye网页,为保证其他人流畅的访问ITeye,该网段被ITeye拒绝
二、您通过某个代理服务器访问ITeye网站,该代理服务器被网络爬虫利用,大量抓取ITeye网页
请您点击按钮解除封锁&LPDIPROPHEADERpdiph;);;参数说明;REFGUIDrguidProp要设置或获取属性;LPDIPROPHEADERpdiph主数据结构;和别处一样,DirecX灵活的有远见的天性使得它;typedefstructDIPROPHEADE;DWORDdwS;DWORDdwHeaderS;DWORDdwO;DWORDdwH
LPDIPROPHEADER pdiph
REFGUID rguidProp 要设置或获取属性的标识,可以为DINPUT.H中的DIPROP_* 预定义值,也可以是制造者提供的GUID
LPDIPROPHEADER pdiph 主数据结构中DIPROPHEADER结构的地址,通常是一个DIPROPDWORD或DIPROPRANGE。主结构中的其它成员含有要被设置的数据
和别处一样,DirecX灵活的有远见的天性使得它看起来比想象得要复杂得多。由于SetProperty可用于人们目前根本没有想到的设备,所以不能让它接受标准数据结构。实际上,它可以接受任何针对要访问的属性而预定义的结构。对标准属性,可以是DIPROPDWORD(任何需要一个DWORD的属性),或DIPROPRANGE(任何需要两个LONG的属性)。但制造商可以声明任何其它类型的结构,仅需以一个DIPROPHEADER结构(见表1-10)作为其数据的第一部分,DirectInput能知道主结构的大小并标识出要设置或获取其属性的对象。
表1-10 DIPROPHEADER结构
typedef struct DIPROPHEADER {
DWORD dwHeaderS
} DIPROPHEADER, *LPDIPROPHEADER;
typedef const DIPROPHEADER *LPCDIPROPHEADER;
DWORD dwSize 必须初始化为外围结构的大小,如sizeof(DIPROPDWORD)
DWORD dwHeaderSize 必须初始化为sizeof(DIPROPHEADER)
DWORD dwObj 属性被访问的设备物的标识,如果包含整个设备则为O(对设定缓冲区大小而言)
DWORD dwHow 如果包含整个设备则为DIPH_DEVIC,否则为DIPH_BYOFFSET或DIPH_BYID,说明采用哪种系统标识设备物
表1-11与1-12为经常与SetProperty和GetProperty一同使用的两个标准外围结构。DIPROPDWORD用于设置或获取任何需要一个DWORD数据的属性;DIPROPRANGE用于设置或获取绝对轴的范围,也可以用于访问任何使用两个LONG数据的属性。关于轴的范围将在
第4章游戏杆输入中详细讨论。
表1-11 DIPROPDWORD结构
typedef struct DIPROPDWORD {
DIPROPHEADER
} DIPROPDWORD, *LPDIPROPDWORD;
typedef const DIPROPDWORD *LPCDIPROPDWORD;
DIPROPHEADER diph 前面进过的结构头
DWORD dwData 数据—由人设置并通过SetProperty传给设备,或由Getproperty填充。典型的数据项为缓冲区大小和轴模式等,数据的意义由方法的rguidProp参数决定
表1-12 DIPROPRANGE结构
typedef struct DIPROPRANGE {
DIPROPHEADER
LONG lM LONG lM
} DIPROPRANGE, *LPDIPROPRANGE;
typedef const DIPROPRANGE *LPCDIPROPRANGE;
成员说明 DIPROPHEADER diph 结构头 LONG lMin 最小范围值 LONG lMax 最大范围值 下面是用SetProperty设置X轴范围的一个例子: #define JOYMIN -1000
#define JOYMAX 1000
DIPROPRANGE
diprg.diph.dwSize
= sizeof( diprg );
diprg.diph.dwHeaderSize
= sizeof( diprg.diph )
diprg.diph.dwObj
= DIJOFS_X
diprg.diph.dwHow
= DIPH_BYOFFSET;
diprg.lMin
diprg.lMax
if ( FAILED( g_lpdid2-&SetProperty( DIPROP_RANG, &diprg.diph ) ) )
retrurn FALSE;
接下来要获取设备的缓冲区大小,这里DIPROPDWORD结构在声明中初始化,但其思想与上一例基本一样。
DIPROPDWORD dipdw =
// The header, which we initialize.
sizeof( DIPROPDWORD ),
// diph.dwSize
sizeof( DIPROPHEADER ),
// diph.dwHeaderSize
// diph.dwObj - no particular object
DIPH_DEVICE,
// diph.dwHow - entire device
// The data will go here.
HRESULT hr = g_lpdid2-&GetProperty( DIPROP_BUFFERSIZE, &dipdw.diph );
1.5.6 协作级别
象每次使用结构时都要填充dwSize成员一样,设置协作级别也是一种令人费解的工作—为什么非得由用户做这项工作?难道DirectInput不能做得更好吗?事实上,DirectInput非常希望把事情做得更好,但有时也需要一些帮助。设置设备的协作级别旨在让DirectInput知道用户所希望的应用程序与系统以及其它程序之间相互联系时有什么样的表现。在深入讲解这一问题之前,先介绍一下程序调用IDirectInputDevice::SetCooperativeLevel时能使用的有效标志组合,见表1-13。
表1-13 IDirectInputDevice::SetCooperativeLevel使用的标志组合
标志含义适用于
DISCL_NONEXCLUSIVE|DISCL_ABCKGROUND 其他人可以以独占或非独占模式获得设备;当前程序可以在任何时刻访问数据除力反馈外的所有设备
DISCL_NONEXCLUSIVE|DISCL_FOREGROUND 其他人可以以独占或非独占模式获得设备;
当前程序只有在位于前台时才能访问数据除力反馈外的所有设备
DISCL_EXCLUSIVE|DISCL_BACKGROUND 其他人可以以非独占模式获得设备;当前程序可以在任何时刻访问数据游戏杆和力反馈设备
DISCL_EXCLUSIVE|DISC_FOREGROUND 其他人可以以非独占模式获得设备;当前程序只有在位于前台时才能访问数据除键盘外的所有设备,对鼠标有效但禁止Windows显示光标
需要指出的第一点是,同一时刻不能有两个程序或一个程序的两个实例以独占模式获得同一设备。这主要是出于安全考虑,它可以防止输入给某一程序的值被传给同时运行的另一程序。对力反馈设备而言此时只能使用独占模式,上述这种特点可以防止两个程序同时输出作用效果,而如果同时输出则将产生混淆和警告。独占模式针对程序与系统间的相互作用不同而有多个分支:
由于Windows要求在任何时刻以同于独占的模式访问键盘,因此程序只能以非独占模式获得键盘。
由于Windows要求在任何时刻以等同于独占的模式使用鼠标,所以当程序以独占模式获得鼠标时,Windows将不能访问鼠标。这就意味着不会再产生任何鼠标消息,光标也将消失。注意这种表现与键盘不同,对键盘而言Windows有优先特权,所以重要的按钮组合如ALT_TAB及CTRL_ALT_DELETE总是有效。
如果考虑其表现则没有太多的理由优先选用独占模式。对鼠标而言选择独占模式为这样可有一点点好处,因以使Windows不再有产生鼠标消息的必要。但对游戏杆而言,选择哪种模式对表现没有影响。以前台模式获得设备意味着只有程序的主窗口位于前台时才能接收输入,对游戏和多数其它程序而言这种设置是自然的。后台模式则意味着只要程序在运行就可以接收输入,哪怕它已被最小化。想象一下一个“Smarthouse”程序,人们可以单击一个远程控制来打开车库门或开始烧烤食物,这种程序可以一直停留在Windows任务栏上,而不必被带到前台进行工作,它是一个很好的后台模式的例子。
另一种使用后台模式的情况是在当一对话框处于打开状态同时要保持获得设备时。因为当属性页位于前台时,用户需要体察其效果,所以设备只能以后台模式获得。如果已设置了前台协作级别,则只要程序转为后台,它将不能访问所用的设备。对鼠标而言,当用户打开菜单或对话框,甚至程序静止在前台时都将失去访问权。这种情况只能准备在设备重新有效时再次获得。下面开始讲获得设备。
1.4.7 获得设备
到这里为止DirectInput设置已包含了简单地准备设备待用的一劳永逸的步骤,但在能得到数据前还必须获得该设备。获得设备即是取得它的使用权并通知DirectInput说明程序想按设定的格式接收数据。但获得设备不是一劳永逸的步骤,由于种种原因,程序可能多次失去对设备的获取,每当这种情况发生时,程序需要重新获得所需设备,但DirectInput不会代劳。
假设DirectInput是一家旅游圣地豪华馆餐厅的服务生,它知道顾客喜欢坐在什么地方,是否愿意和他人用同一张餐桌,喜爱什么样的鲜花摆设等。但并不是任何时候顾客都能径直走入餐厅并坐到自己喜欢的座位上,因为有可能另一个团体已包用了他的餐桌,或者别人刚用完离去后,餐桌未来得及收拾和按喜欢的方式设置。因此,顾客应首先找服务生登记使各种安排都准备妥当。
找服务生登记并获准坐下和获得设备是一样的。每次获得设备时,DirectInput必须把各方面设成用户指定的方式,即清除所有缓冲区内的数据,以最有效的方式为数据传输作安排,同时给出用户选择的那个设备的属性。离开餐厅等同于调用IDirectInputDevice::Unacquire。其目的是让DirectInput知道其它程序或Windows系统现在可以以任何级别访问该设备了。
在结束程序前有时必须有意地释放设备,最常见的情况是当鼠标被以独占模式获得时
Windows系统将不能使用鼠标,这在前面已讲过。这时如果想让Windows接管鼠标的使用权进行菜单选择,就必须调用Unacquire以使Windows做它应作的事;当菜单关闭后再重新获得鼠标。DirectX SDK中的示例程序Scrawl提供了这种用法的例子,当用户右击鼠标时Scrawl释放鼠标并打开一个上下文菜单,用户可以在标准的Windows鼠标光标下进行选择。
如果想改变除力反馈增益以外的设备属性则也需要先释放该设备。前面已提到过,程序有时会不自觉地释放设备,发生这种情况通常是由于程序以前台模式获得设备,但使用者已切换到另一窗口,这就好比那位假想的Maitre d’被贿赂或受到威胁而离开了用户的餐桌,当然可能这个类比不很恰当。
无论释放设备是有意的还是不自觉的,在想重新获取数据之前都必须通知DirectInput,这可由调用IDirectInputDevice::Acquire实现。但并没有简单快速的方法来检验设备是否处于未获取状态,所能作的只是检验申请接收数据时的回应是什么,IDirectInputDevice::GetDeviceState和IDirectInputDevice::GetDeviceData 方法在数据流被打断后的第一次调用时会返回DIERR_INPUTLOST。如果没有重新获得设备,接下来调用时会返回DIERR_NOTACQUIRED。收到DIERR_INPUTLOST后可以试着重新获得该设备并再取一次数据,见下面的代码段:
hr = g_pMouse-&GetDeviceState( sizeof( DIMOUSESTATE ), &dims );
if ( hr == DIERR_INPUTLOST )
hr = g_pMouse-&Acquire();
if ( SUCCEEDED( hr ) )
但是,想以同样的方法响应DIERR_NOTAQUIERD或其它失败消息值是不可取的。假设程序以前台协作级别使用一种设备,当使用者切换到另一程序的窗口时,此程序就会在每次想取得数据时收到DIERR_NOTACQUIRED消息,这时任何获取该设备的尝试都是对CPU周期的浪费,而且还有产生死循环的危险。这时应响应WM_ACTIVATE消息来重新获取该设备,因为此消息表明程序又切换到前台了。
对输入设备的访问进行管理确实很有好处,但这需要在适当的时候进行,当假想的人走近并开***射击时,如果用户不能疯狂地按鼠标或拍打游戏杆,那么一切就都没有意义了。所以应总保持安全,试图重新获取已获得的设备不会有什么害处;对Acquire的多余调用不会有效果。
1.5、取得输入数据
在讲如何真正从设备获取数据这个非常重要的问题之前,先来回顾一下在此之前应进行的步骤。
创建设备。如果支持的设备不是鼠标或键盘,则还有需要得到一个IDirectInputDevice接口,因为至少会用到Poll方法。
设置协作级别。
设置数据格式。
设置设备属性。对游戏杆而言这一步是必需的,因为不设置轴的范围就不能继续进行后面的过程。如果想用缓冲区数据,则也需要设置其属性,因为必须把缓冲区大小从0改为某个值。
获得设备。与前面几步不同,这一步不是一劳永逸的。
1.5.1 两类数据
如果读者使用了旧风格的Windows输入编程方法就需要为诸如WM_KEYDOWN、WM_MOUSEMOVE等消息写处理程序。这些消息由Windows产生以响应设备中单个发生的
事件,如键或按钮被按下或松开或者鼠标有了足够的移动而产生了一个中断等。
尽管DirectInput不使用Windows消息,但它采用了类似的取得输入信息的系统,这个系统称为缓冲区数据。当设备的缓冲区大小属性被设为大于0的值以后,这一系统就开始运作。每当有输入事件发生时,都会有一个数据包被创建,但这些数据包被放在一个私有缓冲区中,而不是转换成消息的格式。在任何时刻都可以用IDirectInputDevice::GetDeviceData方法从那些缓冲区中取出数据包。
读者或许也已用过类似于joyGetPosEx和GetAsyncKeyState函数,它们返回的不是离散的事件,而是整个设备或单个键的当前状态的“快照”。在DirectInput中这被称为立即数据,它存在于数据包中,数据包的格式即是为设备设置的格式,调用IDirectInputDevice::GetDeviceState方法可以取得这些数据。
读者可以在程序中任意选择使用上述类型的数据,我们的示例程序中使用立即数据来检测箭头键或游戏杆轴的状态,用缓冲区数据搜寻对发射键的敲击。鼠标的移动也使用了缓冲区数据,因为我们程序中的光标响应的是鼠标的移动而不是其位置。
从缓冲区中清除一个条目并不会改变GetDeviceState返回数据,因为它返回的总是设备的当前真实状态。就这一点而言它与标准的WindowsGetKeyboardState函数不同,后者实际上只是跟踪被处理的键盘消息。第4、5、6 章有关鼠标、游戏杆和键盘输入将详细说明立即数据和缓冲区数据。
1.5.2 事件通知
DirectInput提供一种机制以通知程序设备上是否发生某事或程序是否失去了设备,用Win32 CreatEvent函数创建一个事件,然后通过把其句柄传递给IDirectInputDevice::SetEventNotification方法(见表1-14)将其关联到某个设备。
表1-14 SetEventNotification方法
HRESULT SetEventNotification(
HANDLE hEvent
CreateEvent 返回的事件句柄,当设备状态改变时此事件将被设置。想关闭某设备的事件通知可通过传NULL实现
事件通知对于实时游戏并不十分有用,这时一般采用的方法是在每次循环时检测设备状态或输入数据缓冲区的内容,也可以同时对这两项进行检测。从另一方面说,程序在鼠标或键盘输入之前不做任何有关的事可以更有效地使用处理器,使之等待事件发生而不是总在轮询各个设备(见DXSDK中的Srawl示例程序)。但对于其它设备,则不得不采用轮询的方法,原因稍后再说明,因此检测信号并不比直接查询设备状态或缓冲区好多少。如果确实发现了使用事件通知的场合,那么在SDK参考中有大量类似于Scrawl中IDirectInputDevice::SetEventNotification的示例代码可以使用。
注意:本章和后续章节中,我们经常使用“事件”一词来说明设备状态或缓冲区里数据项发生改变的事情,如按压按钮或轴的变化等,一般它不表示本章中我们讲到的事件对象,在使用狭义时我们将尽量表达清楚。
1.5.3 轮询以获取数据
当设备状态改变时DirectInput是如何知道的呢?某些情况下,它通过旧风格的方法即硬件中断实现,否则就必须查询驱动器以获得当前设备状态并用一些小把戏来模拟事件(广义和狭义)。所有这些都在幕后静静地运行,但是首先需要做一件事以使轮询操作发生。DirectInput不会自动轮询设备,而只在调用IDirectInputDevice::Poll方法时才这么做。该方法没有任何参数。
包含各类专业文献、外语学习资料、中学教育、幼儿教育、小学教育、行业资料、31DirectInput编程简介等内容。
第14章 DirectX编程简介 DirectX 是微软公司为编写游戏和其他高性能多媒体应用...从 DirectX 9.0c 的 2005 年 10 月升级版开始, DirectInput 已被 XInput (... 编程高手之路—vb 入门和游戏编写—用 VB 编写 DirectX7.0 游戏(上) DirectX...这个对象包含了 建立诸如 DirectDraw、 Direct3D、 DirectSound、 DirectInput 等... 今天,我就开始介绍一些关于游戏编程的基础东西,以便大家 能够真正的开始了解游戏,...这些引擎都是将我们今后会碰到的 DirectDraw、 DirectInput、DirectPlay 等等 ... &Win32 编程” 很不幸,我从开始学习编程到理解这个...(DirectDraw 的 3D 版) ,DirectInput(简化你的应用...下面介绍一下我接触过的三种版 本 控制工具(也是... DirectInput编程基础 – 游戏杆输入_IT/计算机_专业资料。介绍游戏杆的编程/zwell/archive//79143.aspx 目录 3.1 3.2 3.3 ... [program MovePicture v0.3] DirectDraw 与 DirectInput 的游戏编程体验我想关于这个主题的文章,不算少,但也不算太多。但大多是分别介绍 DirectDraw 与 DirectInput... 其中第一部分主要介绍 游戏编程的基本概念; 第 二部分详细介绍游戏编 程中的...¥38.50 章) DirectInput 编程 , (第 3 章) ,DirectSound 和 DirectMusic ... 一.简介 今天我们要接触到令人敬畏的 DirectX。它比...它给你游戏编程所需的一切(有点夸张)。当然了,它...5.DirectInput:为包括游戏杆、鼠标、键盘和游戏控制... 正规一些的 windows 游戏,可以研究一下 DirectInput。...现在的面向对象的编程都不是 用 C 语言的鼠标操作...本节将介绍四种实现动画的手段,所谓动画,也就是连续...网络相关常见问题
首先开发者需确认访问慢的原因,包括用户原因,网络原因,服务端原因。
1. 用户原因建议从以下方面排查:
用户电脑性能,电脑病毒,DNS缓存和设置,上网代理,防火墙设置,浏览器版本和插件等,可以通过换电脑测试。
2.网络原因建议从以下方面排查:
确定用户的IP以及网络运营商,域名解析是否正确,是否存在跨网,局域网额外限制,到其他公网服务是否正常,到相关服务器或网络中间路由节点的网络延迟,有无丢包,小运营商网络不稳定等。
建议通过异地正常网络使用相同帐号测试。
3. 服务端问题建议从以下方面排查:
平台问题,如CDN异常,请求转发异常等;服务端问题,如服务器性能,进程状况,程序异常等。
排查时,请配合使用如tcpdump,tracert等各种命令,以及httpwatch,wireshark等抓包工具。
如通过上述技术手段还无法解决,请提供以上排查信息和数据,通过联系运维支持协助定位。
Ipconfig/ifconfig
显示计算机TCP/IP的详细配置,包括DNS信息,例如ipconfig /all,ipconfig /displaydns。
包发送命令,可以模拟诊断延迟,丢包,测试网络状况,例如linux:ping 10.1.1.1 -c 10 -s 800,ping 10.1.1.1 -i 0.5,windows: -t,ping 10.1.1.1 -n 8 -l 800。
Tracert/traceroute
跟踪路由,显示数据包到达目的的路径及各节点的时间,可以用来发现运营商节点问题和跨网问题等,如。
远程登录服务命令,主要用于测试端口状态,例如telnet 10.1.1.1 8001。
域名查询工具,主要用来查看域名解析是否正确。
wc,netstat -an等。
适用于linux系统。
网络数据采集分析工具,可以对网络数据包进行截获和分析。
通过设定参数来实现对网络数据包的各种监控与分析。
例如,tcpdump -i any -nn tcp and port 443,tcpdump -i any "host 10.1.1.1" -n,tcpdump -s 0 -nX -i eth0 host 113.108.20.23等。
适用于windows系统。
强大的网页数据分析工具,集成在IE浏览器,也可支持firefox,可以用来诊断前台接口调试,CDN下载资源慢和页面响应慢等问题。
建议使用专业版。
适用于windows系统。
独立的网络抓包工具,可***所有电脑和互联网的http通讯,可以用来调试和诊断web服务和本地http的交互等问题。
适用于windows和linux系统。
网络封包分析软件,可以实时分析所有支持协议的通讯数据
请通过以下网站查询:
右键网上邻居--&属性(R)--&右键本地链接--&属性(P),双击Internet协议版本4(TCP/IPV4),修改本地DNS设定。
开始菜单--&控制面板--&网络和共享中心--&本地连接--&属性(P),双击Internet协议版本4(TCP/IPV4),修改本地DNS设定。
开始--&运行--&输入CDM进入到命令模式,输入
命令一:netsh interface ip set dns "网卡名" static 主DNS,
命令二:netsh interface ip set dns "网卡名" static 辅DNS编号(例如:主DNS设定:netsh interface ip set dns "本地连接" static 8.8.8.8,辅DNS设定:netsh interface ip set dns "本地连接" static 8.8.8.8 2)。
开始菜单--&管理工具--&服务,重启DNS Client和DHCP client两项服务。
开始菜单--&运行--&输入CDM进入命令模式,输入ipconfig/flushdns,回车执行,提示成功即可。
确认是否有***对应的端口,是否有做访问限制。
测试连接方式是否正常,例如TELNET只能测试TCP端口,UDP端口可以使用NETCAT等工具进行测试.可以通过wireshark和tcpdump等工具查看具体的数据包走向,明确定位问题。
腾讯机房禁止不同应用的机器相互访问,如果同一应用内端口无法正常访问,请通过联系运维支持查询防火墙设置。
请确认网络是否可用,域名解析是否正确,服务端是否禁止ICMP包,使用tracert/traceroute协助确定网络故障。
如通过上述技术手段还无法解决,请提供以上排查信息和数据,请通过联系运维支持协助定位。
Telnet不通,指的是telnet之后没有任何返回,如果直接返回信息,则表示telnet是通的,但是请求被直接拒绝。
故障排查步骤:
在telnet源上,通过ping测试网络是否正常;
在目标机器上,确认服务器是否设置了禁止telnet(默认没有设置),是否***了正确的IP和端口,可以通过telnet 127.0.0.1测试本地telnet服务;
对于hosting应用,如果通过以上技术手段还无法解决问题,请提供相应信息和数据,请通过联系运维支持排查腾讯防火墙设置。
tracert不通,指的是从某一节点开始,全部是*,并且无法到达目的,如果只是中间某一个节点出现*,则只能说明该节点禁止被tracert。
故障排查步骤:
从tracert的第一个节点开始,到最后一个可访问节点,查询各节点IP的所属运营商,可以判断网络访问路径以及运营商,然后定位是运营商问题,还是目标服务的问题。
注意,小运营商跨网问题,需要自助向运营商反馈。
Udp包的发送和***可以使用如下方法:
发送udp包,使用netcat命令,例如netcat -v -u 10.1.1.1 8001,同时使用tcpdump或者netcat***udp请求。
可以结合以下现象初步判断是否受到DDOS攻击:
机器入流量出现陡增,同时出流量急剧下降;
服务器出现大量SYN,TIME_WAIT等异常连接( netstat -a |awk '{print $6}' |sort|uniq -c|sort -gk 1);
某些IP访问频率过高,占用大量资源(netstat -tun|awk '{print $5}'|uniq -c|sort -r),导致服务不正常等。
注:腾讯已经对常见的攻击进行了监控和防范,开发者也可结合自身逻辑在服务器端限制特定IP访问。
为了防范DDOS网络攻击,建议开发者进行如下开发:
1. 建议在Server端引入超时机制,对指定时间内未发送请求或是未发完请求的客户端主动断开,规避空连接或是慢速请求攻击;
2. 建议对客户端请求频率做统计,对频繁超过配置的源IP,临时封禁一段时间,或是使用验证码进行限制,防止刷请求;
3. 建议安全保护逻辑前置在所有业务逻辑之前,避免恶意请求消耗过多服务器性能。
应用为了兼顾电信、联通、移动的用户,一般都需要实现三网接入,让三个运营商的用户进入都不需要跨网,保证应用质量。
腾讯开放平台提供了TGW域名接入方案(详见:),开发者只需要通过域名绑定,让一个域名同时绑定在电信、联通、移动的代理机器上,即可让电信的用户通过电信代理访问应用,联通的用户通过联通的代理进入,移动的用户通过移动的代理进入,满足了应用对三网接入的需求。
单个用户网络问题,建议开发商协助用户检查其电脑设置,包括DNS,上网代理,防火墙,局域网设置,是否跨网等;
如果是批量用户出现网络问题,可以收集共性信息,比如地域,运营商,访问方式等,然后通过联系运维支持进行问题定位和解决。
当http返回码出现40x的时候,表示用户提交的请求被服务器接受,但是被认为是错误的请求,包括:
请求语法错误,请求文件不存在,服务器拒绝请求,客户端请求发送超时等。
开发者需要结合客户端请求和服务端设置来详细定位。详细返回信息信息请参考:
当http返回码出现50x的时候,表示服务器在处理正确请求时,出现了内部错误,属于服务端问题。
建议开发者在服务代码和逻辑中进行定位。详细返回码信息请参考:DirectInput 键盘编程入门 -
DirectInput 键盘编程入门
游戏编程可不仅仅是图形程序的开发工作,实际上包含了许多方面,本文所要讲述的就是关于如何使用
DirectInput
来对键盘编程的问题。
在 DOS
时代,我们一般都习惯于接管键盘中断来加入自己的处理代码。但这一套生存方式在万恶的
Windows 社会下是行不通的,我们只能靠领 API
或者 DirectInput 的救济金过活。
在 Windows 的 API 中,有一个
GetAsyncKeyState()
的函数可以返回一个指定键的当前状态是按下还是松开。这个函数还能返回该指定键在上次调用
GetAsyncKeyState()
函数以后,是否被按下过。虽然这个函数听上去很不错,但现在领这种救济金的程序员是越来越少了。原因无它,只因为
DirectInput
的救济金比这丰厚,而且看上去似乎更专业?
为了早日成为职业的救济金用户,我们就从学习
DirectInput 的键盘编程开始吧。
DIRECTINPUT 的初始化
前面讲 DirectDraw
时,曾经提到,微软是按 COM 来设计DirectX的,所以就有了一个
DIRECTINPUT
对象来表示输入设备,而某个具体的设备由
DIRECTINPUTDEVICE 对象来表示。
实际的建立过程是先创建一个 DIRECTINPUT
对象,然后在通过此对象的 CreateDevice
方法来创建 DIRECTINPUTDEVICE 对象。
示例如下:
&dinput.h&
#define DINPUT_BUFFERSIZE 16
LPDIRECTINPUT&&&&&&&&&&
lpDirectI& // DirectInput object
LPDIRECTINPUTDEVICE&&&&
lpK&&&& // DirectInput device
BOOL InitDInput(HWND hWnd)
// 创建一个 DIRECTINPUT 对象
hr = DirectInputCreate(hInstanceCopy, DIRECTINPUT_VERSION,
&lpDirectInput, NULL);
if FAILED(hr)
return FALSE;
// 创建一个 DIRECTINPUTDEVICE 界面
hr = lpDirectInput-&CreateDevice(GUID_SysKeyboard, &lpKeyboard, NULL);
if FAILED(hr)
return FALSE;
// 设定为通过一个 256 字节的数组返回查询状态值
hr = lpKeyboard-&SetDataFormat(&c_dfDIKeyboard);
if FAILED(hr)
return FALSE;
// 设定协作模式
hr = lpKeyboard-&SetCooperativeLevel(hWnd,
DISCL_NONEXCLUSIVE | DISCL_FOREGROUND);
if FAILED(hr)
return FALSE;
// 设定缓冲区大小
&&& // 如果不设定,缓冲区大小默认值为 0,程序就只能按立即模式工作
// 如果要用缓冲模式工作,必须使缓冲区大小超过 0
DIPROPDWORD&&&&
property.diph.dwSize = sizeof(DIPROPDWORD);
property.diph.dwHeaderSize = sizeof(DIPROPHEADER);
property.diph.dwObj = 0;
property.diph.dwHow = DIPH_DEVICE;
property.dwData = DINPUT_BUFFERSIZE;
hr = lpKeyboard-&SetProperty(DIPROP_BUFFERSIZE, &property.diph);
if FAILED(hr)
return FALSE;
hr = lpKeyboard-&Acquire();
if FAILED(hr)
return FALSE;
return TRUE;
在这段代码中,我们首先定义了
lpDirectInput 和 lpKeyboard
两个指针,前者用来指向 DIRECTINPUT
对象,后者指向一个 DIRECTINPUTDEVICE 界面。
通过 DirectInputCreate(), 我们为 lpDirectInput
创建了一个 DIRECTINPUT 对象。然后我们调用
CreateDevice 来建立一个 DIRECTINPUTDEVICE
界面。参数 GUID_SysKeyboard
指明了建立的是键盘对象。
接下来 SetDataFormat 设定数据格式,SetCooperativeLevel
设定协作模式,SetProperty
设定缓冲区模式。因为这些函数方法的参数很多,我就不逐个去详细解释其作用了,请直接查看
DirectX 的帮助信息,那里面写得非常清楚。
完成这些工作以后,我们便调用
DIRECTINPUTDEVICE 对象的 Acquire
方法来激活对设备的访问权限。在此要特别说明一点,任何一个
DIRECTINPUT 设备,如果未经 Acquire,是无法进行访问的。还有,当系统切换到别的进程时,必须用
方法来释放访问权限,在系统切换回本进程时再调用
Acquire 来重新获得访问权限。
所以,我们通常要在 WindowProc
中做如下处理:
long FAR PASCAL
WindowProc(HWND hWnd, UINT message,
WPARAM wParam, LPARAM lParam)
switch(message)
case WM_ACTIVATEAPP:
if(bActive)
&&&&&&&&&&&
if(lpKeyboard) lpKeyboard-&Acquire();
&&&&&&&&&&&
if(lpKeyboard) lpKeyboard-&Unacquire();
哦,对了,前一段例程中还提到了立即模式和缓冲模式。在
DirectINPUT 中,这两种工作模式是有区别的。
如果使用立即模式的话,在查询数据时,只能返回查询时的设备状态。而缓冲模式则将记录所有设备状态变化过程。就个人喜好而言,笔者偏好后者,因为这样一般不会丢失任何按键信息。对应的,如果在使用前者时的查询频度太低,则很难保证采集数据的完整性。
DIRECTINPUT 的数据查询
立即模式的数据查询比较简单,请看下面的示例:
BYTE diks[256]; //
DirectInput keyboard state buffer 键盘状态数据缓冲区
HRESULT UpdateInputState(void)
if(lpKeyboard != NULL)&&&&& // 如果 lpKeyboard
对象界面存在
hr = DIERR_INPUTLOST;&& // 为循环检测做准备
// if input is lost then acquire and keep trying
while(hr == DIERR_INPUTLOST)
&&&&&&&&&&&
// 读取输入设备状态值到状态数据缓冲区
&&&&&&&&&&&
hr = lpKeyboard-&GetDeviceState(sizeof(diks), &diks);
&&&&&&&&&&&
if(hr == DIERR_INPUTLOST)
&&&&&&&&&&&
&&&&&&&&&&&&&&&
// DirectInput 报告输入流被中断
&&&&&&&&&&&&&&&
// 必须先重新调用 Acquire 方法,然后再试一次
&&&&&&&&&&&&&&&
hr = lpKeyboard-&Acquire();
&&&&&&&&&&&&&&&
if(FAILED(hr))
&&&&&&&&&&&&&&&&&&&
&&&&&&&&&&&
if(FAILED(hr))
&&&&&&&&&&&
return S_OK;
在上面的示例中,关键处就是使用
GetDeviceState
方法来读取输入设备状态值以及对异常情况的处理。通过使用
GetDeviceState
方法,我们把输入设备的状态值放在了一个
字节的数组里。如果该数组中某个数组元素的最高位为
1,则表示相应编码的那个键此时正被按下。例如,如果
diks[1]&0x80&0,那么就表示 ESC
键正被按下。
学会了立即模式的数据查询以后,下面我们开始研究缓冲模式的情况:
UpdateInputState(void)
if(lpKeyboard != NULL)
DIDEVICEOBJECTDATA& didod[DINPUT_BUFFERSIZE];& // Receives buffered
DWORD&&&&&&&&&&&&&&
HRESULT&&&&&&&&&&&&
hr = DIERR_INPUTLOST;
while(hr != DI_OK)
&&&&&&&&&&&
dwElements = DINPUT_BUFFERSIZE;
&&&&&&&&&&&
hr = lpKeyboard-&GetDeviceData(sizeof(DIDEVICEOBJECTDATA),
&dwElements,
&&&&&&&&&&&
if (hr != DI_OK)
&&&&&&&&&&&
&&&&&&&&&&&&&&&
// 发生了一个错误
&&&&&&&&&&&&&&&
// 这个错误有可能是 DI_BUFFEROVERFLOW 缓冲区溢出错误
&&&&&&&&&&&&&&&
// 但不管是哪种错误,都意味着同输入设备的联系被丢失了
&&&&&&&&&&&&&&&
这种错误引起的最严重的后果就是如果你按下一个键后还未松开时
&&&&&&&&&&&&&&&
发生了错误,就会丢失后面松开该键的消息。这样一来,你的程序
&&&&&&&&&&&&&&&
就可能以为该键尚未被松开,从而发生一些意想不到的情况
&&&&&&&&&&&&&&&
// 现在这段代码并未处理该错误
&&&&&&&&&&&&&&&
解决该问题的一个办法是,在出现这种错误时,就去调用一次
&&&&&&&&&&&&&&&
// GetDeviceState(),然后把结果同程序最后所记录的状态进行
&&&&&&&&&&&&&&&
// 比较,从而修正可能发生的错误
&&&&&&&&&&&&&&&
hr = lpKeyboard-&Acquire();
&&&&&&&&&&&&&&&
if(FAILED(hr))
&&&&&&&&&&&&&&&
&&&&&&&&&&&
if(FAILED(hr))
&&&&&&&&&&&
// GetDeviceData() 同 GetDeviceState() 不一样,调用它之后,
// dwElements 将指明此次调用共读取到了几条缓冲区记录
// 我们再用一个循环来处理每条记录
for(int i=0; i&dwE i++)
// 此处放入处理代码
// didod[i].dwOfs 表示那个键被按下或松开
// didod[i].dwData 记录此键的状态,低字节最高位是 1
表示按下,0 表示松开
// 一般用 didod[i].dwData&0x80 来测试
return S_OK;
其实,每条记录还有 dwTimeStamp 和
dwSequence
两个字段来记录消息发生的时间和序列编号,以便作更复杂的处理。本文是针对初学者写的,就不打算去谈论这些内容了。
DIRECTINPUT 的结束处理
我们在使用 DIRECTINPUT
时,还要注意的一件事就是当程序结束时,必须要进行释放处理,其演示代码如下:
void ReleaseDInput(void)
if (lpDirectInput)
if(lpKeyboard)
&&&&&&&&&&&
// Always unacquire the device before calling Release().
&&&&&&&&&&&
lpKeyboard-&Unacquire();
&&&&&&&&&&&
lpKeyboard-&Release();
&&&&&&&&&&&
lpKeyboard = NULL;
lpDirectInput-&Release();
lpDirectInput = NULL;
这段代码很简单,就是对 DIRECTINPUT
的各个对象去调用 Release
方法来释放资源。这种过程同使用 DIRECTX
的其它部分时是基本上相同的。