[转载]外挂基础知识入门教学|工具下载

mikel阅读(1025)

[转载]外挂基础知识入门教学|工具下载 – 游戏开发:主席 – 博客园.

外挂基础知识入门教学|工具下载

{threadNotes} 外挂制作必备工具下载.

工具下载:
peid 0.94    用于查壳,知道什么壳就可以脱壳拉.
http://www.pediy.com/tools/unpack/File_analysers/peid/peid.rarOllyDbg调试器    调试游戏.
http://www.pediy.com/tools/Debuggers/ollydbg/OllyICE.rar
OD脱壳脚本    用于脱壳.
http://www.pediy.com/tools/Debuggers/ollydbg/script.htm

LordPE    一般用于脱壳.不过还有别的妙用…
http://www.pediy.com/tools/PE_tools/Lordpe/LPE-DLX.rar

ImportREC    脱壳修复工具
http://www.pediy.com/tools/PE_tools/Rebuilder/Import%20REC/ucfir16f.rar

eXeScope    资源编辑修复工具
http://www.pediy.com/tools/Resource/eXeScope/eXeScope.rar

freeRes    资源提取工具(注册码随意输入).
http://www.pediy.com/tools/Resource/freeRes/freeRes0.94.zip

Resource Hacker    资源编辑修复工具
http://www.pediy.com/tools/Resource/Resource%20Hacker/reshhack3.4.zip

WinHex    十六进制编辑工具(一般用来提取游戏的列表等…)
http://www.pediy.com/tools/Editors/winhex/WinHex_12.75_SR-6_HA.rar

UltraEdit    做外挂的~没几个不用这个^^(抠地图必须要用的哈..)
http://www.pediy.com/tools/Editors/Ultra-Edit/Ultraedit.14.hh.rar

IDA    反汇编工具(一般网络游戏加解密这个一键就可以了.)
http://www.pediy.com/tools/Disassemblers/ida/DataRescue.IDA.Pro.Advanced.v5.2.windows-YAG.zip

C32Asm    静态反汇编牛X工具
http://www.pediy.com/tools/Disassemblers/C32Asm/C32Asm.rar

Cheat Engine    目前最好用的内存修改器
http://dl-sh-ctc-1.pchome.net/3p/hl/0706_by_10_gamersky.rar

{threadNotes} Re:外挂源代码&教学&工具下载[包含商业源代码]

首先,我想说明几点:
第一,这篇文章并不是具体教你如何写外挂,只是带你大致浏览一下网游外挂的制作流程,并就其中的一些关键技术点加以简单说明。大家可以用看故事书的心情来阅读此文,了解一下网游外挂制作过程中的一些原理。
第二,网游数据的破解爱麻烦,通常一个网游外挂制作团队内都有一名破解高手坐镇。所以碰到破解方面的问题我就只能一笔带过了。一、游戏封包的加密与解密算法的破解
破解封包的加密与解密算法是制作外挂的第一步,是外挂制作中最具技术含量的步骤,同样也是一个十分令人头痛的环节。如果加密与解密算法被成功地破解,那 么外挂制作也就完成了一半。破解封包的加密与解密算法的行为同样属于黑客们其中之一的行为,因此我们可以在黑客网站里找到相应的资料,另外网络上也有专门 的破解网站为大家提供信息。
在破解封包的加密与解密算法之前我们首先需要知道一些情况。我们知道随着机器性能的提高与网络带宽的提升,新的游 戏运行商对游戏封包的加密与解密算法的设计变得越来复杂。那种原先通过分析封包数据就可以得出加密与解密算法的时代已经变成了过去。现在如果再要破解一个 游戏的封包的加密与解密算法,那么其必须通过分析程序源代码才能清楚。
1.1 封包的概念 本文所讲的封包是指由sockets协议进行发送与接收的数据包。广义的封包是指计算机之间互相进行通信的数据包,其可以因通信协议的不同而在内容上有所不同。
1.2 破解原理 目前破解封包加密与解密算法的方法主要是通过动态调试技术来实现的。其原理是首先通过动态调试跟踪并取出加密与解密算法的代码段,然后再通过分析这些代码最终得出结论。
那为什么我们可以跟踪并取得这些代码呢?首先我们知道无论游戏程序如何设计,其加密与解密算法的代码永远存在于程序中;其次我们知道在程序流的执行过程中,加密与解密算法的代码段一定会被执行。
1.3 破解需要具备的知识 要能顺利进行破解则必须具备一定的知识,一是熟练掌握汇编原理与汇编语言,二是要熟悉加壳与脱壳原理(虽然很多游戏不需要),三是要熟悉代码结构的知识,四是要熟悉动态调试技术与调试工具的使用,五是具有高级语言知识与较高的编程修为。
1.4 破解的技术与方法 动态调试工具我们可以采用OllyDbg工具或其他工具,不熟悉OllyDbg工具的可以查看它的中文帮助。动态调试主要是跟踪代码的 执行,而我们查找加密与解密代码段就是一个跟踪的过程。一般跟踪的起点可以是windows消息、socket中的send与recv等函数。有时程序有 可能将发送与接收过程由一个专门的线程进行处理,那么这种情况下我们需要找出该处理线程。至于具体如何进行跟踪本文不再进行详述,具体内容可以到相关网站 上查看,比如看雪学院、笨冬瓜等网站。这里主要讲一下加密与解密算法代码的特征。
1.5 加密与解密算法代码特征 虽然在未进行分析之前我们很难判断一段代码是否是加密或解密算法,但我们还是可以根据一些特征进行较大概率的猜测。一情况下加密 与解密都要进行一系列的异或、移位、加减、乘除和重复运算过程,因些一段代码中若具有上述特征,我们可以进行较为肯定的断定。
1.6 代码反推导 代码反推导是进行破解算法的主要方法,代码反推导的水平主要与一个人自身的编程修为相关,但在这里仍有一些基本的方法。代码反推导可以有一定 的程式,首先可以将汇编码写成三元表达码,其次将代码中的转移指令转换为条件语句或循环语句,再次将代码中的变量进行迭代,最后进行变量形式转换与语句形 式转换。通过以上的步骤,一般我们可以将汇编语言转换为高级语言,而当我们推导出高级语言后,就能进行较为实义的分析。

①.选择一款目标游戏
制作网游外挂的第一步就是选定一款游戏。目标游戏不是乱选的,里面也有很多讲究。
第一点,选择自己熟悉的游戏类型。如果你之前已经做过网游外挂,那选择一款类似的游戏会给你节省很大的时间,如果是第一次制作的话,那也选一款自己熟悉的游戏类型。
第二点,尽量不要选择热门的游戏,因为热门的游戏往往意味着竞争对手的增多,而且反外挂系统也比较厉害。像梦幻诛仙驱动+反外挂…头疼.
第三点,不要小看玩家人数少的游戏,游戏规模小,一般技术也比较容易突破。如果想销售的话。一款游戏,只要你能形成吃独食的场面,再加上营销搞得好的话,其中的利润将超过你的想象。但要注意,最好不要碰上因为游戏规模过小导致游戏厂商把游戏关闭的衰事。
第四点,尽量选择尚在测试期内的游戏,这使得你有充足的时间制作外挂。

②.目标游戏初分析
1.确定制作网游外挂的类型.
目标网游选定好之后,你首先要做的第一件事就是确定你要制作的网游外挂类型。
网游外挂虽然统称为外挂,但细分的话可以分为以下二类:内挂和脱机外挂。
内挂就是在游戏内呼出的网游外挂,它依赖于网游客户端,所使用到的技术主要包括鼠标和键盘的模拟,内存特殊变量区域的搜索,或者是挂钩游戏的收包函数和模拟游戏的发包函数。
脱机外挂就是指不依赖于客户端,能独立模拟客户端和游戏服务器进行通讯的网游外挂。脱机外挂的实现方式只有一种,就是模拟网游客户端的收包和发包过程。
总体而言,内挂的整体制作难度比脱机外挂要简单一些,但脱机外挂制作要比内挂更有趣,而且用起来也更方便,不必启动庞大的客户端程序。
某些时候脱机比较容易上手,分游戏也不是都是这样。所以以下都是脱机教学为例:

二、游戏指令与数据结构的筛查
游戏指令和指令中所携带的数据结构的筛查是外挂制作的第二步,这一步并不是很难,但十分烦锁。游戏指令和其数据结构的筛查并无技巧可言,主要是通过多次重复比较数据而最终确定结果。
在筛查游戏指令和其数据结构之前我们需要对封包截获技术有所了解,同时能对当前流行的几款封包截获工具如FPE、WPE等有所熟练使用。然而由于某些游 戏运行商会针对一些问题而制定相应对策,因此有些时候需要我们自己编写封包截获工具。自己编写封包截获工具的好处还在于可以具体针对某一款游戏而编制特定 的工具,这之中最重要的是可以事先将加密数据解密成明码,为分析封包提供方便。
2.1 封包截获技术 根据具体的截获原理不同,封包截获技术可分为:一是Hook技术、一是socket重写技术。无论使用何种截获技术我们最终要跟踪的都是socket中的发送函数与接收函数,如send、recv等。
2.1.1 Hook原理 Hook原理是通过向应用程序中注入dll文件,并改写应用程序函数导入表中的DLL调用函数,Hook技术要求我们对可执行程序的文件即PE文件结构有所了解。
2.1.2 socket重写原理 socket重写原理是通过重写整个socket文件,用新写的socket文件代替原socket文件,并由新socket文件 调用原socket文件中的函数,从而截获函数的调用过程。socket重写技术要求改写后的socket文件具有跟原socket文件相同的接口,否则 程序调用将发生错误。
2.2 封包的分析 封包分析时最重要的就是尽量减少分析时的干扰,干扰越少越有利于我们能针对性地得到结果。因此封包分析时一般是将游戏角色尽量带到一个玩家或怪物比较少的地方,同时在分析出一个后尽量过滤一个。
2.3 分析结果的处理 封包分析完毕后,我们可以为每一个指令定义一个含义比较明确的代码,并为每条指令所携带的结构信息定义相应的数据结构,为指令中的状态码也定义相应的代码。为所有指令与数据结构进行相应的定义,可以使我们在后续的外挂代码书写过程中隐藏掉实现的细节。

1.网络截包工具(Microsoft Network Monitor)的使用简介
目 标网游的初步分析最主要的工作是分析游戏初始阶段网游客户端和服务器之间的数据通讯。这一阶段主要是指从输入用户名和密码开始登录游戏到玩家人物出现在游 戏场景中这个阶段。这是开始阶段最关键的一个步骤,如果你能够成功破解网游数据通讯部分的加密,并用Debug程序成功模拟整个登录过程,那你几乎就已经 成功了一半了。如果无法破解加密的话,那就需要赶快重新选定一款游戏了。或者请高手或者朋友帮你分析=。=
关于初步分析,首先要确定网游客户端和服务器之间的大致通讯过程,最起码你要知道客户端连接的是哪一个服务器,连接的端口是多少,在登录的过程中发送和接受了几个包?而要了解这些东西,你就要使用到网络截包工具了。
初学者可以使用.Microsoft Network Monitor V3.1,方便简单。大家可以到下面的网址去下载该软件。
http://support.microsoft.com/kb/933741/zh-cn

下面,我简单介绍一下该软件的使用方法。
安装好程序之后,运行程序,点击【Start Page】页的【Create a new capture tab】按钮,创建一个新的数据捕获会话,点击工具栏上绿色的开始按钮,就可以开始捕获网络数据了。整个程序的界面如下图所示:

各个窗口的作用如下:
Network Conversations下面有二项:
My Traffic代表本机作为发送方或者接收方参与的网络数据包。选中该项后,Frame Summary中将仅仅列出与本机相关的网络数据包。
Other Traffic 则是网络上其他机器之间的网络数据包。因为正好在拦截期内经过本机,所以被顺道拦截了下来。
Capture Filter 是设定拦截数据时的过滤器。
Display Filter 是对拦截结果的过滤设定。
Select Networks 是设定需要拦截本机上的那一个网络。
Aliases用于设定友好名。
Frame Summary 中列出的是符合条件的所有网络数据包
Frame Details则是当前选中的网络数据包的详细结构
Hex Details 则是当前选中网络数据包的二进制格式

2.分析初始阶段C/S网络数据通讯
简单介绍了网络截包工具的使用之后,下面我们就开始初步分析了。
在这篇文章里,我以某款网络游戏作为假定目标。(具体是哪一款,大家就不要深究了。)
首先在【aliases】窗口中将本地客户端和游戏服务器分别命名为:MyComputer和GameServer。注意不要忘了点击【apply】按钮。

然后在【Display Filter】中输入如下语句,仅显示游戏客户端和服务器之间的数据包。数据包类型为TCP是因为网游通讯的协议是TCP协议。

下图中的数据包列表就是目标网游从输入用户名和密码登录游戏到人物出现在游戏中(然后立即退出。)这一阶段客户端和服务器之间的所有往来的数据包。

图中红线标识的三个包代表了一个连接的过程。注意它的TCP Flags的变化。
MyComputer  è  GameServer  .S…… 客户端请求建立连接
MyComputer  ç  GameServer  .S..A… 服务器同意建立连接
MyComputer  è  GameServer  ….A… 连接建立
以上三个包称为建立TCP连接的三段式握手。当你调用Socket类的Connect方法时就会产生上面的三个TCP包。

图中蓝线标识的是连接断开的过程。
MyComputer  è  GameServer  F…A… 客户端请求断开连接
MyComputer  ç  GameServer  ….A… 服务器同意断开请求
MyComputer  ç  GameServer  F…A… 服务器请求断开连接
MyComputer  è  GameServer  ….A… 客户端同意断开请求
调用Socket类的Disconnect方法时就会产生上面的四个TCP包。
从上图中我们不难看出在验证用户名和密码的过程中,客户端和服务器之间总共连接了二次,所以在之后的外挂程序编写过程中,我们同样也要连接二次。

TCP Flag为…PA…表示该TCP包内带有数据,而….A…则是回应包,用于回应上一个包的发送方:我已经收到你上一个包 了,它本身不带数据。所以一般一个…PA…包都有一个对应的….A…包(例如编号为266和269),但如果回应的时候,发现正好有数据 要发送,则可以将回应包掺杂在发送包中发送过去(例如编号为273的回应包就掺杂在275这个包内)。
下面观察客户端和服务器之间的实际数据往来。
1. 客户端连接到服务器
2. MyComputer  ç  GameServer  服务器给客户端发送7字节的数据
3. MyComputer  è  GameServer  客户端给服务器发送90字节的数据
4. MyComputer  ç  GameServer  服务器给客户端发送65字节的数据
5. MyComputer  ç  GameServer  服务器给客户端发送48字节的数据
6. MyComputer  è  GameServer  客户端给服务器发送48字节的数据
7. MyComputer  ç  GameServer  服务器给客户端发送208字节的数据
8. 服务器断开连接
9. ……
以 上就是第一次连接的大致过程。观察每个包内的具体传输数据是没有意义的,因为网游之间的通讯肯定是加密的,你每次拦截下来的数据都会不一样。通常游戏服务 器给客户端发送的第一个包都是KEY包(例如上面的7字节的包),客户端在接收到KEY包之后执行相应的数据加密初始化。所以接下来的任务就是根据已掌握 的数据通讯规律,对游戏客户端的加密算法进行破解了。

3.游戏加密算法破解
网 络游戏所使用的网络通讯函数肯定也是微软操作系统所提供的标准API函数,所以通常在接受网络数据的API函数中下一个断点,当接收到第一个7字节包时, 断点激活,然后逐渐跟进去,查看游戏客户端是如何处理该段数据的,然后我们在外挂中依样画葫芦,进行同样的处理。整个破解过程相当的枯燥无聊,因为面对的 都是汇编代码。大致的说一下。

4.Debug版本制作
破解完成之后,就要制作一个能够登录游戏的Debug版本了,用于确认游戏加密算法的破解是否成功。
至于选择何种编程语言和工具制作外挂则没有限定,常用的如VC,Delphi,等都可以易语言和VB不太推荐局限性太多~但并不是说语言不好,具体的编程在此就不具体说明了,可以根据个人的喜好所选择,
下面谈谈网游中数据通讯的基本单位:指令包。
所 谓指令包就是代表了一个最基本含义的数据包。比如游戏人物向左移动时,游戏客户端就会向服务器发送一个指令包(人物走路包),通知服务器更新游戏人物的坐 标。当游戏人物周围出现一个新的怪物时,服务器会向客户端发送一个指令包(怪物出现包),通知客户端在画面上绘制出该怪物。所以,可以说指令包就是客户端 和服务器之间所使用的通讯语言,而外挂的工作就是解析该种语言,然后模拟客户端和服务器端进行通讯。

各个游戏定义的指令包的格式都不一样,但一般一个指令包通常含有以下几个元素:
XX XX XX XX XX XX XX …
XX XX 红色部分通常与该指令包的长度相关。他可能是指整个指令包的长度,也可能是指他余下部分指令的长度,这需要根据游戏的具体情况来确定。
之所以专门要用一定空间来说明指令包的长度,这是由SOCKET通讯的机制所决定的。
SOCKET连接建立好之后,通过SOCKET连接读取到的数据并不是以指令包为分割的。有可能一个TCP包中正好包含一条指令包,也有可能仅仅包含指令包的一部分(如下图所示)。所以这时候就要根据指令包长度将收到的网络数据截取成单个的指令包。

有一点需要指出的是:刚开始的几个数据包不一定遵循一定的规律,这时候就需要进行特殊处理(因为在开头,所以也比较好处理),而之后的数据包肯定是遵循指令包格式的,不然就乱套了。

XX XX 蓝色部分通常称为指令包标识,用于说明该指令包是属于哪一种类型。比如怪物攻击包,玩家的移动包……,游戏客户端根据收到的相应指令包采取不同的动作。事实上,在客户端程序的内部就是一个很大的Switch语句,用来处理不同的指令包,如下所示:
Switch(指令包类型)
{
Case 移动包: 绘制相关人物的移动; break;
Case 攻击包: 绘制A攻击B的画面; break;

}

XX XX XX 部分是指令包详细的信息,该部分随着不同的指令包而有不同的格式。比如,如果是玩家移动包的话,他就会在此部分详细说明是哪个ID在移动,移动点是从哪儿到哪儿。

Debug成功制作完成的话,那我们就已经成功了一小半了。接下去的工作主要分为动态和静态二个方面。动态方面是根据已掌握的指令包格式,逐个分析游戏中的各个基本指令。静态方面则是从游戏客户端中解析出相应的资源。

③.网游基本指令分析
1.监控网游客户端的发送包和接受包
要分析网游指令,首先就要得到网游指令的数据样本。那么如何得到网游指令的数据样本呢?
首先想到的是使用网络截包工具,这种方式在网游发展早期的话还有一定的可行性,因为那时候网游大部分采用明文通讯,而现在的网游数据通讯肯定是加密的,所以使用网络截包工具截获到的数据毫无意义。
既然无法使用通用的工具,那我们只能把目光移向游戏客户端了,因为在游戏客户端内处理的肯定是未加密的数据。
像 之前的加密破解一样,跟踪游戏对网络数据的处理过程,分别找到游戏中对解密后的指令包的处理函数入口和游戏对要发送指令包进行加密的函数入口这二个地方, 然后修改这二处地方入口点的汇编指令,使之先调用我们编写的函数,然后再调用原始的过程。在我们自己编写的函数内部,分别记录下接收到的指令包和要发送的 指令包(如下图)。这样,网游客户端的发送包和接受包的监控就完成了。

2.分析网游指令的通用方法
能够获得网游指令样本数据之后,接下去就是实际的分析过程了。
一般而言,根据指令包所起的不同作用,可以将游戏指令包分为不同类别:

连接相关指令包 用于与游戏建立连接以及退出游戏时的指令包
玩家属性相关指令包 与玩家本身状态相关的指令包,在刚进入游戏系统时,服务器会发送过来大量的关于玩家角色基本信息的指令包。
环境相关指令包 由于告知玩家周围怪物/NPC/其他玩家状况的指令包
移动相关指令包 与走路相关的指令包
战斗相关指令包 与战斗相关的指令包
交易相关指令包 与交易相关的指令包
……

制作一款外挂,如果只要求基本功能的话,分析20,30个指令包应该就差不多了,如果要求功能比较完善的话,至多50个指令包也就差不多了。
具体分析的方法也很简单,无非是排除干扰因素,逐个击破,以及重复试验,确保分析结果正确。
下面的贴图是我对某一款游戏的28个指令包的具体分析。

三、游戏地图文件的破解与转换
地 图文件的转换也是外挂制作中的一项任务,外挂中使用的地图一般仅需要知道道路通行信息就可以,对于其他的视觉信息是没有必要的,因些我们需要对游戏中的地 图文件进行一定的转换工作。未被转换的游戏地图文件同样也是比较庞大的。由于不同的游戏运行商为了表达不同的游戏效果,因此其在地图文件上的格式也不尽相 同,我们必须分析某款游戏的具体的地图文件格式。
3.1 地图文件格式的分析 一般情况下地图文件并未被加密,但有些也可能进行了加密。地图文件格式的分析即可以直接分析地图文件数据,也可以通过跟踪游戏客户端 代码而破解出其实现过程。跟踪代码的方法同上述加密与解密算法的破解方法相似,所不同的是我们需要跟踪读写文件的函数。
3.2 外挂地图文件格式的组织 外挂地图文件格式是由外挂所需要的信息的要求决定的,在外挂中我们仅需要知道通路信息即可,因此外挂中的地图文件格式一般被组织 为R*C的格式。在R*C的每一格中记录地图在该点的通行状态,一般地我们可以定义通行状态为0阻塞状态为1。然而事实上有些外挂公司为防止其他外挂制作 者的破解,往往把地图文件进行一定的加密处理。

④.网游资源解析
游戏资源主要是指包含在网游客户端内的与游戏相关的数据资源:其中最重要的是地图资源,其他的资源包括NPC的位置坐标啊,地图传送点的坐标啊,等等。

1.地图资源解析
地图资源的解析是外挂制作中很重要的一个方面,寻路算法以及地图间的自动移动等都要依赖于地图资源。
外挂中所使用的地图资源不完全等同于游戏中使用的地图资源。游戏中使用的地图资源包含有更多的信息,而外挂所使用的地图称之为BOOL地图,它仅仅包含一项信息:指定的某一点是可移动点还是障碍点。

例如上图就是某个游戏地图所对应的BOOL地图,其中蓝色部分代表不可移动区域,白色部分代表可移动区域。(另外绿色的点是通过解析其他的资源文件获得的NPC的坐标点,红色点是传送点。)
那么如何获得游戏地图所对应的地图资源呢?通常在网游客户端的程序安装目录内会存在一个类似map或者res之类的目录。地图资源处在其中的可能性很大,而且地图资源本身的文件格式也比较明显,通常它都是以如下的格式存在的:

前面N个字节的地图头信息中肯定包含了地图的宽度信息和高度信息,设宽度和高度分别为W和H。后面的部分通常都是地图点信息。地图上的一个点通常由n个字节所构成,整个地图文件的大小由此计算而得: N + (W*H*n)。

所以判断是否是地图文件很简单,只要看一下文件的头,然后查看一下文件的大小。如果有一组文件满足上面的规律的话,那基本上可以肯定是地图资源文件了。
接 下来就是要把游戏地图转化为BOOL地图。前面提到地图文件中地图上的一个点通常有n个字节所构成,里面含有很多丰富的信息,而BOOL地图所关心的是否 是可移动点的信息通常仅需要用1位即可表示。(注意是位,1个字节有8位,所以n个字节总共有8n位)将一张游戏地图中所有点的该位信息收集起来组成的新 的地图就是BOOL地图。它标示了地图中那些区域玩家是可移动的,那些区域是不可移动的,为后面的寻路算法提供了基础数据。

原理明白了,接下去就是写一个程序解析地图资源。大致步骤如下:
1.  打开一个地图文件
2. 读入地图文件的头信息,解析出地图宽度和高度
3. 读 入地图点信息,对每一个点所代表的n字节数据执行位操作,提炼出其中某一位的信息,保存到自己的结构中。(此处我建议大家采用BMP格式的数据来保存提炼 出来的位信息,好处有三点:一是保存完之后,直接可以用图片浏览工具查看结果,不必自己再写一个绘制的程序,二是使用BMP格式保存的话,保存的数据容量 也小,三是在外挂中显示地图时可以将BMP图片直接作为背景图片贴在窗口上。)
由于之前你尚无法确定n字节中的哪一位代表了点的是否可移动属性,所以每一位你都要取一遍组成一幅地图,然后查看哪一幅和游戏地图最接近。多读几个地图文件做实验,很容易就可以确认下来的。

2.其他资源的解析
地图资源的解析是通过了解物理磁盘上地图文件的保存格式,然后自己写程序解析出来的,使用这种方法还可以解析其他很多资源信息。大家可以仔细观察游戏的安装目录,根据子目录和文件的名字可以分析出很多有用的信息。
但目前游戏厂商也越来越狡猾了,保存在磁盘上的资源文件通常进行了变形(压缩或者加密),使你无法通过简单的分析获得你所需要的信息。
一 种解决办法就是观察游戏是如何处理变型的资源文件的。因为在游戏中资源肯定是以原始形式存在的,通常都是在游戏初始化的时候,从磁盘上读入变形的资源文 件,然后将其恢复为原始状态的资源形式,我们就跟踪该段的处理过程,然后自己模仿写一段程序将变形资源恢复为原始资源形式。
另外一种方法就是直接 从游戏的内存中读取有用的资源信息。该方法的理论依据就是:资源信息在游戏中肯定是明文形态,而且是被有序组织的,也有部分游戏是加密过的,不过认真分析 一般抠取这些列表或者数据都不困难。比如,如果你想要获得所有游戏物品的信息列表,你可以随意选择一个物品名称,然后在游戏的内存中查找他的位置。所有的 物品在游戏内存中肯定是以某种链表的形式存在的,你只要找到了一个,就可以顺藤摸瓜,找到该链表的头,然后自己写一个程序,读写游戏的内存空间,将整个游 戏的物品列表全部读取出来。

四、外挂中智能AI部件的实现
游戏外挂智能AI部件的实现主要由算法与数据两部分组成,在一个具有推广意义的外挂中AI部分的设计是必不可少的。外挂AI部分的设计主要包括以下几方 面的内容:游戏脚本指令体系、自动行走算法、自动杀怪算法和综合控制算法,而在上述算法中相关配置文件的设计是相应算法设计不可缺少的组成部分。
由于智能AI部件的实现是一个比较复杂的事情,同时具体的实现过程可以因每个人理解不同而具有完全不相同的实现代码与实现的数据结构,但一般地它们还是具有一定的共性。
AI部分也是对一个人的分析能力的考验,下面先粗略得介绍一下几个算法的作用与意义,深入的探讨在具体的各个章节中进行。
4.1 游戏脚本指令体系 脚本是使外挂具有通用性的一个实现方法,也是对游戏角色进行控制的手段。脚本指令体系设计的好坏直接关系到外挂的功能与性能。
4.2 自动行走算法 完成自动行走是一个必备的功能,自动行走性能的好坏主要由算法决定。要实现自动行走首先必须找到行走的道路,然后再沿指定道路前进。寻路算法目前主要由A*算法来处理。
4.3 自动杀怪算法 自动杀怪也是一个必备的功能,自动杀怪性能好坏直接关系到升级速度的快慢。
4.4 综合控制算法 一部分由脚本完成一部分由程序处理,综合控制与性能最为相关,综合控制处理的好环直接影响升级的速度。
⑤.网游中的算法
1.寻路算法
外挂中最有名的,也最重要的一个算法就是寻路算法了。所谓寻路算法就是指给定一张地图数据,以及起始点和目标点,然后利用算法计算出一条路径来。它所依赖的数据基础是BOOL地图,这个我们在之前的讲述中已经成功获得了。下面讲一下具体的算法。
常用的寻路算法实现有二种,一种是A*算法,还有一种等高线算法。还记得在大学里面学过遍历图的二种算法吗,一种是深度优先,一种是广度优先。A*算法就对应了深度优先算法,而等高线算法则对应了广度优先算法。
A*算法是最常用的寻路算法了,不过它也有个很大的缺陷,那就是计算出来的路径通常是贴边的,所以如果你在游戏中观察用外挂控制的人物的走动的话,你会发现他通常是沿着障碍物的边走动的,走动起来显得很不自然。
A*算法和等高线算法在CSDN还有Gameres都有例子~这里就不重复了.

另外一点需要指出的是,寻路算法以及之前提到的BOOL地图的解析针对的都是2D的网游,那些纯3D的网游中的人物采用的是碰撞模型,一般3D游戏都是有高度的也就是Z坐标,它可以是正数也可以是负数。这个涉及的东西就多了去了这里就不在详说。

顺便提一下游戏中的走路。目前的2D网游中对于人物走动的处理方式主要有二种:一种是直接向服务器发送玩家要到达的目标地址,还有一种是以当前的坐标点为基点,给服务器发送相应的偏移量。
直 接发送目标地址的方式,如果网游服务器端做的不够严谨的话(没有对玩家要移动的地址和玩家当前地址之间的距离进行校验),可能会存在瞬移的BUG可供外挂 利用。(以前我就曾经碰到过一款有瞬移BUG的网游,利用外挂飞来飞去,飞得太猛了,后来就被游戏开发商给修正了。)
发送偏移量的移动方式见下图,是当前玩家所在的位置,如果玩家需要向上(即向北)移动一步的话,则向服务器发送偏移量7,如果要向斜向角(即西南方向)移动一步的话,则发送偏移量4。很明显,发送偏移量的移动方式不存在瞬移的可能性。

五、外挂配置文件的构造与设计
外挂配置文件的设计是属于外挂智能AI设计中的一部分。实现AI功能的基础一方面是算法,另一方面数据。算法的介绍集中在《游戏智能AI部件的实现》章节中讲述,这儿主要解述外挂中的配置文件的设计。
配置文件的支持直接与实现某种功能相关,支持的内容越全面那么与脚本配合后所能实现的功能越强大,并且对智能AI过程的设计起到帮助的作用。在一般游戏 外挂的配置文件中主要包括以下几部分内容,其分别为地图配置文件、过门配置文件、物品配置文件、怪物配置文件、NPC配置文件、装备配置文件、技能配置文 件和战斗配置文件。
5.1 地图配置文件 这部分内容由《游戏地图文件的破解与转换》的章节进行详细的讲解。地图配置文件的意义除了起显示作用外,其最重要的作为是提供寻路算法的数据支持。
5.2 过门配置文件 现在的游戏大部分是采取小幅地图切换的模式,图与图进行切换的点我们称之为过门。由于过门之间在数据上是不连续的,因些我们需要为些建立过门之间的联系。过门配置文件的意义主要是提供图与图之间寻路算法的数据支持。
5.3 物品配置文件 物品配置主要决定物品是否被捡取、丢弃、购买、出售、修理和物品所属种类等,物品的各类决定物品被何种NPC所处理。
5.4 怪物配置文件 怪物配置主要决定怪物是否被攻击、躲避及用什么形式攻击、多少等级间被攻击等。
5.5 NPC配置文件 NPC配置主要决定NPC在何图、何坐标、处理何类工作等。
5.6 装备配置文件 装备配置主要决定何种职业用何种装备、何等级别用何等装备等。
5.7 技能配置文件 技能配置主要决定何技能在何级别被何职业所学习与练习等。
5.8 战斗配置文件 战斗配置主要决定何级别该在何地图级别等。
外挂配置文件的具体构造形式主要由其内容所决定,同时可以考虑是否对文件进行加密处理。

2.其他算法
外挂中除了寻路算法之外,还有其他的一些算法应用,比如地图间移动算法:在已知各个地图间传送点的情况下,计算出从地图A移动到地图B所要经过的所有地图,这同样是一个经典的图论算法问题。
此外还有打怪时如何搜索最近怪物的算法,以及最有效的自动战斗的算法,这些算法要根据每款游戏的实际情况而进行相应的变动。

六、脚本解析器设计与游戏脚本指令
我们为什么要进行脚本解析器设计呢?其目的主要是解决外挂通用性的问题,我们知道有些游戏运行商时常会调整或扩增其游戏中的一些数据。如果我们把这些信 息硬编码在程序中,那么游戏运行商每修改一次数据,我们就要重新更改与编译外挂一次,这为外挂的推广使用带来了极大的不便。另外,脚本解析器也是作为智能 AI处理的一个部分,游戏行为的实现很大一部分依赖于脚本代码的书写。因此设计脚本解析器与游戏脚本指令体系是十分重要的,其中脚本指令体系更为重要。
6.1 脚本解析器的设计 脚本解析器设计是属于编译原理的范畴,大家如对编译原理有所掌握,那么设计一个脚本解析器是相当容易的。脚本解析器的设计是与脚本指令 体系相关的,离开了脚本指令体系的设计脚本解析器的设计也就免谈,因此在脚本解析器设计前我们首先需要设计好我们的脚本指令体系。
6.2 编译原理 编译原理是讲叙代码翻译的一门课程,编译原理主要涉及词法分析、语法分析、语法树构建、代码转译等方面的知识。现代的高级程序语言是属于形式语 言的,它是按一定的格式与规则进行书写,从而表达一定的行为与逻辑。而对于脚本解析器来讲所涉及的内容则较为狭窄,一般情况下脚本解析器被设计为解设执行 的程序体系,因此主要涉及词法分析与语法分析的内容。
6.2.1 词法分析 词法分析是指将我们编写的文本代码流解析为一个一个的记号,分析得到的记号以供后续语法分析使用。在词法分析中同样涉及到一错误的判断与处理。
6.2.2 语法分析 语法分析是将上述得到的记号按一定的规则进行检测,若符合某个规律则处理相应规律所对应的事情。语法分析最终可以将脚本代码的行为给解析出来,并最终完成脚本规定的行为。
6.3 脚本指令体系与组成 脚本指令体系是脚本设计中的核心,脚本指令体系设计的合理与优异与否直接与外挂能力与智能水平直接相关。一般情况下脚本支持的指令越 多则脚本所能实现的能力越大。任何外挂中使用的脚本主要具有以下几个要素:自定义变量支持语句、类型识别能力、赋值语句、脚本流控制语句、系统变量支持语 句、数值和字符串运算语句、比较语句及游戏操控语句等。
6.3.1 自定义变量支持语句 主要考虑变量的作用域与变量类型的问题。变量作用域在外挂中主要分为全局与局部两类。变量类型在外挂中主要分为数值型、字符串型和时间型三类。另外变量所能接收数据的长度也必须给予考虑。
6.3.2 类型识别能力 这类处理可以用显性或用非显性的方法进行处理,显性处理较为符合目前大多数编程语言的习惯。
6.3.3 赋值语句 分为数值型、字符串型与时间型三类数据的赋值问题。一般采用主流程序使用的方法,这种方式的用户较为广泛。
6.3.4 脚本流控制语句 分为条件语句、多分支语句、真性循环语句、假性循环语句、计数循环语句和强制跳转语句。
6.3.5 系统变量支持语句 与实际的游戏相关。比如游戏中角色的职业、级别等,另外也可以设定游戏中没有的但我们使用频繁的变量。系统变量的多少直接与脚本所能支持的功能和执行的性能有关。
6.3.6 数值和字符串运算语句 数值间有加减乘除等运算,字符串有相连定位查找等运算,时间有加减等运算。
6.3.7 比较语句 游戏中的比较语句主要包括数据间的比较、字符串的比较和时间的比较。比较方式主要有大小与相等。
6.3.8 游戏操控语句 与实际的游戏相关,比如买、卖、修、存、取、捡、丢、用、走、砍、挖、杀等,具体格式由游戏决定。
⑥.题外话
网游外挂产业真的是一个很有趣的领域,他主要是包含二个方面:技术和营销。
技 术方面,网游外挂所涉及的技术之广并不逊色于一款网游所涉及的技术。其中技术难度最大的还是破解方面,据闻中国几大顶尖的破解组织高手大部分都与外挂破解 有或多或少的联系。近几年随着大部分的游戏都运用了NProtect,在与NProtect的逐渐斗法过程中,战场从RING3层逐渐转移到系统内核层, 也着实培养出了一批对于Window内核有深刻理解的高手,国人之幸啊。
讨论网游的技术是一个很有趣的话题,但个人认为更有趣的方面在于网游外挂 的营销。由于近几年网游外挂制作团队的大量涌现。同一款游戏常常有着好几家外挂在互相竞争。这时候的外挂营销就像一个硝烟弥漫的战场,你不仅要与外挂同行 竞争,还要和游戏发行商互相斗法,同时随着近几年相关法律的健全,你还要小心自己不要被相关政府部分盯上。
在与同行竞争的过程中,不仅要比拼技术 (游戏采用了更严密的保护手段,你还要能破解它),比拼反应时间(游戏客户端有了更新,你要迅速反应),更要比拼营销手段,推出各种各样的优惠套餐,尽可 能的拉拢住更多的客户,比如,如果外挂单月的收费是20,则半年只要100,全年只要180,这样表面上你好像少收了钱,但实际上你把客户和你牢牢的绑定 住了,即使竞争对手推出更好更便宜的外挂,你也不用害怕,因为你有足够的时间来反应。类似的手段还有很多,这主要看你的营销水平了。
同时还要处理 好与竞争对手之间的关系,能很快消灭对手的,则绝不留情。明显不如对手,且对手满怀敌意的,则干脆开放免费版,让大家都不好受。势均力敌的,能结成同盟的 最好达成价格联盟,避免互相砍价给彼此造成损失。互相敌视的,则要尽量给竞争对手下绊子,比如攻击对手的站点啊,到他的论坛上面捣乱啊……,总之就是想尽 办法击败对手,取得一家独大的场面,一旦你能在一款游戏里面吃独食,形成垄断,那外挂的价格就随你定了。够你爽的。
外挂的营销就像一个群雄并起的战国时代,大家各施手段,各凭本事,看谁能笑到最后。这里面发生过很多有趣的故事。不过近几年国内相关法律越来越严格了,所以并不建议大家踏入这个泥水潭。

[转载]T-SQL之公用表表达式(CTE)

mikel阅读(925)

[转载]T-SQL之公用表表达式(CTE) – 我的成长点滴 – 博客园.

以前也写过几篇,后来总算觉得写得不够好,写了点又删了点,最后一直没有东西留下来,随着时间的流逝,几乎没有积累。最近在看T-SQL相关的 书,结合工作中遇到的问题,我打算写点东西来记录我的学习经历,希望大家指出我的不对的地方,希望一起探讨开发中的问题。小弟再次谢过了。
“T-SQL相关的”
废话就不多说了,公用表表达式(Common Table Expressions), 是SQL SERVER支持的一种类型的表表达式。
CTE的语法如下:
WITH
<cte_name> [(<target_col_list>)]
AS
(
  <定义CTE的内部查询>
)
<对CTE进行外部查询>;
CTE的语法,从WITH开始,首先在括号里定义CTE内部查询,然后在外部查询引用CTE的名称。
对CTE的内部查询表达式,有如下规则:
  1.查询必须是一个有效的表;
  2.所有的列必须要有名称;
  3.所有的列名必须唯一;
4.不允许使用order by(除非同时指定了top,原因很简单,因为order by之后,返回的是游标,但是如果同时指定了top,则order by可以当作是top的排序方式)。
下面是一个CTE的例子。
use Northwind; go with EmployeeYearOrder as ( select (e.FirstName+N' '+e.LastName) as employeename,year(o.orderdate) as theyear,COUNT(o.OrderID) ordernum from dbo.Orders o inner join dbo.Employees e on o.EmployeeID = e.EmployeeID group by e.FirstName+N' '+e.LastName,year(o.orderdate) ) select * from EmployeeYearOrder cur left join EmployeeYearOrder pre on cur.theyear = pre.theyear+1

上边的例子是多引用的CTE,利用CTE定义了每年的雇员的订单的数目,在外部查询中,通过两次引用EmployeeYearOrder ,一个代表当前年份,一个代表上一年。

如果使用派生表的话,代码是如下组织的:

use Northwind; go select * from ( select (e.FirstName+N' '+e.LastName) as employeename,year(o.orderdate) as theyear,COUNT(o.OrderID) ordernum from dbo.Orders o inner join dbo.Employees e on o.EmployeeID = e.EmployeeID group by e.FirstName+N' '+e.LastName,year(o.orderdate) )as cur left join ( select (e.FirstName+N' '+e.LastName) as employeename,year(o.orderdate) as theyear,COUNT(o.OrderID) ordernum from dbo.Orders o inner join dbo.Employees e on o.EmployeeID = e.EmployeeID group by e.FirstName+N' '+e.LastName,year(o.orderdate) ) as pre on cur.theyear = pre.theyear+1;

注意上边的查询,核心查询的部分重复了两次。查询越复杂,引用次数越多,基于CTE的解决方案越有优势。当然从性能上来讲,这两种写法,经过查询优化器分析后,最终都得到同样的执行计划。

还有多CTE的情况。CTE不允许直接嵌套,但是可以用同一个WITH定义多个CTE,从而得到和嵌套派生表相同的效果,但是却没有嵌套派生表那么复杂。

看下边的查询:

多CTE的解决方案,返回的是每个雇员每一年处理的订单的数目
with c1 as ( select YEAR(orderdate) as theyear,(e.FirstName+N' '+e.LastName) as employeename,OrderID from dbo.Orders as o inner join dbo.Employees e on o.EmployeeID = e.EmployeeID ), c2 as ( select theyear,employeename,COUNT(OrderID) ordernum from c1 group by theyear,employeename ) select employeename,theyear,ordernum from c2;

就是一个多CTE的例子,看起来比多层嵌套的派生表要直观。

CTE最大的用处,我觉得还是递归查询。

还是给出一个例子。

WITH Emps AS ( SELECT empid, mgrid, firstname, lastname FROM HR.Employees WHERE empid = 5 UNION ALL SELECT Emp.empid, Emp.mgrid, Emp.firstname, Emp.lastname FROM Emps AS Mgr JOIN HR.Employees AS Emp ON Emp.mgrid = Mgr.empid ) SELECT * FROM Emps;

这段查询将返回每位经理的直接下属。

如上查询所示,递归的CTE,必须包含至少两个查询。第一个查询被成为定位点成员, 它只是一个返回有效表的查询,作为递归的基础或定位点。而第二个查询则成为递归成员,是该查询成为递归成员的是对CTE名称的递归引用。如果担心循环的发 生,则可以指定option(maxrecursion n)来限制递归成员的调用次数。关于CTE的更多应用和深入理解,有机会再深入去讲解。

对于T-SQL公用表表达式,就讲到这里,有什么问题,还请大家指出,一起探讨。

参考文献:《Microsoft SQL Server 2008 技术内幕:T-SQL查询》

[转载]T-SQL逻辑处理之表运算符

mikel阅读(1006)

[转载]T-SQL逻辑处理之表运算符 – 我的成长点滴 – 博客园.

在前两篇T-SQL查询处理详解, T-SQL查询处理详解 (续)中,我较为详细地介绍了在一般查询中忽略实际的优化执行计划的情况下的逻辑处理过程。接下来,我将从表运算符,连接查询,分析函数,子查询等方面入手,深入地探讨这些细节,欢迎大家一起讨论T-SQL开发中遇到的问题。废话不多说,开始了。
SQL SERVER 2008支持4种表运算符,JOIN,PIVOT,UNPIOVT,APPLY,。这四种运算符,JOIN大家肯定不陌生,但是后面的几个,估计就有不少人很少使用了。
一.PIVOT。
首先看这样的一条SQL查询。

SELECT EmployeeID,
SUM(CASE WHEN theyear = 2006 THEN themoney END) AS [2006],
SUM(CASE WHEN theyear = 2007 THEN themoney END) AS [2007],
SUM(CASE WHEN theyear = 2008 THEN themoney END) AS [2008],
SUM(CASE WHEN theyear = 2009 THEN themoney END) AS [2009],
SUM(CASE WHEN theyear = 2010 THEN themoney END) AS [2010],
SUM(CASE WHEN theyear = 2011 THEN themoney END) AS [2011]
FROM (SELECT EmployeeID, YEAR(Orderdate) AS theyear, themoney
FROM dbo.Order) AS O
GROUP BY EmployeeID;
这种行转列的需求,在实际情况中特别常见,尤其是在提取年度或者月度 报表的数据时,基本少不了这种结果集。看看上面的这条查询,是不是觉得不是很好。不好在哪里呢?有两点,1.代码量大,而且重复代码太多,碰到很多行要转 成列的情况,就非常麻烦了。在项目里,经常碰到做月度报表,需要把12个月的数据利用这种写法转成列,就让人吐血了;2.执行效率不高。
这时,我向您介绍T-SQL中特有的表运算符,PIVOT。Pivot运算符用于在列和行之间对数据进行旋转或透视转换,同时执行聚合运算。对于上面的需求,用PIVOT可以如下实现相同的功能:
SELECT * FROM (SELECT EmployeeID, YEAR(Orderdate) AS theyear, themoney
FROM dbo.Order) AS O
PIVOT(
SUM(themoney) FOR theyear IN([2006],[2007],[2008],[2009],[2010],[2011])) AS P;
怎么样,是不是简洁多了?
来简要分析一下PIVOT的执行过程。PIVOT运算符用一个名为O的表表达式作为它的左输入,每个订单占一行,包含EmployeeID和theyear和themoney。
PIVOT操作涉及到一下三个逻辑阶段。
1.分组。来看看PIVOT子句,我们发现,PIVOT运算符引用了表O(给表取的别名)的两个列作为输入参数,分别是themoney和 theyear。在这个阶段,会隐式对表O中的行进行分组,分组依据是根据那些没有作为pivot输入参数的所有列。所以,就相当于有一个隐藏的 group by EmployeeID一样,最后结果集就是每个EmployeeID作为一个组。
2.扩展。这个阶段是把原始列的值扩展到他们相应的目标列中,在逻辑上,相当于为IN子句中指定的每个目标列使用以下case表达式:
CASE WHEN <spreading_col> = <target_col_element> THEN <expression> END.
注意一点,不带ELSE的CASE子句相当于包含了一个隐式的ELSE NULL。
3.聚合。这个阶段就是对每个CASE表达式应用指定的聚合函数,对于上边的查询,就是SUM(),并生成结果列。
对于PIVOT的更多的应用,有机会再深入了解。
二.UNPIVOT。
很明显,UN这个前缀表明了,它做的操作是跟PIVOT相反的,即列转行。UNPIVOT操作涉及到以下三个逻辑处理阶段。
1,生成副本
2,提取元素
3,删除带有NULL的行
看看这个MSDN上的例子。

CREATE TABLE pvt (VendorID int, Emp1 int, Emp2 int,
Emp3
int, Emp4 int, Emp5 int);
GO
INSERT INTO pvt VALUES (1,4,3,5,4,4);
INSERT INTO pvt VALUES (2,4,1,5,5,5);
INSERT INTO pvt VALUES (3,4,3,5,4,4);
INSERT INTO pvt VALUES (4,4,2,5,5,4);
INSERT INTO pvt VALUES (5,5,1,5,5,5);
GO
Unpivot the table.
SELECT VendorID, Employee, Orders
FROM
(
SELECT VendorID, Emp1, Emp2, Emp3, Emp4, Emp5
FROM pvt) p
UNPIVOT
(Orders
FOR Employee IN
(Emp1, Emp2, Emp3, Emp4, Emp5)
)
AS unpvt;
GO
简单对上边的查询作一个分析。UNPIVOT的输入是左表表达式P,第一步,先为P中的行生成多个副本,在UNPIVOT中出现的每一列,都会 生成一个副本。因为这里的IN子句有5个列名称,所以要为每个来源行生成5个副本。结果得到的虚拟表中将新增一个列,用来以字符串格式保存来源列的名称 (for和IN之间的,上面例子是Employee )。第二步,根据新增的那一列中的值从来源列中提取出与列名对应的行。第三步,删除掉结果列值为null的行,完成这个查询。
三.APPLY。
APPLY运算符是把右表表达式应用与左输入中的每一行。对于左表中的每一行,都要计算一次右边输入的表达式。
APPLY 有两种形式:CROSS APPLY 和 OUTER APPLY。CROSS APPLY 仅返回外部表中通过右表表达式生成结果集的行。OUTER
APPLY 既返回生成结果集的行,也返回不生成结果集的行,其中右表表达式生成的列中的值为 NULL。(外部表:左边表,内部表:右表表达式)

以下示例查询为每个客户返回具有最大订单ID的两个订单:
SELECT C.customerid, C.city, A.orderid
FROM dbo.Customers AS C
CROSS APPLY
(SELECT TOP (2) O.orderid, O.customerid
FROM dbo.Orders AS O
WHERE O.customerid = C.customerid
ORDER BY orderid DESC) AS A;
如果将CROSS APPLY换成了OUTER APPLY,则会把不满足右表表达式的行添加进来,并给内部表的列设为NULL。
欢迎大家一起探讨T-SQL开发,并提出问题,一起讨论。
参考文献:《Microsoft SQL Server 2008 技术内幕:T-SQL查询》

[转载]分享45个海量免费电子书下载网站

mikel阅读(1172)

[转载]分享45个海量免费电子书下载网站 – 梦想天空 – 博客园.

随着网络和信息技术的快速发展,电子书越来越流行。以Amazon Kindle为代表的电子书阅读器的出现改变了人们传统的阅读方式,如同iPod改变人们听音乐一样。如今,很多网上书店也推出了电子书商品,相比传统的 纸质书,电子书便携、容易使用、大容量的特点非常适合现代生活,用户可以以更低的价格方便的购买到更多的图书,为电子书的流行奠定了基础。今天要与大家分 享的是45个非常有用的免费电子书下载网站,还等什么呢,赶紧淘书去吧:)

Ebooklink.net

ebooklink.net

一个免费电子书搜索引擎,超过200,000本电子图书供下载,有计算机,经济,健康,小说等各种类别。

SearchPDFebooks
SearchPDFebooks

一个海量免费电子书搜索引擎,超过255,000,000免费PDF格式电子书供下载

Scribd
image
一个非常流行的在线文件共享社区,您可以在线阅读电子书和下载PDF、TXT或DOC格式的电子书。

University of Virginia library

image

最好的在线图书馆之一,可通过搜索查找电子书。

Diesel eBooks
Diesel ebooks
提供了供Microsoft Reader和Mobipocket Reader阅读的免费电子书,有计算机类的电子书。

PDFgeni
PDFgeni
一个免费的PDF搜索引擎,输入书名或者你要查找的书的关键词。

Globusz
clip_image020
可以在线免费阅读图书,你也可以下载你喜欢的电子书。

Free-ebooks
Free-ebooks
可以下载小说,健康,计算机和互联网等很多完全免费的电子书。

Freebookspot
Freebookspot
汇集从互联网上收集的免费电子书链接。

Getfreeebooks

Getfreeebooks

一个免费的高质量电子书下载网站,你也可以分享您的电子书给Getfreeebooks供其它人下载。

Gotenberg

image

一个优质的电子图书网站,提供可在iPad、Kindle、Android、iPhone和iPod上阅读的电子书下载。

Ebookpdf
clip_image022
超过100,000本电子图书供免费下载。

KnowFree

image

KnowFree是一个电子书门户网站,用户可以相互交换免费电子书,视频教程或者其他资料。

Onlinecomputerbooks
clip_image027

可以找到计算机和互联网,设计,编程等很多主题的图书。

Freecomputerbooks
clip_image018
免费下载电脑,编程,工程,数学,讲义和教程等类别的电子书。
EBook3000
EBook3000
一个超大的免费电子图书下载网站,有商业,技术,规划,设计等很多类别。

Freetechbooks
clip_image019

免费在线计算机科学与编程电子书籍。

EBooksLab
EBooksLab
主要以计算机和医疗方面的电子书为主。
Ebookdirectory
clip_image024
超过20,000本电子书供免费下载,一个对计算机和互联网从业人员非常有用的网站。

Witguides

image

一个分类电子书下载库,列出了下载数最多的前十本电子书。

4eBooks

image

4eBooks有非常多的计算机编程方面的电子书,每个供下载的电子书都有一个简短描述。

Freebookcentre
Freebookcentre
免费下载技术类图书,其中包括计算机科学,编程,移动技术,物理学,医学等类别的电子图书。EBooksread
EBooksread
一个免费的在线图书馆,在那里你可以阅读和下载电子图书。

Readeasily

Readeasily

提供在线阅读的网上图书馆。

Downloadfreepdf
Downloadfreepdf
下载免费的PDF格式电子图书,并下载用于在Pocket PC和Palm设备访问的电子图书。
Acrobatplanet
Acrobatplanet
一个收集各种类别免费PDF电子书的网站。

Ebooksdownloadfree
可以免费下载医疗,搜索引擎优化,小说和电脑类免费电子图书,还有一个流行电子图书排行。

PDf-search-engine

image

一个在线查找PDF格式电子书的网站。

Ebook Delicate

image

提供了用户可以自由分享他们的电子书的免费平台,这个网站没有实际的文件存储。

Zillr
clip_image025
免费电子图书门户。
Memoware
clip_image026
电子图书和掌上电脑阅读的文件。

Upenn.edu
clip_image031

超过35000本电子图书供免费下载。

Ebooksdownloadfree
Ebooksdownloadfree
可以免费下载医疗,搜索引擎优化,小说和电脑类免费电子图书,还有一个流行电子图书排行。
Ebookshare
Ebookshare
另一个免费下载电子书的网站。
PlanetPDF
PlanetPDF
一个PDF用户社区,在那里你可以找到像PDF转换工具,PDF编辑器来在线编辑你的PDF文件, 同时可下载流行的电子书。
Ebooksdirectory
E-booksdirectory
拥有大约2800本免费电子书,覆盖了400多个类别。
Onlinefreeebooks
clip_image023
一个简单的电子图书网站,提供免费电子书下载。
Manybooks
Manybooks
ManyBooks为您的掌上电脑,iPod或电子书阅读器提供免费电子书

Fictionwise
clip_image033
供Palm,掌上电脑,和Mac等平台的免费电子书。

E-books
clip_image034
免费下载PDF、CHM格式图书。

BesteBooksWorld

image

涉及题材非常广泛的电子图书网站。
Ufindbook
clip_image035
免费下载电子图书。
Snipfiles
clip_image028
提供免费电子书和软件。
Bookyards
clip_image029

一个在线图书馆,提供免费下载。
Asksam
clip_image030
免费下载电子图书和免费检索的数据库。
Ebooklobby
clip_image032
艺术,商业,电脑,烹饪,法律,体育,旅游等各种类别的免费电子图书,同时列出了最受欢迎的十大电子书。

(编译来源:梦想天空 原文来自:45+ Free Ebook Download Web Sites of Information Overload

[转载]T-SQL查询处理详解 (续)

mikel阅读(792)

[转载]T-SQL查询处理详解 (续) – 我的成长点滴 – 博客园.

在上一篇T-SQL查询处理详解, 文中提到了在如果不考虑查询优化器做的优化的情况下T-SQL查询的逻辑处理过程,讲得很粗糙,这一篇将补充说明这个逻辑处理过程,并对一些容易误解的地 方进行说明。不对之处,还请指出。另外再次声明一点,这种逻辑处理顺序,是理论上的处理过程,实际情况还会根据查询优化器选择最优的执行计划。

还是从FROM阶段讲开。

(1)FROM阶段

这个阶段是查询逻辑处理的第一步。想到这里,想起了LINQ表达式不就是从FROM开始的嘛,看来还是挺有道理的。FROM阶段负责表示表或要查询的表。 如果指定了表运算符,还需  要按从左到右的顺序,对运算符进行逐个处理。表运算符有4类,JOIN,APPLY,PIVOT,UNPIVOT。每个表运算符都有自己的处理规则。这里 挑最常见的JOIN来说。

对于联接(join),一般有以下几个个步骤:

a.求笛卡尔积,对两张表进行cross join,得出最大的可能结果集。如果左表有n行,右表有m行,则结果集有nxm行。

b.利用ON筛选条件来筛选,剔除不符合条件的行。

c.对于外联接(left,right,full outer join),还需要添加外部行。在上个步骤中,ON条件剔除掉了所有不匹配两张表的行。但是在外联接中,通过指定外连接的类型,需要将其中的一个或者两个 表标记为保留表,并返回该表中所有的行。所以这时候还需要将保留表中被ON筛选条件剔除的行重新加入到结果集中(这些重新加进来的表,书中称为外部行), 并将外部行中非保留表的列值标记为NULL.再次提醒一下,这一个步骤,只有外联接才执行,对于内联接(inner join)只需要执行a和b两个步骤的。

(2)WHERE阶段

对于上一步骤返回的虚拟表,经过where条件的判定,只有让where条件为true的行才会被保留下来。请注意,因为还没有对数据进行分组,所以在 where子句中不能聚合。也不能引用select列表中创建的别名,因为SELECT阶段还在后头呢。例如where orderid>max(orderid), select year(thedate) as theyear… where theyear>2010是不能使用的。

另外一个很让人迷惑的问题是,对于包含JOIN的查询,到底ON和WHERE子句有什么区别,应该什么时候使用ON子句,什么时候使用where子句。这里作一下说明。只有对于外联接,ON和where子句才会存在这种逻辑区别, 因为在外联接中,通过ON子句的筛选之后,还要对保留表进行外部行添加,而where子句则是在外部行添加过之后才进行筛选的。因此,ON子句对这种外联 接的情况的筛选,并不是最终的结果,在FROM阶段的第三个步骤,还会把外部行添加回来的。而对于内联接,where子句和on子句作用是完全一样的,在 哪里筛选都是同种效果,没有其他步骤。所以在处理这种含有外联接的查询,一定要注意ON筛选和where筛选的区别,避免使用错了,达不到筛选的效果。另 外,对于内联接,一个不错的建议是,对于两个表都存在的字段筛选,用ON子句,对于单个表的字段筛选,用where,例如:select * from a inner join b on a.col = b.col where a.col2 >1。

(3)GROUP BY阶段

在这一阶段将上一步返回的虚拟表中的 结果集按分组进行重组,由分组集所有列的每个唯一组合,标识出一个组。再用这些组,跟上一步返回的每一个行进行关系。注意,每个行只能关联一个组。最后, 生成的结果集中,每个组只能有一行。关于GROUP BY还有很多有意思的地方,比如cube,rollup,grouping等等,有时间再一一介绍。

(4)HAVING阶段

HAVING筛选器用于对上一步返回的结果集进行筛选。HAVING筛选器是唯一能筛选分组数据的筛选器,ON和where都不行。理由很简单,ON和where都是在分组之前进行处理的,自然不能对分组进行筛选。所以HAVING和WHERE的区别,也是很显而易见了。HAVING 只能与 SELECT 语句一起使用。HAVING 通常在 GROUP BY 子句中使用。如果不使用 GROUP BY 子句,则 HAVING 的行为与 WHERE 子句一样。

(5)SELECT阶段

这一个步骤,将构造最终返回给调用者的表。这个步骤涉及到3个子阶段。

a.计算表达式。在这个阶段中,select列表可以返回油上一步得到的虚拟表的基础列,也可以是对这些基础列的操作。有一点需要注意的是,在这个select列表中,所有的表达式是同时计算的。举个例子,在SQL中,可以这么交换两个列值:update tab_test set col1 = col2,col2 =col1;在别的语言看来这的确很神奇。

而且在select列表中创建的别名不能在同一select列表中的其他表达式中使用。所以,基于这个特性,我们就会得出一个结论,select列表的顺序是无关紧要的。

b.处理DINSTINCT,果查询中指定了DINSTINCT,则从上一步返回的虚拟表中删除重复的行。

c.应用TOP选项。对于指定了TOP选项的查询,则会根据查询的order by子句来选择指定数量的行。TOP选项里有很多特殊的地方,此处先不扯远,以后有机会单独讲。

(6)ORDER BY阶段

这一步按照order by列表中的列明对上一步的表进行排序,返回游标。

这里有必要谈一下集合和游标。SQL的理论基础是集合,集合是无序的,它只是成员的一种逻辑集合。对于带有排序作用的ORDER BY子句的查询,可以返回一个对象,其中的行按照特定的顺序组织在一起。ANSI把这种 对象成为游标(cursor)。

因为在这一步中,最后返回的是游标,所以带有order by的查询,是不能用来定义视图,子查询,公用表等。例如:

SELECT * FROM (SELECT col1,col2 FROM tab_test ORDER BY col1)是无效的,并且将报错。

但是如果同时指定了TOP选项,则是一个例外。SELECT * FROM (SELECT top (10) col1,col2 FROM tab_test ORDER BY col1),对于这个查询,因为同时指定了top和order by,则子查询中的结果一定是固定而且有序的的,但是外部的查询,则不能保证是有序排列的。

对于T-SQL查询逻辑处理,就整理到这里,有什么问题,还请大家指出,一起探讨。

参考文献:《Microsoft SQL Server 2008 技术内幕:T-SQL查询》

[转载]MongoDB学习笔记(三) 在ASP.NET MVC模式下通过Jqgrid表格操作MongoDB数据

mikel阅读(835)

[转载]MongoDB学习笔记(三) 在MVC模式下通过Jqgrid表格操作MongoDB数据 – lipan – 博客园.

看到下图,是通过Jqgrid实现表格数据的基本增删查改的操作。表格数据增删改是一般企业应用系统开发的常见功能,不过不同的是这个表格数据 来源是非关系型的数据库MongoDB。noSQL虽然概念新颖,但是MongoDB基本应用实现起来还是比较轻松的,甚至代码比基本的ADO.net访 问关系数据源还要简洁。由于其本身的“非关系”的数据存储方式,使得对象关系映射这个环节对于MongoDB来讲显得毫无意义,因此我们也不会对 MongoDB引入所谓的“ORM”框架。

下面我们将逐步讲解怎么在MVC模式下将MongoDB数据读取,并展示在前台Jqgrid表格上。这个“简易系统”的基本设计思想是这样的: 我们在视图层展示表格,Jqgrid相关Js逻辑全部放在一个Js文件中,控制层实现了“增删查改”四个业务,MongoDB的基本数据访问放在了模型层 实现。下面我们一步步实现。

一、实现视图层Jqgrid表格逻辑

首先,我们新建一个MVC空白项目,添加好JQueryJQueryUI、Jqgrid的前端框架代码:

然后在Views的Home文件夹下新建视图“Index.aspx”,在视图的body标签中添加如下HTML代码:


接着新建Scripts\Home文件夹,在该目录新建“Index.js”文件,并再视图中引用,代码如下:

jQuery(document).ready(function () {

//jqGrid初始化
jQuery("#table1").jqGrid({
url: '/Home/UserList',
datatype: 'json',
mtype: 'POST',
colNames: ['登录名', '姓名', '年龄', '手机号', '邮箱地址', '操作'],
colModel: [
{ name: 'UserId', index: 'UserId', width: 180, editable: true },
{ name: 'UserName', index: 'UserName', width: 200, editable: true },
{ name: 'Age', index: 'Age', width: 150, editable: true },
{ name: 'Tel', index: 'Tel', width: 150, editable: true },
{ name: 'Email', index: 'Email', width: 150, editable: true },
{ name: 'Edit', index: 'Edit', width: 150, editable: false, align: 'center' }
],
pager: '#div1',
postData: {},
rowNum: 5,
rowList: [5, 10, 20],
sortable: true,
caption: '用户信息管理',
hidegrid: false,
rownumbers: true,
viewrecords: true
}).navGrid('#div1', { edit: false, add: false, del: false })
.navButtonAdd('#div1', {
caption: "编辑",
buttonicon: "ui-icon-add",
onClickButton: function () {
var id = $("#table1").getGridParam("selrow");
if (id == null) {
alert("请选择行!");
return;
}
if (id == "newId") return;
$("#table1").editRow(id);
$("#table1").find("#" + id + "_UserId").attr("readonly","readOnly");
$("#table1").setCell(id, "Edit", "<input id="Button1" onclick="Update(\&quot;&quot; + id + &quot;\&quot;)" type="button" value="提交" /><input id="Button2" onclick="Cancel(\&quot;&quot; + id + &quot;\&quot;)" type="button" value="取消" />");
}
}).navButtonAdd('#div1', {
caption: "删除",
buttonicon: "ui-icon-del",
onClickButton: function () {
var id = $("#table1").getGridParam("selrow");
if (id == null) {
alert("请选择行!");
return;
}
Delete(id);
}
}).navButtonAdd('#div1', {
caption: "新增",
buttonicon: "ui-icon-add",
onClickButton: function () {
$("#table1").addRowData("newId", -1);
$("#table1").editRow("newId");
$("#table1").setCell("newId", "Edit", "<input id="Button1" onclick="Add()" type="button" value="提交" /><input id="Button2" onclick="Cancel(\&quot;newId\&quot;)" type="button" value="取消" />");
}
});
});

//取消编辑状态
function Cancel(id) {
if (id == "newId") $("#table1").delRowData("newId");
else $("#table1").restoreRow(id);
}

//向后台ajax请求新增数据
function Add() {
var UserId = $("#table1").find("#newId" + "_UserId").val();
var UserName = $("#table1").find("#newId" + "_UserName").val();
var Age = $("#table1").find("#newId" + "_Age").val();
var Tel = $("#table1").find("#newId" + "_Tel").val();
var Email = $("#table1").find("#newId" + "_Email").val();

$.ajax({
type: "POST",
url: "/Home/Add",
data: "UserId=" + UserId + "&amp;UserName=" + UserName + "&amp;Age=" + Age + "&amp;Tel=" + Tel + "&amp;Email=" + Email,
success: function (msg) {
alert("新增数据: " + msg);
$("#table1").trigger("reloadGrid");
}
});
}

//向后台ajax请求更新数据
function Update(id) {
var UserId = $("#table1").find("#" + id + "_UserId").val();
var UserName = $("#table1").find("#" + id + "_UserName").val();
var Age = $("#table1").find("#" + id + "_Age").val();
var Tel = $("#table1").find("#" + id + "_Tel").val();
var Email = $("#table1").find("#" + id + "_Email").val();

$.ajax({
type: "POST",
url: "/Home/Update",
data: "UserId=" + UserId + "&amp;UserName=" + UserName + "&amp;Age=" + Age + "&amp;Tel=" + Tel + "&amp;Email=" + Email,
success: function (msg) {
alert("修改数据: " + msg);
$("#table1").trigger("reloadGrid");
}
});
}

//向后台ajax请求删除数据
function Delete(id) {
var UserId = $("#table1").getCell(id, "UserId");
$.ajax({
type: "POST",
url: "/Home/Delete",
data: "UserId=" + UserId,
success: function (msg) {
alert("删除数据: " + msg);
$("#table1").trigger("reloadGrid");
}
});
}

二、实现控制层业务

在Controllers目录下新建控制器“HomeController.cs”,Index.js中产生了四个ajax请求,对应控制层也有四个业务方法。HomeController代码如下:

public class HomeController : Controller
{
UserModel userModel = new UserModel();
public ActionResult Index()
{
return View();
}

///
/// 获取全部用户列表,通过json将数据提供给jqGrid
///
public JsonResult UserList(string sord, string sidx, string rows, string page)
{
var list = userModel.FindAll();
int i = 0;
var query = from u in list
select new
{
id = i++,
cell = new string[]{
u["UserId"].ToString(),
u["UserName"].ToString(),
u["Age"].ToString(),
u["Tel"].ToString(),
u["Email"].ToString(),
"-"
}
};

var data = new
{
total = query.Count() / Convert.ToInt32(rows) + 1,
page = Convert.ToInt32(page),
records = query.Count(),
rows = query.Skip(Convert.ToInt32(rows) * (Convert.ToInt32(page) - 1)).Take(Convert.ToInt32(rows))
};

return Json(data, JsonRequestBehavior.AllowGet);
}

///
/// 响应Js的“Add”ajax请求,执行添加用户操作
///
public ContentResult Add(string UserId, string UserName, int Age, string Tel, string Email)
{
Document doc = new Document();
doc["UserId"] = UserId;
doc["UserName"] = UserName;
doc["Age"] = Age;
doc["Tel"] = Tel;
doc["Email"] = Email;

try
{
userModel.Add(doc);
return Content("添加成功");
}
catch
{
return Content("添加失败");
}
}

///
/// 响应Js的“Delete”ajax请求,执行删除用户操作
///
public ContentResult Delete(string UserId)
{
try
{
userModel.Delete(UserId);
return Content("删除成功");
}
catch
{
return Content("删除失败");
}
}

///
/// 响应Js的“Update”ajax请求,执行更新用户操作
///
public ContentResult Update(string UserId, string UserName, int Age, string Tel, string Email)
{
Document doc = new Document();
doc["UserId"] = UserId;
doc["UserName"] = UserName;
doc["Age"] = Age;
doc["Tel"] = Tel;
doc["Email"] = Email;
try
{
userModel.Update(doc);
return Content("修改成功");
}
catch
{
return Content("修改失败");
}
}
}

三、实现模型层数据访问

最后,我们在Models新建一个Home文件夹,添加模型“UserModel.cs”,实现MongoDB数据库访问代码如下:

public class UserModel
{
//链接字符串(此处三个字段值根据需要可为读配置文件)
public string connectionString = "mongodb://localhost";
//数据库名
public string databaseName = "myDatabase";
//集合名
public string collectionName = "userCollection";

private Mongo mongo;
private MongoDatabase mongoDatabase;
private MongoCollection mongoCollection;

public UserModel()
{
mongo = new Mongo(connectionString);
mongoDatabase = mongo.GetDatabase(databaseName) as MongoDatabase;
mongoCollection = mongoDatabase.GetCollection(collectionName) as MongoCollection;
mongo.Connect();
}
~UserModel()
{
mongo.Disconnect();
}

///
/// 增加一条用户记录
///
///
<span> </span> public void Add(Document doc)
{
mongoCollection.Insert(doc);
}

///
/// 删除一条用户记录
///
public void Delete(string UserId)
{
mongoCollection.Remove(new Document { { "UserId", UserId } });
}

///
/// 更新一条用户记录
///
///
public void Update(Document doc)
{
mongoCollection.FindAndModify(doc, new Document { { "UserId", doc["UserId"].ToString() } });
}

///
/// 查找所有用户记录
///
///
public IEnumerable FindAll()
{
return mongoCollection.FindAll().Documents;
}

}

四、小结

代码下载:http://files.cnblogs.com/lipan/MongoDB_003.rar

自此为止一个简单MongoDB表格数据操作的功能就实现完毕了,相信读者在看完这篇文章后,差不多都可以轻松实现MongoDB项目的开发应用了。聪明的你一定会比本文做的功能更完善,更好。下篇计划讲解linq的方式访问数据集合。

[转载]T-SQL查询处理详解

mikel阅读(1130)

[转载]T-SQL查询处理详解 – 我的成长点滴 – 博客园.

最近在看《Microsoft SQL Server 2008 技术内幕:T-SQL查询》这本书,受益匪浅。准备写点东西记录一下我的学习过程。

首先简单提一下T-SQL。T-SQL的正式名称是Transact-SQL,是ANSI和ISO SQL标准的Microsoft SQL Server扩展,而PL/SQL是ORACLE对SQL标准的扩展。

对于T-SQL编程,用得最广泛的,莫过于查询(Querying)。要想写出高质量、高性能的查询语句,必须深入地了解逻辑查询处理。

一、逻辑查询处理的各个阶段

(5)SELECT DISTINCT TOP(<top_specification>) <select_list>

(1)FROM <left_table> <join_type> JOIN <right_table> ON <on_predicate>

(2)WHERE <where_predicate>

(3)GROUP BY <group_by_specification>

(4)HAVING <having_predicate>

(6)ORDER BY <order_by_list>

上边语句是一个普通格式的查询语句,基本包含了所有的查询条件和关键字。你可能会发现前边的序号并不是按顺序来的,被你说对了,这是SQL与其他编 程语言不同的最明显特征,就是它的执行顺序并不是按照编写顺序来的。上边的序号,就是查询语句在执行过程中的逻辑处理顺序。下面简单介绍一下各个阶段都干 了啥事。

(1)FROM 阶段

FROM阶段标识出查询的来源表,并处理表运算符。在涉及到联接运算的查询中(各种join),主要有以下几个步骤:

a.求笛卡尔积。不论是什么类型的联接运算,首先都是执行交叉连接(cross join),求笛卡儿积,生成虚拟表VT1-J1。

b.ON筛选器。这个阶段对上个步骤生成的VT1-J1进行筛选,根据ON子句中出现的谓词进行筛选,让谓词取值为true的行通过了考验,插入到VT1-J2。

c.添加外部行。如果指定了outer join,还需要将VT1-J2中没有找到匹配的行,作为外部行添加到VT1-J2中,生成VT1-J3。

经过以上步骤,FROM阶段就完成了。概括地讲,FROM阶段就是进行预处理的,根据提供的运算符对语句中提到的各个表进行处理(除了join,还有apply,pivot,unpivot)

(2)WHERE阶段

WHERE阶段是根据<where_predicate>中条件对VT1中的行进行筛选,让条件成立的行才会插入到VT2中。

(3)GROUP BY阶段

GROUP阶段按照指定的列名列表,将VT2中的行进行分组,生成VT3。最后每个分组只有一行。

(4)HAVING阶段

该阶段根据HAVING子句中出现的谓词对VT3的分组进行筛选,并将符合条件的组插入到VT4中。

(5)SELECT阶段

这个阶段是投影的过程,处理SELECT子句提到的元素,产生VT5。这个步骤一般按下列顺序进行

a.计算SELECT列表中的表达式,生成VT5-1。

b.若有DISTINCT,则删除VT5-1中的重复行,生成VT5-2

c.若有TOP,则根据ORDER BY子句定义的逻辑顺序,从VT5-2中选择签名指定数量或者百分比的行,生成VT5-3

(6)ORDER BY阶段

根据ORDER BY子句中指定的列明列表,对VT5-3中的行,进行排序,生成游标VC6.

当然SQL SERVER在实际的查询过程中,有查询优化器来生成实际的工作计划。以何种顺序来访问表,使用什么方法和索引,应用哪种联接方法,都是由查询优化器来决 定的。优化器一般会生成多个工作计划,从中选择开销最小的那个去执行。逻辑查询处理都有非常特定的顺序,但是优化器常常会走捷径。

详细的介绍,待续。

参考文献:《Microsoft SQL Server 2008 技术内幕:T-SQL查询》

[原创]ASP.NET刷新页面SessionID不同问题

mikel阅读(1625)

今天突然发现apsx页面的session.SessionID在每次刷新或者点击按钮的时候,每次的SessionID都不同。和之前学到 session是客户端与服务器端建立的会话,一但建立始终保存在服务器端,除非session过期或主动关闭session。那SessionID就应 该是一个了,很是困惑。

在网上仔细查了一下才发现,原来当用户第一次请求给定的应用程序中的 .aspx 文件时,ASP 生成一个 SessionID,SessionID 是由一个复杂算法生成的号码,它唯一标识每个用户会话,在新会话开始时,服务器将 Session ID 作为一个 cookie 存储在用户的 Web 浏览器中。

所以,每个页面的SessionID会不时的变化,当保存过一个Session之后(如:Session[“test”]=”123′),页面中的SessionID就不会再变化了。

上述仅用于vs2005, vs2003Session.SessionID一直不变化。

[转载]各大网站架构总结笔记

mikel阅读(1019)

[转载]各大网站架构总结笔记 – sxwgf – 博客园.

记得在大学里不止一次关注网站架构方面的东西了,但每次都是泛泛了解,也没有着重记录,一段时间后对各种架构的思想也就模糊了。这几天不知怎么的又 心血来潮(可能是快毕业了冲动了)想深入了解一下网站架构方面的知识,并想通过这次来总结一下网站架构,记录一点东西供自己以后翻阅,也给那些希望了解这 方面知识的朋友提供一点点有用的信息,下面是我这次学习的总结笔记,有什么写得不妥的地方还请大家指出,还有希望这篇随笔能抛砖引玉,大家各抒己见。

1、MySpace架构

回顾了MySpace的成长史,真是让人惊叹,他的架构基本经历了五个里程碑,每个阶段都是显得那么仓促,那么无奈,那么坎坷,又是那么的精彩,网 站为了生存只能想尽一切办法去优化系统架构,让用户满意。他给我们后人的启示是要尽早发现系统的瓶颈,设计师在设计时要有前瞻思想,否则今后有可能也要这 样仓促的升级你的产品。
这里是“五个里程碑”的具体介绍。

2、Flickr网站架构总结

Flickr.com 是网上最受欢迎的照片共享网站之一,还记得那位给Windows Vista拍摄壁纸的Hamad Darwish吗?他就是将照片上传到Flickr,后而被微软看中成为Vista壁纸御用摄影师。

–Pair of ServerIron’s做负载均衡

–Squid做html和照片的缓存

–Memcached做数据缓存

–尤其是mySQL数据库采用master-slave和shards技术实现了mySQL数据库的负载均衡,解决了数据库的瓶颈,达到了数据库横向扩展的目标。

这里是他详细的架构描述

3、YouTube架构总结

这个貌似在国内是被和谐的,要翻墙才能访问(不知到底何故)。看看他的架构:
--NetScaler用于负载均衡和静态内容缓存

--使用lighttpd作为Web服务器来提供视频服务

--CDN在多个地方备份内容,这样内容离用户更近的机会就会更高

--使用Google的BigTable,一个分布式数据存储、数据库分成shards,不同的用户指定到不同的shards、使用BigTable将图片备份到不同的数据中心,代码查看谁是最近的

这里是YouTube详细架构描述

4、PlentyOfFish架构总结

这个我觉的最神奇了,一个人每天花2个小时,可以维护一个每天3000W PV的,而且是基于.NET的(呵呵,终于给我们.net程序员一个好榜样了)。简述他的架构:
–用Microsoft Windows操作系统作为服务器

–使用ASP.NET技术

–使用IIS作为Web容器

–用Akamai CDN来缓存网页

–用Foundry ServerIron 来做负载均衡

SQLServer采用master-slave架构,两台负责read操作,master那台负责写操作

–所有的request数据都使用了gzip压缩

PlentyOfFish详细架构描述

5、WikiPedia架构总结

维基百科(Wikipedia)是一个基于Wiki技术的全球性多语言百科全书协作计划,同时也是一部在网际网路上呈现的网路百科全书,其目标及宗旨是为全人类提供自由的百科全书──用他们所选择的语言来书写而成的,是一个动态的、可自由和的全球知识体。

使用MediaWiki软件

[转载]利用SVN的POST-COMMIT钩子自动部署代码

mikel阅读(986)

[转载]利用SVN的POST-COMMIT钩子自动部署代码 – 小狼的世界 – 博客园.

最近配置了一台SVN服务器,用来保存自己一些小项目的代码。同时,SVN服务器也是一台前端的Web。所以希望利用SVN的POST-COMMIT钩子HOOKS,在提交代码的同时进行部署。

具体的步骤如下:

1、找到SVN项目的HOOKS目录。目录中默认会几个对应操作的钩子模板,我们需要创建一个post-commit的文件。

2、post-commit的主要内容

#/bin/bash

REPOS=”$1″
REV=”$2″
WEB_PATH=”/home/fltrpsrv2/www”
TEST_PATH=”/home/apache/testx/www.h.cn”

export LANG=zh_CN.UTF-8

CURDATE=`date`
echo “Code Deployed By at $CURDATE” >> /home/fltrpsrv2/svn/www.h.cn/hooks/code_deploy_log
/usr/local/bin/svn update –username xxx –password xxx /home/apache/htdocs/ >> /home/fltrpsrv2/svn/www.h.cn/hooks/
code_deploy_log

3、接下来需要我们首先Checkout一份代码到WEB的服务目录中。

4、 代码CO出来之后,可以进行post-commit脚本的测试了。因为svn的hooks执行的时候不带有任何的环境变量,所以我们不能通过简单的 ./post-commit 进行代码的测试。必须要使用sudo su 等命令切换到svn或者apache服务器运行用户下,用下面的方法进行测试

env – ./post-commit

这样,执行svn update 命令的用户就是svn或者apache服务器的运行用户,因此,我们需要保证代码的存放目录必须有对应用户的权限,否则,这个脚本是不能执行的。我就是因为这个权限的问题,耽误了很多时间。

关于权限问题,在SVN的官方站点中也有说明,大家可以参考后面的资料。

在Unix系统中,如果没有对应的用户,也可以使用c语言编写脚本的形式,并且赋予+S的权限来达到相应的目的,具体的查看附件中的资料吧。

Technorati 标签: ,,,

参考资料:
1、SVN Authentication and Auto Update
2、SVN Forum
3、Website auto update
4、Hook Debuging