西电微机原理期末试题试题,会的大神帮忙做下,有重谢2

741486 条评论分享收藏感谢收起赞同 593429 条评论分享收藏感谢收起新学期开学了,有没有小伙伴帮忙查一下两门课的课程表,微机原理和数电模电的,有酬金,重谢【南京理工大学吧】_百度贴吧
&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&签到排名:今日本吧第个签到,本吧因你更精彩,明天继续来努力!
本吧签到人数:0成为超级会员,使用一键签到本月漏签0次!成为超级会员,赠送8张补签卡连续签到:天&&累计签到:天超级会员单次开通12个月以上,赠送连续签到卡3张
关注:227,898贴子:
新学期开学了,有没有小伙伴帮忙查一下两门课的课程表,微机原理收藏
新学期开学了,有没有小伙伴帮忙查一下两门课的课程表,微机原理和数电模电的,有酬金,重谢
南京职业技术学校有哪些,北大青鸟中博软件学院助你掌握一技之长,毕业直达名企,做高薪白领.北大青鸟中博,名师课堂,重实践,多年领衔IT教育口碑!
下个南理工助手
或者 超级课程表
数模电实验应该在小学期,地点基础实验楼
谢谢大家伙,求支援
希望大家帮忙
没有人知道么,帮忙查查
求帮忙,求助攻
登录百度帐号&h1&背景&/h1&&p&原大众点评的订单单表早就已经突破两百G,由于查询维度较多,即使加了两个从库,优化索引,仍然存在很多查询不理想的情况。去年大量抢购活动的开展,使数据库达到瓶颈,应用只能通过限速、异步队列等对其进行保护;业务需求层出不穷,原有的订单模型很难满足业务需求,但是基于原订单表的DDL又非常吃力,无法达到业务要求。随着这些问题越来越突出,订单数据库的切分就愈发急迫了。&/p&&p&这次切分,我们的目标是未来十年内不需要担心订单容量的问题。&/p&&h1&垂直切分&/h1&&p&先对订单库进行垂直切分,将原有的订单库分为基础订单库、订单流程库等,本文就不展开讲了。&br&&br&&/p&&figure&&img src=&https://pic3.zhimg.com/v2-d520e2b7ebff599ff4326cad30c35016_b.png& data-rawwidth=&692& data-rawheight=&375& class=&origin_image zh-lightbox-thumb& width=&692& data-original=&https://pic3.zhimg.com/v2-d520e2b7ebff599ff4326cad30c35016_r.jpg&&&/figure&&h1&水平切分&/h1&&p&垂直切分缓解了原来单集群的压力,但是在抢购时依然捉襟见肘。原有的订单模型已经无法满足业务需求,于是我们设计了一套新的统一订单模型,为同时满足C端用户、B端商户、客服、运营等的需求,我们分别通过用户ID和商户ID进行切分,并通过PUMA(我们内部开发的MySQL binlog实时解析服务)同步到一个运营库。&br&&figure&&img src=&https://pic2.zhimg.com/v2-8aeeced24d25_b.png& data-rawwidth=&692& data-rawheight=&663& class=&origin_image zh-lightbox-thumb& width=&692& data-original=&https://pic2.zhimg.com/v2-8aeeced24d25_r.jpg&&&/figure&&/p&&h2&切分策略&/h2&&h3&1. 查询切分&/h3&&p&将ID和库的Mapping关系记录在一个单独的库中。&br&&/p&&p&优点:ID和库的Mapping算法可以随意更改。&br&缺点:引入额外的单点。&/p&&figure&&img src=&https://pic4.zhimg.com/v2-7f0f4ca2c3fb7bcd73c4cb_b.png& data-rawwidth=&692& data-rawheight=&354& class=&origin_image zh-lightbox-thumb& width=&692& data-original=&https://pic4.zhimg.com/v2-7f0f4ca2c3fb7bcd73c4cb_r.jpg&&&/figure&&h3&2. 范围切分&/h3&&p&比如按照时间区间或ID区间来切分。&br&&/p&&p&优点:单表大小可控,天然水平扩展。&br&缺点:无法解决集中写入瓶颈的问题。&/p&&figure&&img src=&https://pic4.zhimg.com/v2-953be42a363f9c806ca0b_b.png& data-rawwidth=&692& data-rawheight=&252& class=&origin_image zh-lightbox-thumb& width=&692& data-original=&https://pic4.zhimg.com/v2-953be42a363f9c806ca0b_r.jpg&&&/figure&&h3&3. Hash切分&/h3&&p&一般采用Mod来切分,下面着重讲一下Mod的策略。&figure&&img src=&https://pic1.zhimg.com/v2-28538acd5c8bf3abd9990_b.png& data-rawwidth=&692& data-rawheight=&438& class=&origin_image zh-lightbox-thumb& width=&692& data-original=&https://pic1.zhimg.com/v2-28538acd5c8bf3abd9990_r.jpg&&&/figure&&/p&&p&数据水平切分后我们希望是一劳永逸或者是易于水平扩展的,所以推荐采用mod 2^n这种一致性Hash。&/p&&p&以统一订单库为例,我们分库分表的方案是32*32的,即通过UserId后四位mod 32分到32个库中,同时再将UserId后四位Div 32 Mod 32将每个库分为32个表,共计分为1024张表。线上部署情况为8个集群(主从),每个集群4个库。&/p&&p&为什么说这种方式是易于水平扩展的呢?我们分析如下两个场景。&/p&&br&&h4&场景一:数据库性能达到瓶颈&/h4&&h4&方法一&/h4&&p&按照现有规则不变,可以直接扩展到32个数据库集群。&br&&figure&&img src=&https://pic4.zhimg.com/v2-df8f973a6cd231f24df33bf_b.png& data-rawwidth=&692& data-rawheight=&654& class=&origin_image zh-lightbox-thumb& width=&692& data-original=&https://pic4.zhimg.com/v2-df8f973a6cd231f24df33bf_r.jpg&&&/figure&&/p&&h4&方法二&/h4&&p&如果32个集群也无法满足需求,那么将分库分表规则调整为(32*2^n)*(32/2^n),可以达到最多1024个集群。&br&&figure&&img src=&https://pic3.zhimg.com/v2-5d58bb0d07bb1abf117226_b.png& data-rawwidth=&692& data-rawheight=&431& class=&origin_image zh-lightbox-thumb& width=&692& data-original=&https://pic3.zhimg.com/v2-5d58bb0d07bb1abf117226_r.jpg&&&/figure&&/p&&h4&场景二:单表容量达到瓶颈(或者1024已经无法满足你)&/h4&&h4&方法:&/h4&&p&&figure&&img src=&https://pic1.zhimg.com/v2-1ceb2c5278ef0fec5a3fd_b.png& data-rawwidth=&692& data-rawheight=&412& class=&origin_image zh-lightbox-thumb& width=&692& data-original=&https://pic1.zhimg.com/v2-1ceb2c5278ef0fec5a3fd_r.jpg&&&/figure&假如单表都已突破200G,200*T(按照现有的订单模型算了算,大概一万千亿订单,相信这一天,嗯,指日可待!),没关系,32*(32*2^n),这时分库规则不变,单库里的表再进行裂变,当然,在目前订单这种规则下(用userId后四位 mod)还是有极限的,因为只有四位,所以最多拆8192个表,至于为什么只取后四位,后面会有篇幅讲到。&/p&&p&另外一个维度是通过ShopID进行切分,规则8*8和UserID比较类似,就不再赘述,需要注意的是Shop库我们仅存储了订单主表,用来满足Shop维度的查询。&/p&&h2&唯一ID方案&/h2&&p&这个方案也很多,主流的有那么几种:&/p&&h3&1. 利用数据库自增ID&/h3&&p&优点:最简单。&br&缺点:单点风险、单机性能瓶颈。&/p&&h3&2. 利用数据库集群并设置相应的步长(Flickr方案)&/h3&&p&优点:高可用、ID较简洁。&br&缺点:需要单独的数据库集群。&/p&&h3&3. Twitter Snowflake&/h3&&p&优点:高性能高可用、易拓展。&br&缺点:需要独立的集群以及ZK。&/p&&h3&4. 一大波GUID、Random算法&/h3&&p&优点:简单。&br&缺点:生成ID较长,有重复几率。&/p&&h3&我们的方案&/h3&&p&为了减少运营成本并减少额外的风险我们排除了所有需要独立集群的方案,采用了带有业务属性的方案:&/p&&blockquote&&p&时间戳+用户标识码+随机数&/p&&/blockquote&&p&有下面几个好处:&/p&&ul&&li&方便、成本低。&/li&&li&基本无重复的可能。&/li&&li&自带分库规则,这里的用户标识码即为用户ID的后四位,在查询的场景下,只需要订单号就可以匹配到相应的库表而无需用户ID,只取四位是希望订单号尽可能的短一些,并且评估下来四位已经足够。&/li&&li&可排序,因为时间戳在最前面。&/li&&/ul&&p&当然也有一些缺点,比如长度稍长,性能要比int/bigint的稍差等。&/p&&h2&其他问题&/h2&&h1&&ul&&li&事务支持:我们是将整个订单领域聚合体切分,维度一致,所以对聚合体的事务是支持的。&/li&&li&复杂查询:垂直切分后,就跟join说拜拜了;水平切分后,查询的条件一定要在切分的维度内,比如查询具体某个用户下的各位订单等;禁止不带切分的维度的查询,即使中间件可以支持这种查询,可以在内存中组装,但是这种需求往往不应该在在线库查询,或者可以通过其他方法转换到切分的维度来实现。&/li&&/ul&&/h1&&h2&数据迁移&/h2&&p&数据库拆分一般是业务发展到一定规模后的优化和重构,为了支持业务快速上线,很难一开始就分库分表,垂直拆分还好办,改改数据源就搞定了,一旦开始水平拆分,数据清洗就是个大问题,为此,我们经历了以下几个阶段。&/p&&h3&第一阶段&figure&&img src=&https://pic4.zhimg.com/v2-b709dedba2dd6593dab4b3_b.png& data-rawwidth=&692& data-rawheight=&659& class=&origin_image zh-lightbox-thumb& width=&692& data-original=&https://pic4.zhimg.com/v2-b709dedba2dd6593dab4b3_r.jpg&&&/figure&&/h3&&ul&&li&数据库双写(事务成功以老模型为准),查询走老模型。&/li&&li&每日job数据对账(通过DW),并将差异补平。&/li&&li&通过job导历史数据。&/li&&/ul&&h3&第二阶段&/h3&&figure&&img src=&https://pic3.zhimg.com/v2-8ab2f23e5aaedaff15b0e_b.png& data-rawwidth=&692& data-rawheight=&536& class=&origin_image zh-lightbox-thumb& width=&692& data-original=&https://pic3.zhimg.com/v2-8ab2f23e5aaedaff15b0e_r.jpg&&&/figure&&ul&&li&历史数据导入完毕并且数据对账无误。&/li&&li&依然是数据库双写,但是事务成功与否以新模型为准,在线查询切新模型。&/li&&li&每日job数据对账,将差异补平。&/li&&/ul&&h3&第三阶段&/h3&&figure&&img src=&https://pic1.zhimg.com/v2-25224fbbcfda2e258a799ec053cd8b54_b.png& data-rawwidth=&692& data-rawheight=&659& class=&origin_image zh-lightbox-thumb& width=&692& data-original=&https://pic1.zhimg.com/v2-25224fbbcfda2e258a799ec053cd8b54_r.jpg&&&/figure&&ul&&li&老模型不再同步写入,仅当订单有终态时才会异步补上。&/li&&li&此阶段只有离线数据依然依赖老的模型,并且下游的依赖非常多,待DW改造完就可以完全废除老模型了。&/li&&/ul&&h2&总结&/h2&&p&并非所有表都需要水平拆分,要看增长的类型和速度,水平拆分是大招,拆分后会增加开发的复杂度,不到万不得已不使用。&/p&&p&在大规模并发的业务上,尽量做到在线查询和离线查询隔离,交易查询和运营/客服查询隔离。&/p&&p&拆分维度的选择很重要,要尽可能在解决拆分前问题的基础上,便于开发。&/p&&p&数据库没你想象的那么坚强,需要保护,尽量使用简单的、良好索引的查询,这样数据库整体可控,也易于长期容量规划以及水平扩展。&/p&&p&&strong&最后感谢一下棒棒的DBA团队和数据库中间件团队对项目的大力协助!&/strong&&/p&&br&&br&&p&&strong&不想错过技术博客更新?想给文章评论、和作者互动?第一时间获取技术沙龙信息?&/strong&&/p&&p&&strong&请关注我们的官方微信公众号“美团点评技术团队”。&/strong&&/p&
背景原大众点评的订单单表早就已经突破两百G,由于查询维度较多,即使加了两个从库,优化索引,仍然存在很多查询不理想的情况。去年大量抢购活动的开展,使数据库达到瓶颈,应用只能通过限速、异步队列等对其进行保护;业务需求层出不穷,原有的订单模型很…
11.30更新:见后面地形生成和总结部分。过百赞了知乎小透明好开森,谢谢大家!&br&12.3更新:谢大家厚爱。回答一位 &a data-hash=&de96a5c62a75321ecf4815& href=&//www.zhihu.com/people/de96a5c62a75321ecf4815& class=&member_mention& data-editable=&true& data-title=&@兔子先生& data-hovercard=&p$b$de96a5c62a75321ecf4815&&@兔子先生&/a& 的问题,见文章:&a href=&https://zhuanlan.zhihu.com/p/& class=&internal&&答兔子先生&/a&,欢迎讨论。&br&&br&研究过MC地形生成和基础架构的强答一发。&br&长文预警。&br&&br&先说架构。&br&&br&MC分客户端和服务端两面,客户端主要负责渲染,服务端是本地服务器或者远程服务器(本地游戏两个都要跑,真正的MC服只跑服务端)。其实这俩就共享一个IWorld接口,然后这个接口里面乱的一塌糊涂。&br&&br&地图的基础单元是chunk,就是16*256*16的大方块,里面包含小方块。小方块可以是普通IBlock,可以带特殊数据(metadata),也可以有TileEntity(按照方块处理的实体)。区块里面还存了Entity就是实体(人啊,僵尸啊,猪啊还有掉落的物品之类的),实体的位置和方向都是浮点数float或double(双精度),因此拥有更大的自由度。&br&前后端(客户端和服务端)存储数据都靠的这个chunk,传输和生成也是以chunk为单位,所以会看到服务器卡的时候是一块一块冒出来的。chunk放到HashMap里面备用。&br&保存(1.2之后的原版服务端anvil文件格式)把32*32个chunk放到一个.mca(老版是.mcp)文件里面,用一种碎片化管理的方式来节省空间同时减少IO句柄数量,具体的格式MC Wiki上有详细的解释。&br&&br&所有MC的存储数据(注意不包括方块和音乐本身这种理论上是做死了的数据)都用一种二进制数据格式存储(NBT Named Binary Tags 命名二进制标签 ——Minecraft Wiki),里面包含数组数据、浮点数、整数或者字符串,其实结构相当简单,项目有树形结构,每个子项还有字符串形式的名字。它其实可以和JSON简单互译,做命令方块地图时会用到的JSON格式的实体数据就是这个东西。&br&&br&服务端动态加载/保存区块,然后用一个单线程来每个tick更新整个地图(所以很慢啊喂!),具体方式既粗暴又简单:遍历每个方块,看到有计划更新(比如水、岩浆、刷怪笼、植物这种)的就让他更新一下,看到TileEntity或者Entity每个tick都更新,同时处理玩家的数据:放方块/砸方块/开箱子/熔铁锭等等。对了,服务端还要负责更新光照(看后面)。&br&要是玩家走到了没有生成过的荒野,就会启动区块生成器来生成新的区块。生成这个特别复杂,也是我主要研究的问题,一会儿再讲。&br&&br&客户端维护一个非常 *智障* 的GUI系统,里面的渲染基本上都是legacy opengl直接瞎写的,GUI更新基本上都是全屏更新的,鼠标行动基本上全是轮询的,根本不管什么事件啦dirty区域啦缓存啦,就两个字:粗暴。当然MC总共也没什么GUI好谈,所以也就算了。&br&这里提一句其实服务器里面花花绿绿的字符的实现原理也非常暴力,就是用小结号§来代表下一个字符是格式转义字符,从k到r(好像?)都代表不同的意义,颜色或者加粗或者倾斜或者随机颜色,封面MINECRAFT大字旁边那行字就是用这个渲染的。什么?你问§怎么办?没办法。(摊手)&br&&br&除了GUI就是游戏主要的渲染部分。MC应用15年前的opengl技术成功地把每一个chunk编译成16个list(opengl中的列表,可以理解为把要画的步骤先告诉显卡以后再用只要告诉他列表的号码就行了,用来提升效率),每个16*16*16,不包括TileEntity和Entity——它们是每帧单独渲染的,所以MC生物一多就卡,而用bug透视的时候看不到方块但是能看得到生物和箱子。&br&客户端每帧要是看到新的改变过的chunk(被什么东西更新过了)就把它和它四周的chunk都重新生成一遍list(这里你会看到要是服务器加载很慢的话最远处的chunk是看得到截面的),这个过程叫做tessellation,就是把每个小的面片(长方形)放到一起组成一个大的顶点数组(就是一个大模型),一股脑儿传给显卡(因为从cpu传连续数据比零零散散地传数据要快很多)。&br&最后渲染的时候也是通过数学计算把在视野(按照玩家视角)中的list找出来一一调用渲染。这里有个小bug,就是筛选是否在视野中是假定这个list里面的所有东西都包含在这16*16*16的大方块里面的,所以如果你搞一个信标的话,它的光柱会一直延伸到天空上,但是一旦整个list被判断不可见,这个光柱也会一起消失掉。&br&&br&至于光照,光照也是在服务器端算好的(是不是难以想象?),我们看到的有渐变的平滑光照也是在方块光照上加工计算出来的。&br&光照有两部分,一部分叫做天光(sky light),也就是不算任何方块光源这个位置的光照强度。细节上,他是这么计算的:如果他直接被阳光照射(上面没有别的方块),那么亮度是16(或者说15,取决于你是从0开始数数还是从1开始),如果上面一个是半透明方块,那么亮度是上面的亮度减去版透明方块的阻碍值(比如树叶是2,玻璃是1),如果上面是不透明方块(比如石头),那么亮度是0。你可能要问,山洞里面也不是全黑啊?别急,慢慢讲。&br&区块生成的时候只会这样一遍地生成最简单的光照,而把光照的扩散放到一个队列里面等待服务端更新时一点点处理,每个tick能上限好像是1个事件。(理论上这是为了加快速度不让光照计算拖慢服务器来着,然而。。)处理事件是一个递归的过程,从这个方块出发,如果他6个方向(上下左右前后)的方块的亮度大于等于它,那就不递归(这种时候定义空气的光阻为1),否则把那个方向上的方块+(自身亮度-1)放进队列。当然要是减到0就不动了。&br&按照mojang员工的天真想法,既然光照最大是15(因为我从0开始数数),每一格至少减弱1,那么跟我源头上这一个坐标差距大于十五的我就不用管了咯?所以他们限制每次递归最多递归到这么远,之后不管亮度多少都留到下一个tick去吧。但是这里会有一个bug:如果山洞顶上被开了一个洞,从天上漏光,结果从山洞边缘开始计算天光,算到洞附近发现开始变亮了,但是又碍于距离限制不能再算下去,所以有时候就会看到非常突兀的亮度变化。&br&光照的另一部分叫做方块光(block light),也就是萤石啊南瓜灯啊火柴啊这种方块的灯光。这种也是碰到空气就-1,碰到不透明的石头就变成0,碰到半透明的就减去他的光阻量(代码里面叫做opacity,意为不透明度)。然后每个方块上的亮度以最大值为准,同样塞到前面的那个队列里面。服务端更新的时候计算。&br&&br&光照当然是用来渲染的,而这也是MC渲染极其重要的一部分。&br&每个方块的光照强度等于方块光和天光的最大值,那么晚上怎么办呢?其实天光在晚上的亮度就等于 原先亮度 - 夜晚的亮度降,也就是原来12级光,白天是15级阳光,算出来就是12级,晚上变成11级月光,就是 12 - (15 - 11) = 8级光。当然这个值不能小于0。&br&最早的光照对应就是一个线性的亮度,15级就是100%的亮度,7级就是50%,0级就是0%,后来发现这样效果不好,就改成了对数型:第(n-1)级的亮度是第n级的90%,这样亮度变化比较自然,而且哪怕光照为0也不是全黑(地狱/下界的这个系数是90%,0级光实际上还有46%的亮度,所以显得下界特别亮)。&br&后来觉得还不好,所以就干脆弄了一个二维的亮度图,横坐标是天光,纵坐标是方块光,这个在minecraft.jar里面找得到一个纹理文件(哎呀名字我忘了),就是这个。因此可以看到方块光偏暖色,天光偏冷色,也是这么来的。&br&&br&渲染更重要的当然就是大家关心的纹理,所谓MC像素之称的来源之一。&br&如果打开beta以后的minecraft,jar文件就会看到,纹理图片都是分开的图片,但是一个list(还记得list吗)只能用一个纹理单元,因此MC就用了一种取巧的做法:程序启动的/更换材质包时候把所有要用的纹理加载一遍然后拼到一张图上面,有时候会存到.minecraft/下的一个临时文件夹里面,再加载进显卡,就可以一起用了。当然Entity和TileEntity的纹理是不用这么干的,因为他们每个都是独立渲染的,不需要这么弄。&br&这也是为什么MC的API要求方块必须在使用之前就在Registry里面注册好,这样才能知道你到底要哪些纹理啊。(貌似这个问题在1.8采用了新的model模式后有了改善)这也是为什么在切换语言之后MC都要卡好久,因为切换语言也算作切换材质包,要把所有的小纹理图片再拼接一遍,还要把所有的chunk已经生成好的list重新生成一遍。(卡!)&br&&br&然后就要根据玩家的位置,角度,以及人称(第一人称/背后第三人称/面对第二人称)来计算投影矩阵(也就是摆摄像机)。这个过程是在客户端处理的,所以就算服务端卡爆了你的脑袋还是可以自由旋转的,至于为什么你可以看到多人中别的玩家的脑袋转,我猜是时不时地传一些数据回去做插值弄的。&br&最后直接丢给opengl画出来,三维渲染部分就基本完成了。其实这里MC还允许像光影mod那样的后期处理(post-processing),1.7左右的版本有一个“Super Secret Setting”的按钮在游戏暂停的设置里面,点一下可以在好多后期特效之间切换。&br&还要画天空、太阳、月亮、天气和云层,当然太阳月亮就是贴个图(月有阴晴圆缺哦,纹理文件有一个专门管月相的),下雨下雪就是一个动画,云层也就是一堆半透明的方块。&br&最后一个部分:第一人称的手和手上拿的东西,这个也是三维渲染的,不过难度不大,相信大家想想就明白了,也就不多说了。&br&&br&然后是介于三维和二维之间的粒子系统。粒子系统其实原来是用来模拟像水滴、碎片、树叶这样数量巨大的东西,以至于渲染成真正的三维模型代价过高,不得不用一个永远对着玩家的面来代替,后来就管所有这种永远面向玩家的面叫做“粒子”(particle),或者“标志板”(bulletin board)。&br&MC的粒子系统主要都是一些效果:入水/钓鱼是的水滴,吃饭时的食物残渣,多人模式中别人头顶上的名字,魔药产生的效果,经验球,爆炸的“冲击波”,都是粒子。(是不是有点大)&br&反正粒子一般放在同样的三维环境中渲染,用同样的投影矩阵,但是往往会被归为一个特殊的粒子引擎,因为它们作为没有体积的物体却经常要表现出一种重力感(就比如说水滴会跳起来再落下去),不能放到一般的三维场景中渲染。&br&&br&三维画完了是二维:下面的道具栏和聊天内容这种,按E可以赛艇(划掉)打开的完整物品栏(inventories)、各种可交互菜单(箱子、铁毡、熔炉、村民……)。这些凌驾于所有三维内容之上(因为不会被挡掉),最后被渲染,同时也充当与玩家交互的功能。(所以说MC的GUI很乱啦。。这里都不分清楚的)&br&&br&说到交互,肯定大家会想,电脑到底是怎么知道我选中了哪个面,往哪里放了东西了呢?其实判断选中的面还简单,做点小计算都不难,真正的难点在于客户端和服务器之间的传输协议。&br&传输协议,顾名思义,就是两者之间关于信息传输所商定的协议。具体的技术细节在&a href=&//link.zhihu.com/?target=http%3A//wiki.vg/Main_Page& class=& wrap external& target=&_blank& rel=&nofollow noreferrer&&wiki.vg&/a&这里有详细说,我就不展开了。总结一下,就是:MC里面除了区块(chunk)的传输是全局的以外,其他各种东西的传输协议都是个管个的:箱子有箱子的传输协议,玩家有玩家的协议,熔炉有熔炉的协议。你作为玩家不管干了什么,改变了什么东西,都要通过相应的协议把实际内容传输给服务器端监听这个数据的东西,然后再由它对地图中的内容进行操作。&br&这里也能看出MC的一大毛病:该耦合的不耦合,该分离的不分离,协议一大堆各自为政,各种方块(block)又互相调用,乱成一团——一个基础的Block类足足有几千行。&br&&br&---------------------------------------------分割线-----------------------------------------------------&br&&br&说了这么多,我们来理一下思路吧。&br&&br&MC启动你的游戏有两种:本地 or 远程。&br&本地游戏需要 玩家操作GUI生成或加载世界 -& 启动本地服务端(IntegratedServer)-& 加载地图 -& 以单人单机模式提供游戏。&br&或者远程服务器由服务器管理员启动(官网上有下载原版的minecraft_server.jar,但是基本上现在用的都是水桶bukkit服),然后 启动服务端(原版是MinecraftServer) -& 加载地图 -& 以多人联机模式提供游戏。&br&&br&服务端启动后的基本流程是: 等待玩家连接并通过验证 -& 发送初始数据 -& 按需发送/加载/生成地 图 -& 更新(tick)地图内容,加上散播生物啊之类的操作 -& 发送地图 -& ……&br&而客户端则是: 连接服务端 -& 获取初始数据(时间、玩家位置、验证信息等) -& 获取地图(区块)数据 -& 渲染地图 -& 接受玩家输入(鼠标/键盘操作) -& 发送数据包给服务端 -& 获取更新过的地图 -& ……&br&当然理论上这里很多步骤都应该是多线程并行进行的,但是MC的这个代码写的啊……很多都没有优化,这也是为什么MC老是卡的重要原因之一。&br&&br&这大致就是整个MC里面在发生的事情的总结,接下去的内容比较技术向,请不愿看者直接跳到再下一个分割线。&br&&br&-------------------------------------------分割线---------------------------------------------------&br&&br&说说MC的地形生成吧。这个我研究的比较多,也想借此机会说一说。&br&&br&多图杀猫预警!!!!!!!!!!!&br&&br&更新:来填坑啦!&br&是时候祭出我尘封已久的mod开发工具了!&br&(本段内容基于1.10.2的fml反编译,使用的是IntelliJ idea进行开发,教程。。有空再说吧)&br&&figure&&img src=&https://pic1.zhimg.com/50/v2-ab89da9ade74_b.jpg& data-rawwidth=&1365& data-rawheight=&726& class=&origin_image zh-lightbox-thumb& width=&1365& data-original=&https://pic1.zhimg.com/50/v2-ab89da9ade74_r.jpg&&&/figure&好吧这是今年8月从fml官网上直接生成的,前面那个换电脑的时候丢了。。悲伤。。&br&不过这都不能阻止我的脚步!ctrl+alt+F10运行!&br&&figure&&img src=&https://pic4.zhimg.com/50/v2-f6cc05ed0a56de75ad68d9f7a2eaa252_b.jpg& data-rawwidth=&857& data-rawheight=&510& class=&origin_image zh-lightbox-thumb& width=&857& data-original=&https://pic4.zhimg.com/50/v2-f6cc05ed0a56de75ad68d9f7a2eaa252_r.jpg&&&/figure&&figure&&img src=&https://pic2.zhimg.com/50/v2-84c184ef3adee6e4f1b9f510_b.jpg& data-rawwidth=&857& data-rawheight=&512& class=&origin_image zh-lightbox-thumb& width=&857& data-original=&https://pic2.zhimg.com/50/v2-84c184ef3adee6e4f1b9f510_r.jpg&&&/figure&OK!&br&&br&好吧说正事。&br&微软接手之后MC开发组搞了一个功能:生成世界时进入更多世界选项(英文版More world options)&br&&figure&&img src=&https://pic3.zhimg.com/50/v2-b88eca7a11584baf5799edc_b.jpg& data-rawwidth=&546& data-rawheight=&435& class=&origin_image zh-lightbox-thumb& width=&546& data-original=&https://pic3.zhimg.com/50/v2-b88eca7a11584baf5799edc_r.jpg&&&/figure&世界类型点到“自定义”(Customized),(这里1.10.2直接点就可以了,我记得老一点的版本要按住alt还是ctrl再点才回出来),然后按“自定义”(Customize)按钮就能调整世界生成的一些参数:&br&&figure&&img src=&https://pic4.zhimg.com/50/v2-4c26addee4d55bbe7386b6b_b.jpg& data-rawwidth=&710& data-rawheight=&474& class=&origin_image zh-lightbox-thumb& width=&710& data-original=&https://pic4.zhimg.com/50/v2-4c26addee4d55bbe7386b6b_r.jpg&&&/figure&(调成中文吧。。)&br&可以自定义的选项有四页,可以通过调整它们来直观地看到MC世界生成的各种参数:&br&&figure&&img src=&https://pic1.zhimg.com/50/v2-47a3b9ab9e2f55510ecdd227ffbfaddc_b.jpg& data-rawwidth=&870& data-rawheight=&668& class=&origin_image zh-lightbox-thumb& width=&870& data-original=&https://pic1.zhimg.com/50/v2-47a3b9ab9e2f55510ecdd227ffbfaddc_r.jpg&&&/figure&&br&&figure&&img src=&https://pic4.zhimg.com/50/v2-fbefafe1bfadb3_b.jpg& data-rawwidth=&870& data-rawheight=&668& class=&origin_image zh-lightbox-thumb& width=&870& data-original=&https://pic4.zhimg.com/50/v2-fbefafe1bfadb3_r.jpg&&&/figure&&figure&&img src=&https://pic3.zhimg.com/50/v2-ce28f7ce539ffd_b.jpg& data-rawwidth=&870& data-rawheight=&668& class=&origin_image zh-lightbox-thumb& width=&870& data-original=&https://pic3.zhimg.com/50/v2-ce28f7ce539ffd_r.jpg&&&/figure&&figure&&img src=&https://pic1.zhimg.com/50/v2-626d4fc8eade5f8ff084bca_b.jpg& data-rawwidth=&870& data-rawheight=&668& class=&origin_image zh-lightbox-thumb& width=&870& data-original=&https://pic1.zhimg.com/50/v2-626d4fc8eade5f8ff084bca_r.jpg&&&/figure&第三、第四页比较高级,之后说到具体的生成原理的时候再说,先来玩玩前两页的参数:&br&&figure&&img src=&https://pic1.zhimg.com/50/v2-4fce5de6930_b.jpg& data-rawwidth=&656& data-rawheight=&465& class=&origin_image zh-lightbox-thumb& width=&656& data-original=&https://pic1.zhimg.com/50/v2-4fce5de6930_r.jpg&&&/figure&&br&抬高海平面,变成岩浆海,乱七八糟的东西都不要,河流越少越好,生物群系Extreme Hills+(全是山),生成!&br&&figure&&img src=&https://pic4.zhimg.com/50/v2-1fa23b4cb7f8be_b.jpg& data-rawwidth=&1365& data-rawheight=&726& class=&origin_image zh-lightbox-thumb& width=&1365& data-original=&https://pic4.zhimg.com/50/v2-1fa23b4cb7f8be_r.jpg&&&/figure&厉害了我的哥!这里菱形的亮区也证明了我前面说的:光照更新的时候只能更新15格以外的,更远的哪怕有变化也不会继续更新下去。&br&&figure&&img src=&https://pic1.zhimg.com/50/v2-591bc5bde8f_b.jpg& data-rawwidth=&1365& data-rawheight=&726& class=&origin_image zh-lightbox-thumb& width=&1365& data-original=&https://pic1.zhimg.com/50/v2-591bc5bde8f_r.jpg&&&/figure&过一会儿就好了。可以看到MC加载地图是加载了一个方形的区域。&br&&figure&&img src=&https://pic1.zhimg.com/50/v2-25c2dc7faf6f0c4cdfa8c14_b.jpg& data-rawwidth=&703& data-rawheight=&391& class=&origin_image zh-lightbox-thumb& width=&703& data-original=&https://pic1.zhimg.com/50/v2-25c2dc7faf6f0c4cdfa8c14_r.jpg&&&/figure&远处的树是悬空的,因为另一边的区块还没生成出来。&br&&figure&&img src=&https://pic1.zhimg.com/50/v2-df88ccedbb5ff_b.jpg& data-rawwidth=&861& data-rawheight=&594& class=&origin_image zh-lightbox-thumb& width=&861& data-original=&https://pic1.zhimg.com/50/v2-df88ccedbb5ff_r.jpg&&&/figure&因为离岩浆太近,草地上的雪都化了。&br&&figure&&img src=&https://pic4.zhimg.com/50/v2-60ba215a925d70c1c04d0f37878cdf34_b.jpg& data-rawwidth=&318& data-rawheight=&64& class=&content_image& width=&318&&&/figure&按E可以赛艇。&br&&br&再来一个:&br&&figure&&img src=&https://pic3.zhimg.com/50/v2-28c94bfe01_b.jpg& data-rawwidth=&670& data-rawheight=&572& class=&origin_image zh-lightbox-thumb& width=&670& data-original=&https://pic3.zhimg.com/50/v2-28c94bfe01_r.jpg&&&/figure&泥土大理石统统不要,我就是要石头直接暴露在阳光之下。&br&&figure&&img src=&https://pic3.zhimg.com/50/v2-dcefc_b.jpg& data-rawwidth=&746& data-rawheight=&454& class=&origin_image zh-lightbox-thumb& width=&746& data-original=&https://pic3.zhimg.com/50/v2-dcefc_r.jpg&&&/figure&&br&感觉发家致富都靠这个了。&br&&figure&&img src=&https://pic2.zhimg.com/50/v2-bc21c5a9652_b.jpg& data-rawwidth=&677& data-rawheight=&468& class=&origin_image zh-lightbox-thumb& width=&677& data-original=&https://pic2.zhimg.com/50/v2-bc21c5a9652_r.jpg&&&/figure&&br&别的统统不要,Extreme Hills生物群系,生成!&br&&figure&&img src=&https://pic1.zhimg.com/50/v2-1db9c5d8fcb4d783cdcf69_b.jpg& data-rawwidth=&857& data-rawheight=&601& class=&origin_image zh-lightbox-thumb& width=&857& data-original=&https://pic1.zhimg.com/50/v2-1db9c5d8fcb4d783cdcf69_r.jpg&&&/figure&金矿与钻石的海洋!&br&&figure&&img src=&https://pic3.zhimg.com/50/v2-cd17a6dd8d5a5c2789fd_b.jpg& data-rawwidth=&875& data-rawheight=&607& class=&origin_image zh-lightbox-thumb& width=&875& data-original=&https://pic3.zhimg.com/50/v2-cd17a6dd8d5a5c2789fd_r.jpg&&&/figure&&br&我要完成一个小目标了!(虽然是创造)&br&&br&好吧就玩到这里,我们来看代码吧。&br&&b&警告:&/b&接下来有大量程序猿内容,患有&b&代码恐惧症&/b&者可跳过。&br&&br&地形生成的代码主要在这几个包里面:&br&&figure&&img src=&https://pic2.zhimg.com/50/v2-266c3c47c39aff55d0fdbff_b.jpg& data-rawwidth=&387& data-rawheight=&400& class=&content_image& width=&387&&&/figure&&br&net.minecraft.world.gen底下的是 不同世界类型(主世界/下界/末地/超平坦)、重要的地形元素(洞穴/峡谷)、噪声生成器(用来生成随机的东西):&br&&figure&&img src=&https://pic3.zhimg.com/50/v2-1fb69b0bf2cb9d46709f_b.jpg& data-rawwidth=&352& data-rawheight=&423& class=&content_image& width=&352&&&/figure&net.minecraft.world.gen.features是各种特征,有树(包括树的变种针叶林之类的)、植被(仙人掌、花草之类的)、地质(黏土clay、沙子sand)、地貌(湖泊、沼泽、下界的岩浆)、人文景观(地牢、沙井,但是村庄女巫房海底遗迹这些不在这里):&br&&figure&&img src=&https://pic4.zhimg.com/50/v2-f6103bfdac4b_b.jpg& data-rawwidth=&309& data-rawheight=&482& class=&content_image& width=&309&&&/figure&&figure&&img src=&https://pic2.zhimg.com/50/v2-b64f2d8262eff164dc737f_b.jpg& data-rawwidth=&279& data-rawheight=&522& class=&content_image& width=&279&&&/figure&net.minecraft.world.gen.layers其实才是生成生物群系(biome),也就是从二维上确定每个竖列(1*256*1)是什么类别,net.minecraft.world.biome管的是生成生物群系之后确定每个生物群系的特征。这个包里面的东西很重要,一会儿一起说:&br&&figure&&img src=&https://pic2.zhimg.com/50/v2-89fe3c1ed0cb9c5eddca_b.jpg& data-rawwidth=&472& data-rawheight=&484& class=&origin_image zh-lightbox-thumb& width=&472& data-original=&https://pic2.zhimg.com/50/v2-89fe3c1ed0cb9c5eddca_r.jpg&&&/figure&net.minecraft.world.biome正如前面所说是各种生物群系的内容,这个也之后单独说:&br&&figure&&img src=&https://pic2.zhimg.com/50/v2-091ebc5cdabee870d3843_b.jpg& data-rawwidth=&293& data-rawheight=&650& class=&content_image& width=&293&&&/figure&&br&net.minecraft.world.gen.structure管的是复杂的大型人文景观生成(村庄,1.9之后的末地城,废弃矿洞,下界要塞,海底遗迹):&br&&figure&&img src=&https://pic2.zhimg.com/50/v2-6f56fe75eadf5db901f76_b.jpg& data-rawwidth=&395& data-rawheight=&506& class=&content_image& width=&395&&&/figure&&br&net.minecraft.world.gen.structure.template是按照文件中定义的方块模板来确定真正使用的方块,就像自然生成的草地并不是千篇一律的,而是会随机地旋转一定的角度,就是这个东西在帮忙:&br&&figure&&img src=&https://pic4.zhimg.com/50/v2-6f3fd8adae0_b.jpg& data-rawwidth=&377& data-rawheight=&150& class=&content_image& width=&377&&&/figure&net.minecraft.village和net.minecraft.world.end是管理村庄和末地的特殊活动(和村民交♂易,打末影龙):&br&&figure&&img src=&https://pic4.zhimg.com/50/v2-b45aebfdde3078baf7f8602_b.jpg& data-rawwidth=&412& data-rawheight=&380& class=&content_image& width=&412&&&/figure&&br&各个包的介绍就到这里,接下来介绍一下生成一个区块的整体流程,这里我截一段net.minecraft,world.gen.ChunkProvideOverworld(生成主世界区块)里面的代码来描述,加了中文注释:&br&&div class=&highlight&&&pre&&code class=&language-text&&public Chunk provideChunk(int x, int z)
// 生成一个区块专属的伪随机种子,这个种子只跟x和z的位置有关
this.rand.setSeed((long)x * L + (long)z * L);
ChunkPrimer chunkprimer = new ChunkPrimer(); // 封装了一下方块设置的操作
// 生成低分辨率的生物群系、密度图、方块,后面会讲
this.setBlocksInChunk(x, z, chunkprimer);
// 生成标准大小的生物群系
this.biomesForGeneration = this.worldObj.getBiomeProvider().loadBlockGeneratorData(this.biomesForGeneration, x * 16, z * 16, 16, 16);
// 按照生物群系替换掉高度图里面的普通方块
this.replaceBiomeBlocks(x, z, chunkprimer, this.biomesForGeneration);
// 前面生成选项里面看到的各种设置,要不要洞穴之类的
if (this.settings.useCaves)
this.caveGenerator.generate(this.worldObj, x, z, chunkprimer);
if (this.settings.useRavines)
this.ravineGenerator.generate(this.worldObj, x, z, chunkprimer);
if (this.mapFeaturesEnabled)
if (this.settings.useMineShafts)
this.mineshaftGenerator.generate(this.worldObj, x, z, chunkprimer);
if (this.settings.useVillages)
this.villageGenerator.generate(this.worldObj, x, z, chunkprimer);
if (this.settings.useStrongholds)
this.strongholdGenerator.generate(this.worldObj, x, z, chunkprimer);
if (this.settings.useTemples)
this.scatteredFeatureGenerator.generate(this.worldObj, x, z, chunkprimer);
if (this.settings.useMonuments)
this.oceanMonumentGenerator.generate(this.worldObj, x, z, chunkprimer);
// 构造区块对象,把方块和生物群系数据填进去
Chunk chunk = new Chunk(this.worldObj, chunkprimer, x, z);
byte[] abyte = chunk.getBiomeArray();
for (int i = 0; i & abyte. ++i)
abyte[i] = (byte)Biome.getIdForBiome(this.biomesForGeneration[i]);
// 生成基础的天空光照(见前文)
chunk.generateSkylightMap();
&/code&&/pre&&/div&重点的setBlocksInChunk函数:&br&&div class=&highlight&&&pre&&code class=&language-text&&public void setBlocksInChunk(int x, int z, ChunkPrimer primer)
this.biomesForGeneration = this.worldObj.getBiomeProvider().getBiomesForGeneration(this.biomesForGeneration, x * 4 - 2, z * 4 - 2, 10, 10); // 这是一个低分辨率的生物群系表,用在generateHeightmap里面的
// 生成密度图,原文虽然写的是高度图,但是实际上是密度图,可能是fml的人理解有问题
this.generateHeightmap(x * 4, 0, z * 4);
// 这里往下的这么一大段都是在插值,密度大于0的填上石头,低于海平面而且不是石头的
// 填上海水,别的都是空气。
// 话说这段真是乱七八糟,一堆magic number,估计fml的人也懒得看懂,
// 既没加注释也没改变量名。。。
for (int i = 0; i & 4; ++i)
// 为什么是33和5呢? 33=32+1, 5=4+1
int j = i * 5;
int k = (i + 1) * 5;
for (int l = 0; l & 4; ++l)
int i1 = (j + l) * 33;
int j1 = (j + l + 1) * 33;
int k1 = (k + l) * 33;
int l1 = (k + l + 1) * 33;
for (int i2 = 0; i2 & 32; ++i2)
double d0 = 0.125D;
double d1 = this.heightMap[i1 + i2];
double d2 = this.heightMap[j1 + i2];
double d3 = this.heightMap[k1 + i2];
double d4 = this.heightMap[l1 + i2];
// 于高度y方向线性插值
double d5 = (this.heightMap[i1 + i2 + 1] - d1) * 0.125D;
double d6 = (this.heightMap[j1 + i2 + 1] - d2) * 0.125D;
double d7 = (this.heightMap[k1 + i2 + 1] - d3) * 0.125D;
double d8 = (this.heightMap[l1 + i2 + 1] - d4) * 0.125D;
// 于x和z方向线性插值
for (int j2 = 0; j2 & 8; ++j2)
double d9 = 0.25D;
double d10 = d1;
double d11 = d2;
double d12 = (d3 - d1) * 0.25D;
double d13 = (d4 - d2) * 0.25D;
for (int k2 = 0; k2 & 4; ++k2)
double d14 = 0.25D;
double d16 = (d11 - d10) * 0.25D;
double lvt_45_1_ = d10 - d16;
for (int l2 = 0; l2 & 4; ++l2)
if ((lvt_45_1_ += d16) & 0.0D)
primer.setBlockState(i * 4 + k2, i2 * 8 + j2, l * 4 + l2, STONE);
else if (i2 * 8 + j2 & this.settings.seaLevel)
primer.setBlockState(i * 4 + k2, i2 * 8 + j2, l * 4 + l2, this.oceanBlock);
d10 += d12;
d11 += d13;
&/code&&/pre&&/div&感觉这一段还要解释一下。&br&x和z不是16,32这样的世界坐标,而是区块坐标,是世界坐标的16/1,也就是区块(1, 2)代表了世界坐标上面的(16,32)到(31,47)的矩形区域。&br&这里首先生成了一个低分辨率的生物群系,长宽是10*10,是因为接下来要用的密度图长宽是5*5。&br&然后生成密度图,这个待会儿也会讲。之所以说是密度图而不是高度图是因为它是三维的5*33*5,而不是二维的结构,所以只能称之为密度。&br&接下来是线性插值。一个区块是16*256*16,而为了让密度更为平滑,这里把密度图的长宽各放大4倍,高度放大8倍,再做线性插值就会比较光滑不会变化特别突兀。&br&有人要问,5*4不是20吗?33*8不是264吗?其实因为5*33*5多出来的一圈是不用的,而且插值需要两个值才能计算,所以四个采样点其实需要5个数据,33也一样是这个道理。&br&再浅显一点来说,密度图里面的0,1,2,3,4(java数组从0开始)对应的是区块x或z轴里面的0, 4, 8, 12, 16,既然我们要计算位置为13,14,15的方块,那就得用12和16对应的3和4来插值。&br&插值的过程从循环变量为j2的那个循环开始,0.125就是8分之1,因为高度上放大8倍,后面的0.25也是因为放大了4倍,lvt_45_1其实可以写得易懂一点的,也是一个插值变量,跟前面的d1,d2,d3,d4,d10,d11是一样的,只不过mojang的程序猿为了装逼特地写了这么个东西罢了。。所以说代码质量差不仅在于架构,还在于其中各种细节的写法,像这种绝对就不算优秀的代码。&br&&br&先到这里吧。。还有好多。。各位先看着哈。。&br&&br&--------------------------------------------分割线-------------------------------------------------&br&&br&总结全文,深化主旨,首尾呼应:&br&Minecraft是个好游戏,但代码写的确实不咋地。&br&&br&我最初接触MC还是13年,那时候大部分还在用1.4.2或者1.5,当然,都是盗版。&br&一开始只是玩单机,打生存,搭房子,有时候开创造玩玩红石电路,特别喜欢一个人晚上独自边打MC边听他的bgm。C418的歌,很宁静,让人很放松。&br&后来基本上大部分玩法都玩儿遍了,也不怎么愿意去花功夫做那种超大的红石电路或者建筑之类的,总想着找点别的花样。于是我装了Forge,下了很多mod,比如IndustrialCraft这种大名鼎鼎的,还有一些辅助性的mod,比如1.6.2之后就停止开发的SPC(Single Player Command 卖安利)。SPC是模仿WorldEdit的担任命令行mod,就是各种批量编辑,一个人搭建筑非常方便(因为WorldEdit WE只有多人能用,SPC可以单人模式用)。结果换1.7.2之后SPC不支持了,于是就萌生了自己开发一个的想法。(好吧最后还是流产了,当时水平不够做出来的性能很差,现在有能力做了又没了当初那种激情)&br&又因为刚好学java,听说MC也是用java写的,所以就网上找教程写mod。我还记得14年的时候架着100K不到的梯子屁颠屁颠地装fml的时候,去maven central仓库上下东西动不动就停住了,结果一晚上都没装好。&br&国内(哪怕到今天)都缺少真正有质量的mod开发教程,特别是中文的基本没有。贴吧上曾找到过一篇讲mod开发环境配置的,不过也止于1.6.2,到了1.7之后FML的整个API变了好多,原来的教程都用不了了。&br&后来在网上找到了一篇文章:&a href=&//link.zhihu.com/?target=http%3A//notch.tumblr.com/& class=& external& target=&_blank& rel=&nofollow noreferrer&&&span class=&invisible&&http://&/span&&span class=&visible&&notch.tumblr.com/&/span&&span class=&invisible&&&/span&&/a& ,是MC最早的开发者Markus Persson(马库斯·泊松,网名notch)的个人博客,还有一篇讲述他的地形生成算法的博文:&br&&a href=&//link.zhihu.com/?target=http%3A//notch.tumblr.com/post//terrain-generation-part-1& class=& external& target=&_blank& rel=&nofollow noreferrer&&&span class=&invisible&&http://&/span&&span class=&visible&&notch.tumblr.com/post/3&/span&&span class=&invisible&&/terrain-generation-part-1&/span&&span class=&ellipsis&&&/span&&/a&&br&大概翻译一段(水平有限):&br&&blockquote&&p&In the very earliest version of Minecraft, I used a 2D Perlin noise heightmap to set the shape of the world. Or, rather, I used quite a few of them. One for overall elevation, one for terrain roughness, and one for local detail. For each column of blocks, the height was (elevation + (roughness*detail))*64+64. Both elevation and roughness were smooth, large scale noises, and detail was a more intricate one. This method had the great advantage of being very fast as there’s just 16*16*(noiseNum) samples per chunk to generate, but the disadvantage of being rather dull. Specifically, there’s no way for this method to generate any overhangs.&/p&&p&在Minecraft最早的版本中,我将一张2维perlin noise图作为高度图来确定世界的形状。或者说,好几张perlin noise图。一张是整体海拔,一张是地形的粗糙度,一张是小范围的细节。对于每一列方块,其高度是 (海拔+(粗糙度*细节))*64+64。 海拔和粗糙度的图都是连续光滑的,大尺度的噪声图,而细节则更加参差不齐。这个生成方式有一个巨大的优点就是奇快无比,因为对于每个区块,只不过需要生成16*16*(噪声图的数量) 个采样点,然而缺点就是地图相当单调无趣。更重要的是,这种方式根本生成不了悬崖。&/p&&br&&p&So I switched the system over into a similar system based off 3D Perlin noise. Instead of sampling the “ground height”, I treated the noise value as the “density”, where anything lower than 0 would be air, and anything higher than or equal to 0 would be ground. To make sure the bottom layer is solid and the top isn’t, I just add the height (offset by the water level) to the sampled result.&/p&&p&于是我改成了一种有些相似的、基于3维perlin noise的生成方式。我并不是对“地面高度”进行采样,而是将这个噪声的数值看做是“密度”(因此我说前面代码中应该是密度图而不是高度图 ——译者注),其中密度小于0的点都是空气而大于等于0的会成为大地(其实就是实心方块 ——译者注)。为了确保地图底端是地面而顶端是空气,我便把采样值加上采样点的高度(海拔高度)。(其实应该说是“减去采样点的高度”更易懂,因为大于0的才是方块,而水下的高度是负值 ——译者注)&/p&&br&&p&Unfortunately, I immediately ran into both performance issues and playability issues. Performance issues because of the huge amount of sampling needed to be done, and playability issues because there were no flat areas or smooth hills. The solution to both problems turned out to be just sampling at a lower resolution (scaled 8x along the horizontals, 4x along the vertical) and doing a linear interpolation. Suddenly, the game had flat areas, smooth hills, and also most single floating blocks were gone.&/p&&p&不幸的是,我立刻碰上了可玩性和效率两方面的麻烦。因为有大量的点需要被采样所以效率很成问题,又因为缺乏大片的平地或平滑的山丘而缺乏可玩性。这两个问题共同的解决方案最终定为了这样:用一个更低的分辨率去采样噪声图(横向8倍,纵向4倍)(代码里面是横向4倍纵向8倍,可能跟后来的代码改动有关 ——译者注)并且进行线性插值(见setBlocksInChunk函数 ——译者注)。突如其来地,世界上有了平原、丘陵,而且单独浮在空中的诡异方块中的绝大部分也都消失无踪了。(其实还有很多了啦 ——译者注)&/p&&/blockquote&这激起了我浓厚的兴趣。于是我把长长的代码打印下来,8号字体,一面3列,足足十几张纸,每天一有空就看代码,读代码,用笔做注释,并且为我觉得命名不当的部分想一个更好的名称。到现在这些还藏在我的书桌里面。这些东西百度根本搜不到,google也很少有结果,而以fml对这些代码的注释程度来看他们根本懒得去读这些既混乱又不必要读懂的代码。&br&当然后来还是不了了之了。我自己也仿造过MC,当时就是完全追星似的疯狂模仿,用的java,跟MC一样的LWJGL库,同样(好吧我承认这么做有盗版的嫌疑)的纹理,几乎相同的文件格式、区块大小、渲染原理、(基于notch文章的)地形生成、体系架构,几乎就是我前面写的这么多的一个自己的重现。我甚至还学习MC混淆了编译后的代码。可笑的是,迫于当时的水平,效率竟比MC原版还要低。最后开发进程是因为我U盘的丢失而终止的,源码自然也丢了,只剩一份速度奇慢的编译版本,下载地址:&a href=&//link.zhihu.com/?target=http%3A//pan.baidu.com/s/1eQvM0U2& class=& external& target=&_blank& rel=&nofollow noreferrer&&&span class=&invisible&&http://&/span&&span class=&visible&&pan.baidu.com/s/1eQvM0U&/span&&span class=&invisible&&2&/span&&span class=&ellipsis&&&/span&&/a&,有兴趣的自己下载来玩玩吧。&br&今年想过要重制,结果忙了一个夏天又泡汤了,现在想来总觉得有点可惜。&br&后来被朋友忽悠着去打了一段时间MC服务器,也买了正版,不过服务器没有打过一个月的,基本两三个星期就崩掉关服不开放了,也不知道是我的问题还是整个现状都这样。(我真的没有干过坏事啊!)据说国内开一个服很快就会有黑客来勒索要钱否则就DDoS攻击(Distributed Denial of Service分布式拒绝服务攻击,用大量肉鸡的流量淹没对方,让对方或因为过高的流量费用而关闭服务,或因为过低的响应速度而降低服务质量到一个不可接受的程度),也不知道是不是真的,反正崩了好几个服,打拼好久的家园东西都没了,也就不打了。&br&最近确实玩的不多,也就偶尔休息的时候像以前那样,搞搞建筑,搭搭红石电路之类的,都是单机玩,一方面没空,一方面也有点享受那种独自一人的感觉和C418的背景音乐。&br&&br&我知道这段算不上问题的“从编程的角度来看”,不过写了前面这么多,也使我回想起来以前发生过的不少。要不是这个答案,也不知道这段往事会被封存到什么时候。就借此机会,一吐为快吧。希望大家不必计较,并且欢迎任何技术上的讨论。&br&&br&以上。&br&&br&&br&&br&&br&各位看官看完记得点个赞再走啊。。。谢啦。。。
11.30更新:见后面地形生成和总结部分。过百赞了知乎小透明好开森,谢谢大家! 12.3更新:谢大家厚爱。回答一位
的问题,见文章:,欢迎讨论。 研究过MC地形生成和基础架构的强答一发。 长文预警。 先说架构。 MC分客户端和服务端两面…
&p&我参加过多次的校园招聘,主要是软件开发方向,这里把面试过程中遇到的一些问题总结下,希望对各位同学有所帮助。&/p&&p&1. 简历&/p&&p&
让我们从简历开始,除了笔试分数,这是面试官了解学生情况的唯一途径,简历能否体现个人的特点与优势,决定了学生能否进入面试阶段。&/p&&p&
一份简历大概分为如下几个部分,或者说,面试官也只关注这几个部分。&/p&&p&(1)基本信息。&/p&&p&
基本信息虽然很简单,但很多时候会导致一票否决,比如意向地区,上海的面试官会把意向地区不是上海的直接过滤掉。提交简历之前最好能确认下当前批次是哪个区域的面试官过来招聘。&/p&&p&(2)项目经历&/p&&p&
这个是非常看重的一部分,项目的内容,复杂度,个人在项目中承担的角色,工作,结果等能很好的体现一个人的综合能力,通常也是面试官和学生交流的重点。&/p&&p&(3) 实习经历&/p&&p&
根据在公司实习的情况,可以考察一个人能否很好的适应比较正式的工作环境,和同事相处的情况,对团队协作的理解,对工作流程的理解等。&/p&&p&(4) 获奖情况&/p&&p&
我们会注意到比较大的奖项。大部分人的简历上都有奖,只是有含金量的奖非常少,很多人罗列一大堆校级的优秀学生,奖学金,小型比赛的奖,意义其实不是特别大。&/p&&p&(5) 其它能体现个人能力和特点的信息&/p&&p&
有人会写自己的感悟或者总结,这一点很能吸引面试官的注意,有思想,能独立思考的人总是喜欢的。还有比较好的是个人作品,一个库,一个App,能成事的人也总是会受欢迎的。如果还有,体育或者文艺特长,篮球,街舞,小提琴等,公司里面总会有体育比赛和晚会,如果有这方面的优势,也会为你加分。&/p&&p&
简历的目标是能体现自己的能力,让面试官选中自己,进入面试,毫无特点的简历会被过滤掉。什么叫毫无特点的简历?比如简历上罗列了自己所上的课程列表,还有自己的项目简介。所有的学生都可以这样写,如果你的简历换个名字看起来也没有问题,那么就表示你的简历毫无特色,因为在这个简历里面完全看不到你个人的特点和优势。&/p&&br&&p&2. 实践&/p&&p&
实践非常重要,看起来非常明显的一个问题,但不清楚为什么很多学生根本不重视实践。作为计算机科班的学生,写代码就是实践。很多学生除了作业和课程设计,几乎不写别的代码。研究生会多一些导师的项目,但也就没有别的了。计算机语言和英语一样,不记单词不写短文不练口语怎么学好英语?不写代码永远也不理解什么叫代码设计,什么叫性能优化,什么叫debug。曾经遇到一个学生,说他看了一年的书,但是没有写一行代码,就好像看完武林秘籍但是根本不练武,有什么用呢?我建议一个学生在大学阶段至少写到5万行代码,你才能大致理解什么叫编程。&/p&&p&
我曾经问过一些学生,你为什么不写代码?答:老师没有布置。&/p&&br&&p&3. 算法与数据结构&/p&&p&
重要么?重要。这是程序员的基础,我建议计算机专业的学生都应该自己把所有基础算法都实现一遍。虽然以后的工作中算法与数据结构的工作占比可能很少,但这是理解编程框架,系统机制,性能优化的一些基础知识。实现基本的算法与数据结构,可以对相关的算法有更深入的认识,可以感受到对内存的细微操作,可以体会对数据操纵,可以培养“程序”的感觉。算法与数据结构基础好,也可以说明逻辑思维能力好,也是程序员特质的一种表现。&/p&&br&&p&4. ACM&/p&&p&
大概5年前,如果你的简历上出现了ACM的信息,还是很酷的一件事情,不过现在ACM在简历上泛滥了。几乎每份简历都会有ACM的信息,大家都有的话就不算特色了。有的简历没有其它项目经验,仅列出一堆的ACM获奖情况,这个能说明你编程特别牛么?ACM偏重于算法与数据结构,但语言本身,文件系统,网络操作等基础没有涉及。曾经叫一个参加过一堆ACM比赛的人写一个统计文章词频的程序,说不知道如何读文件,问用socket写过网络程序没,也没有,大学4年就练习算法了。任何程序员都必须在一个实际存在的操作系统上工作,都必须和文件,网络打交道,还要理解进程,线程,磁盘,内存等。算法不是不重要,但用不着4年时间只学一个算法,起码用1年的时间学学网络编程,文件读写,体验下Windows操作系统,iOS,Android的系统,做个实际的项目多接地气啊。&/p&&br&&p&5. 项目与工作室&/p&&p&
这个非常重要。有的地区氛围浓厚一些,很多简历上都标明自己是某某工作室的,面试官就特别喜欢。因为参与过一个实际的项目,不仅能证明自己的动手能力,还表明也积累了一些项目经验。一个实际的项目和做课程设计相差太远了。需要考虑用户需求,做产品特性规划,架构设计,模块设计,人力安排,沟通协作,风险把控,质量测试,性能优化,发布推广,用户问题跟进,是一个比较完整的项目流程。有过这个经历的学生能比较快的融入公司的工作环境,知道自己应该如何工作。还有的学生在项目过程中为了解决实际问题而在某个领域里面深入专研,积累了丰富的经验,技术水平比普通的学生要好出不少。这些都是面试官非常看中的。有的地区氛围不那么浓,比较少有项目,怎么办呢?太简单了,拉上几个小伙伴,找一个大家感兴趣的方向,马上就可以发起一个项目。有没有用户都不要紧,关键是自己参与了这个过程,实践了自己的专业。&/p&&p&
很多简历上的项目经历都是空白,问为什么,说,老师没有布置。&/p&&br&&p&6. 学生社团与学生会&/p&&p&
很多人在简历上说自己参加了某某社团,参加了学生会。举办过某某活动,招募了多少团员之类。这个对于面试用处大么?看情况。我曾经遇到一个学生,这个学生为了社团尽心尽力,4年时间都用在社团活动上了,但自己专业的课程没有学好,这就本末倒置了。我们看中的是你在社团与学生会活动中所获得的经验,比如与人沟通的技巧,分工协作的经验,组织管理能力等。这个属于加分项。&/p&&br&&p&7. 跨专业找工作&/p&&p&
经常会遇到其他专业的学生来找计算机的工作。跨专业的学生,通常会有这样的问题,他们在基础方面不如计算机专业的学生学得扎实。要做一个软件,或者要做一个App,现在的门槛其实不高。使用Visual Studio,XCode,Android Studio来做一个简单的应用,任何一个理工科专业的大学生,只要有少许的基础和兴趣,通过1个月的学习,应该就可以做出简单的应用了。但是,计算机组成原理,编译原理,操作系统,算法与数据结构,软件工程,这些如果没有学过的话,对编程或者软件的理解会非常浅。举个例子,Photoshop任何人学习1个月基本上都可以做简单的操作了,但能用Photoshop来进行艺术创作么?Photoshop或者XCode,都是工具而已。&/p&&p&
曾经录取的跨专业的学生,都在基础方面下了很深的功夫,甚至比计算机专业的都要好。所以,跨专业本身并不是问题,还是看学习的深度。&/p&&br&&p&8. 实习&/p&&p&
实习经历和项目经历一样重要。特别是大公司的实习经历,会有更多的加分。在实际的公司里面做具体的工作,能感受到实际的工作流程,自己能获得正规的指导,培训,如果公司对自己的评价还不错,那本身也代表了一种认可。那么如何获得大公司的实习机会?通过实习生招聘。&/p&&br&&p&从公司的角度,我们希望看到怎样的应聘者?我们希望你具备:&/p&&p&聪明:逻辑思维好,反应快。&/p&&p&勤奋:大量的实践活动。&/p&&p&沟通好:表达清晰,有条理。&/p&&p&有思想:有自己的观点。&/p&&p&爱学习:主动关注专业领域内的知识,在深度与广度上探索。&/p&&p&专注:能在一个特定领域进行深入研究。&/p&&p&项目经验:参与过实际的项目。&/p&&br&&p&祝大家都能找到心仪的工作。&/p&
我参加过多次的校园招聘,主要是软件开发方向,这里把面试过程中遇到的一些问题总结下,希望对各位同学有所帮助。1. 简历 让我们从简历开始,除了笔试分数,这是面试官了解学生情况的唯一途径,简历能否体现个人的特点与优势,决定了学生能否进入面试阶段。…
&figure&&img src=&https://pic2.zhimg.com/v2-cf6badfa3ebe769df50e045bad08b33d_b.jpg& data-rawwidth=&1728& data-rawheight=&1080& class=&origin_image zh-lightbox-thumb& width=&1728& data-original=&https://pic2.zhimg.com/v2-cf6badfa3ebe769df50e045bad08b33d_r.jpg&&&/figure&&p&写作上,我真是个很随性的人,挖再多的坑都不一定会去补上,反正读者也没一直催...正因为随性,我才说过曾经写的那本书的经历实在是痛苦,虽然写完后给我带来的好处还是很多的。也是因为随性,将公众号沉静了个把月,发神经去写博客 &a href=&https://link.zhihu.com/?target=http%3A//evilcos.me& class=& external& target=&_blank& rel=&nofollow noreferrer&&&span class=&invisible&&http://&/span&&span class=&visible&&evilcos.me&/span&&span class=&invisible&&&/span&&/a&,一写还写了好几篇。博客上主要还是技术,这是本,改不了。而公众号,技术点谈太深就不行了,更多还是科普及观点相关的文字。&/p&&p&我的读者们,尤其是收听这么久我公众号(包括本知乎专栏)的读者们,你们估计已经习惯我这样的风格了,那请继续保持习惯...你以为“懒人在思考”这个名字是怎么来的,就是因为懒,说好听点是“随性”:D&/p&&p&回归正题,我填下坑。&/p&&p&前两篇文章大家可以温习下:&a href=&https://link.zhihu.com/?target=http%3A//mp.weixin.qq.com/s%3F__biz%3DMzA3NTEzMTUwNA%3D%3D%26mid%3D%26idx%3D1%26sn%3D3c3cda13aa732%26chksm%3D8485d6dbb3f25fcdd9be028df67fdc2ad0af07e08e69c5328%26scene%3D21%23wechat_redirect& class=& wrap external& target=&_blank& rel=&nofollow noreferrer&&新人上手 Python 另类建议——被和谐了的答案&/a&&a href=&https://link.zhihu.com/?target=http%3A//mp.weixin.qq.com/s%3F__biz%3DMzA3NTEzMTUwNA%3D%3D%26mid%3D%26idx%3D1%26sn%3D9ccaf1cf71718ad9aff761%26chksm%3Db3f25fc01e668e79b85fe9e961fbd083fe85eaa0dffd3788%26scene%3D21%23wechat_redirect& class=& wrap external& target=&_blank& rel=&nofollow noreferrer&&爬虫 Tip 相关与不相关的几点补充&/a&&/p&&p&为什么说写爬虫很简单,你看源码:&a href=&https://link.zhihu.com/?target=https%3A//github.com/evilcos/crawlers& class=& external& target=&_blank& rel=&nofollow noreferrer&&&span class=&invisible&&https://&/span&&span class=&visible&&github.com/evilcos/craw&/span&&span class=&invisible&&lers&/span&&span class=&ellipsis&&&/span&&/a&&/p&&p&如果你按我前两篇文章说的,基于 Scrapy 写爬虫,那么我开源的这个小爬虫你可以直接参考:crawl3 目录下可以翻到 rosi.py,专门爬 &a href=&https://link.zhihu.com/?target=http%3A//rosioo.com& class=& external& target=&_blank& rel=&nofollow noreferrer&&&span class=&invisible&&http://&/span&&span class=&visible&&rosioo.com&/span&&span class=&invisible&&&/span&&/a& 这个站的美女图片。这样运行:&/p&&blockquote&&p&1. 按照官方文档安装 Scrapy&a href=&https://link.zhihu.com/?target=https%3A//doc.scrapy.org/en/1.2/intro/install.html& class=& external& target=&_blank& rel=&nofollow noreferrer&&&span class=&invisible&&https://&/span&&span class=&visible&&doc.scrapy.org/en/1.2/i&/span&&span class=&invisible&&ntro/install.html&/span&&span class=&ellipsis&&&/span&&/a&&/p&&p&2. git clone &a href=&https://link.zhihu.com/?target=https%3A//github.com/evilcos/crawlers& class=& external& target=&_blank& rel=&nofollow noreferrer&&&span class=&invisible&&https://&/span&&span class=&visible&&github.com/evilcos/craw&/span&&span class=&invisible&&lers&/span&&span class=&ellipsis&&&/span&&/a&&/p&&p&3. 创建个 /data/rosi/ 目录,用于保存下载的图片,当然这个路径可以在 settings.py 里修改&/p&&p&4. 进入 crawl3 目录,运行命令:scrapy crawl rosi&/p&&p&5. 没了&br&&/p&&/blockquote&&p&这份代码确实很少,也很简单且粗暴,但我估计能真的领悟深刻的人会很少。&/p&&p&我交代下我的爬虫生涯吧,2008年开始就写爬虫到现在,过程也发现了 Scrapy 等爬虫框架,但那时都太初始了,哪有现在用得这样爽?而我因为后来负责公司的漏洞扫描器引擎的编写,爬虫是我最费力写出来的,从零踏了无数坑,fix 了无数 bugs。回头看看 N 年前自己写的大爬虫,复杂到我想吐,估计后来那些参与维护我写的那个大爬虫的同学会恨死我...其实这不完全怪我呀,爬虫本就是个非常复杂的玩意,因为其面对的 Web 环境复杂度太高,我随便罗列些必定会踏到的坑吧:&/p&&blockquote&&p&字符集编码&br&HTML/XML不规范&br&链接伪协议及不规范&br&各种文件类型&br&网页跳转:服务端、客户端(Meta、JavaScript、Flash等)&br&Cookie/认证会话维护&br&代理维护&br&GPC(Cookie/Post/Get)参数提取&br&HTTP多种请求&br&超时:连接超时/读取超时&br&JavaScript动态出来的链接&br&AJAX请求&br&链接爬取去重算法&br&广度深度算法&br&并发:进程/线程/协程&br&调度:同步/异步&br&各种内存优化&br&各种异常维护&br&灵活配置&br&灵活扩展&br&优秀的文档...&br&&/p&&/blockquote&&p&我可以肯定是我现在由于赶时间,并没都罗列全面。经历过这些的人才会感慨:工程化能力的重要性啊...&/p&&p&你看,我上面开源的那个 Scrapy 小爬虫是多么的简单,然而,我强烈建议:&/p&&blockquote&&p&&strong&当你在享受这种快感的时候,去深度理解理解下,这种快感的由来吧。&/strong&&br&&/p&&/blockquote&&p&我愿意花钱去赞助一些优秀开源甚至花高价钱去买一些优秀工具,虽然我没钱。促使我这样做,以及不断学习的本质是:&strong&我感受到那种敬畏&/strong&。&br&&/p&&p&赶时间,这篇先这样,有空继续。如果你跟上了,欢迎邮件我:,邮件肯定看,由于精力问题,我不一定回复,请理解。&/p&&p&-----------------&br&微信公众号「&strong&Lazy-Thought&/strong&」&br&几个黑客在维护,都很懒,都想改变点什么&/p&
写作上,我真是个很随性的人,挖再多的坑都不一定会去补上,反正读者也没一直催...正因为随性,我才说过曾经写的那本书的经历实在是痛苦,虽然写完后给我带来的好处还是很多的。也是因为随性,将公众号沉静了个把月,发神经去写博客 ,…
&figure&&img src=&https://pic2.zhimg.com/v2-34e4d3682cbde0c212f2c_b.jpg& data-rawwidth=&1280& data-rawheight=&780& class=&origin_image zh-lightbox-thumb& width=&1280& data-original=&https://pic2.zhimg.com/v2-34e4d3682cbde0c212f2c_r.jpg&&&/figure&&p&想必不管是做Web前端还是后端,HTTP都是一个绕不开的话题,尤其是初入行的小白,更是被浏览器的界面和后端框架蒙蔽了双眼,在此,我们就来一探究竟。&/p&&p&本文主要探讨通过nc和curl这样的工具,探究在访问网页时,HTTP协议里到底在传输一些什么东西。&/p&&h1&环境准备&/h1&&ul&&li&Ubuntu&/li&&li&nc&/li&&li&curl&/li&&/ul&&p&可能你电脑里没有安装curl,那就需要安装一下:&/p&&div class=&highlight&&&pre&&code class=&language-text&&&span&&/span&sudo apt-get install curl -y
&/code&&/pre&&/div&&h1&背景知识&/h1&&ul&&li&基本的Linux操作技能&/li&&li&知道IP地址、端口号的概念&/li&&/ul&&p&在开始正式内容之前,有必要在此复习一下计算机网络相关的内容,作为后续的铺垫。&/p&&p&IP地址这个概念应该还比较好理解,这个展开说内容就老多了。知道一点就行:一般127.0.0.1指代本机。&/p&&p&端口号:没什么神秘的,一条网线接在一个电脑上,但是电脑上有很多个程序都要进行网络通信,那么当网口来了一坨数据的时候,这些数据哪些交给QQ,哪些交给浏览器呢?这时候端口号就发挥作用了,在TCP和UDP这样的网络通讯协议里面,每个数据包里头都有一个源端口号和目的端口号,而通讯双方的电脑里也保存着哪个端口号对应的是哪个进程(这里的对应关系是可以由运行的进程主动去设置的),这样,电脑收到数据的时候,根据数据里的目的端口号就能判断这个数据该交给QQ还是浏览器了。&/p&&p&以上就是一个粗略的介绍。接下来开始实操,动手。&/p&&h1&准备:体验一下工具&/h1&&p&首先,打开两个终端。一个充当客户端,一个充当服务端。&/p&&p&这里需要用上nc这个工具了。首先,让nc启动之后监听某个端口(这里选择8000端口),等待TCP链接:&/p&&div class=&highlight&&&pre&&code class=&language-text&&&span&&/span&$ nc -l 8000
&/code&&/pre&&/div&&p&这个命令看不明白不要紧,解释一下。大致的意思就是说,nc这个程序启动了,并且在此宣布,本机的8000端口被它承包了,所有向8000端口发起的TCP链接都会由nc接管。&/p&&p&运行之后,什么都没有,终端也就卡在那里了,OK,毕竟人家在等嘛,而且是在线等,挺急的。&/p&&p&不管了,切换到第二个终端,然后使用这样的命令:&/p&&div class=&highlight&&&pre&&code class=&language-text&&&span&&/span&$ nc 127.0.0.1 8000
&/code&&/pre&&/div&&p&好了,这个命令的意思就是向127.0.0.1这个主机的8000端口发起连接,兄弟,咱们来说说悄悄话呢?这里的127.0.0.1就是本机的IP地址。&/p&&p&好了,这下两个终端里的nc就通过TCP建立起链接了。&/p&&p&接下来,随便选一个终端,在里面输入一些英文字母,然后敲一下回车,看一下另一个终端会发生什么。&/p&&p&诶?神奇了,一个终端中输入的内容会在敲下回车之后出现在另一个终端里面,反过来也是一样的。这里发生的也就是两个nc进程通过本机的8000端口建立了TCP连接,双方相互通信的过程。&/p&&h2&在两个不同的机器上&/h2&&p&如果是在一个局域网内,有两个电脑,一台电脑的IP为&A&,另一台的IP为&B&,那么,你可以在A电脑上运行:&/p&&div class=&highlight&&&pre&&code class=&language-text&&&span&&/span&$ nc -l 8000
&/code&&/pre&&/div&&p&然后在B电脑上运行(这里的&A&就是A主机的IP地址):&/p&&div class=&highlight&&&pre&&code class=&language-text&&&span&&/span&$ nc &A& 8000
&/code&&/pre&&/div&&p&然后这两台主机就建立起TCP连接了,而且可以相互通信了,在一边输入内容按下回车后,另一边也能看见相应的内容了。有了这样的工具,稍后就可以通过一些操作,来看清楚HTTP协议里面,到底在传输一些什么东西。&/p&&p&这里的A主机就是服务端,B主机就是客户端。服务端先运行起来,等着客户端去找它。在Web这边,在访问网页的时候也是如此。&/p&&h1&HTTP响应&/h1&&p&浏览器在访问网站的时候,服务器到底给浏览器回复了一些什么玩意儿呢?&/p&&p&有了上面的工具,接下来该探究一下HTTP了,到这里才正式开始切入正题了。&/p&&p&首先,使用一下curl。curl能够向指定的URL发起一个http请求,并显示出请求回应中的内容。先拿百度开刀,执行命令:&/p&&div class=&highlight&&&pre&&code class=&language-text&&&span&&/span&$ curl -i http://baidu.com
&/code&&/pre&&/div&&p&就能得到如下的输出结果:&/p&&div class=&highlight&&&pre&&code class=&language-text&&&span&&/span&HTTP/1.1 200 OK
Date: Fri, 25 Nov :59 GMT
Server: Apache
Last-Modified: Tue, 12 Jan :00 GMT
ETag: &51-47cf7e6ee8400&
Accept-Ranges: bytes
Content-Length: 81
Cache-Control: max-age=86400
Expires: Sat, 26 Nov :59 GMT
Connection: Keep-Alive
Content-Type: text/html
&meta http-equiv=&refresh& content=&0;url=http://www.baidu.com/&&
&/code&&/pre&&/div&&p&这里,你所看到的这一大堆数据,便是一个完整的HTTP响应报文。也就是你在浏览器中输入网址&a href=&https://link.zhihu.com/?target=http%3A//baidu.com& class=& external& target=&_blank& rel=&nofollow noreferrer&&&span class=&invisible&&http://&/span&&span class=&visible&&baidu.com&/span&&span class=&invisible&&&/span&&/a&敲下回车之后,baidu服务器返回给浏览器的数据。&/p&&p&了解HTTP响应的格式,以及其中的各部分字段的含义,有助于我们理解一些Web应用的功能以及浏览器的行为。&/p&&p&这里对HTTP响应的数据稍作分析。上述报文中的第一行:&/p&&div class=&highlight&&&pre&&code class=&language-text&&&span&&/span&HTTP/1.1 200 OK
&/code&&/pre&&/div&&p&为HTTP响应的状态,这里的200代表OK,表示服务器已经成功地处理了请求。报文往后的内容直到其中的空行部分:&/p&&div class=&highlight&&&pre&&code class=&language-text&&&span&&/span&Date: Fri, 25 Nov :59 GMT
Server: Apache
Last-Modified: Tue, 12 Jan :00 GMT
ETag: &51-47cf7e6ee8400&
Accept-Ranges: bytes
Content-Length: 81
Cache-Control: max-age=86400
Expires: Sat, 26 Nov :59 GMT
Connection: Keep-Alive
Content-Type: text/html
&/code&&/pre&&/div&&p&这部分为HTTP响应的头部,其中大部分内容其实是可读性较强的,基本可以直接分析其中的含义。如Content-Type: text/html表示此次HTTP响应的结果为html文本,浏览器便可以根据头部的这个信息判断响应的内容应该当作html来处理。如果你访问的是一个图片,那么这里的Content-Type则可能会是image/png之类的。头部中的Content-Length: 81表示整个响应的正文长度为81,这个也比较关键,告诉浏览器整个响应的正文部分数据有多少。&/p&&p&HTTP响应的头部中有许多非常有用的信息,这些信息往往决定着浏览器收到报文后接下来需要做出什么样的行动。其中有一些内容在多数场景下都不是必须的,比如Server、Accept-Ranges。&/p&&p&响应中的最后一部分内容:&/p&&div class=&highlight&&&pre&&code class=&language-text&&&span&&/span&&html&
&meta http-equiv=&refresh& content=&0;url=http://www.baidu.com/&&
&/code&&/pre&&/div&&p&为响应的正文部分,这部分内容的长度是和响应头部的Content-Length对应值一致。&/p&&p&归结到底,HTTP响应报文中包含的数据有:&/p&&ul&&li&状态&/li&&li&头部&/li&&li&正文(可能没有)&/li&&/ul&&p&当没有正文的时候,Content-Length对应的值为0。&/p&&h2&手工构造一个HTTP响应&/h2&&p&上面大致了解了HTTP响应的大体结构,那么,我们来自己构造一个合法的HTTP响应,看看浏览器能不能做出正常的反应。&/p&&p&首先,用nc监听本地的8000端口:&/p&&div class=&highlight&&&pre&&code class=&language-text&&&span&&/span&nc -l 8000
&/code&&/pre&&/div&&p&接着,在浏览器中访问:&/p&&div class=&highlight&&&pre&&code class=&language-text&&&span&&/span&http://127.0.0.1:8000
&/code&&/pre&&/div&&p&这时候,你可以看到,浏览器一直在转圈圈就像网站卡住了一样。回头看终端里的nc,能够看到一大堆内容:&/p&&div class=&highlight&&&pre&&code class=&language-text&&&span&&/span&GET / HTTP/1.1
Host: localhost:8002
Connection: keep-alive
Upgrade-Insecure-Requests: 1
User-Agent: Mozilla/5.0 (M Intel Mac OS X 10_12_1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/54.0.2840.98 Safari/537.36
Accept: text/html,application/xhtml+xml,application/q=0.9,image/webp,*/*;q=0.8
Accept-Encoding: gzip, deflate, sdch, br
Accept-Language: en-US,q=0.8,zh-CN;q=0.6,q=0.4,zh-TW;q=0.2
&/code&&/pre&&/div&&p&这便是浏览器发出去的HTTP请求的内容,这个稍后再作分析。我们在这里先构造出一个合法的HTTP响应报文,回送给浏览器,让它去解析并显示出网页。趁浏览器卡住的时候,别急着关闭终端中的nc,将下面这段数据直接粘贴到nc所在的终端中,并回车:&/p&&div class=&highlight&&&pre&&code class=&language-text&&&span&&/span&HTTP/1.1 200 OK
Content-Length: 14
Content-Type: text/html
&h1&Hello&/h1&
&/code&&/pre&&/div&&p&接下来会到浏览器,你就能看到,浏览器能够正常显示出网页了,里面出现了一个大标题Hello。&/p&&p&其实,浏览器在发起一个请求之后,只要服务器回应它一个符合HTTP标准的报文,它就能正常地解析并做出处理。而无论浏览器访问的对方是真的Web服务器还是假的nc,浏览器并不能知道它访问的目标到底是个什么东西,它只知道按照HTTP规范去解析收到的数据。(这里可以看到,在网络中骗过浏览器是非常容易的,如传输的数据是未加密的,在这些传输的过程中很有可能会被拦截甚至篡改,浏览器被欺骗,进而用户被欺骗)&/p&&h1&HTTP请求&/h1&&p&在使用浏览器访问网站的时候,浏览器到底发送了一些什么东西过去呢?&/p&&p&上一段在自己构造HTTP响应的时候,已经看到了浏览器发给服务器的请求是什么样子:&/p&&div class=&highlight&&&pre&&code class=&language-text&&&span&&/span&GET / HTTP/1.1
Host: localhost:8002
Connection: keep-alive
Upgrade-Insecure-Requests: 1
User-Agent: Mozilla/5.0 (M Intel Mac OS X 10_12_1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/54.0.2840.98 Safari/537.36
Accept: text/html,application/xhtml+xml,application/q=0.9,image/webp,*/*;q=0.8
Accept-Encoding: gzip, deflate, sdch, br
Accept-Language: en-US,q=0.8,zh-CN;q=0.6,q=0.4,zh-TW;q=0.2
&/code&&/pre&&/div&&p&首先,和HTTP响应类似,这个请求报文的第一行内容:&/p&&div class=&highlight&&&pre&&code class=&language-text&&&span&&/span&GET / HTTP/1.1
&/code&&/pre&&/div&&p&为HTTP请求行,它里面包含了请求方法、请求路径和HTTP协议的版本三个信息。当你访问http://127.0.0.1/hello/world的时候,请求行就变成了:&/p&&div class=&highlight&&&pre&&code class=&language-text&&&span&&/span&GET /hello/world HTTP/1.1
&/code&&/pre&&/div&&p&整个请求报文余下的部分内容则为请求的头部,也和HTTP响应的结构类似。请求头部会附带一些关于浏览器自己的信息,比如上述头部中的User-Agent的值代表了当前是一个什么浏览器,那么这样服务器就可以通过这个字段的内容判断用户所使用的浏览器(当然,这是可以伪造的),借助于此还可以做到根据不同的浏览器返回不同的页面(比如手机和PC的页面不一样)。&/p&&p&但是这里没看到“正文”呢?前面说到,HTTP的响应中Content-Length可能为0,那么就意味着没有正文。这里也一样,在HTTP请求中,GET请求没有正文部分。而POST类型的请求,比如在你在浏览器中输入用户名密码点击登陆之后,浏览器发出的报文可能就是长这样的:&/p&&div class=&highlight&&&pre&&code class=&language-text&&&span&&/span&POST /user/login HTTP/1.1
Host: example.com
Connection: keep-alive
Upgrade-Insecure-Requests: 1
User-Agent: Mozilla/5.0 (M Intel Mac OS X 10_12_1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/54.0.2840.98 Safari/537.36
Accept: text/html,application/xhtml+xml,application/q=0.9,image/webp,*/*;q=0.8
Accept-Encoding: gzip, deflate, sdch, br
Accept-Language: en-US,q=0.8,zh-CN;q=0.6,q=0.4,zh-TW;q=0.2
Content-Length: 27
username=test&password=test
&/code&&/pre&&/div&&p&也就是你输入的用户名和密码是放在正文部分发送到服务器的,服务器根据自己的解析规则再将其解析出来。这里其实也就意味着,如果你在登录一个网站,期间如果遇到数据被它人拦截这样的问题,那么,你的密码就...&/p&&h2&手工构造一个HTTP请求&/h2&&p&上面说到我们可以收工构造一个HTTP响应,同样自己也可以收工构造一个HTTP请求。首先在终端里,输入:&/p&&div class=&highlight&&&pre&&code class=&language-text&&&span&&/span&$ nc baidu.com 80
&/code&&/pre&&/div&&p&通过nc去连接&a href=&https://link.zhihu.com/?target=http%3A//baidu.com& class=& external& target=&_blank& rel=&nofollow noreferrer&&&span class=&invisible&&http://&/span&&span class=&visible&&baidu.com&/span&&span class=&invisible&&&/span&&/a&这个主机的80端口,连接好之后,我们便可以通过手工输入内容来发起一个HTTP请求。像这样:&/p&&div class=&highlight&&&pre&&code class=&language-text&&&span&&/span&GET / HTTP/1.1
Host: baidu.com
&/code&&/pre&&/div&&p&这就是一个最精简的请求,可以直接在nc连接到&a href=&https://link.zhihu.com/?target=http%3A//baidu.com& class=& external& target=&_blank& rel=&nofollow noreferrer&&&span class=&invisible&&http://&/span&&span class=&visible&&baidu.com&/span&&span class=&invisible&&&/span&&/a&的80端口后,直接粘贴上述数据,然后回车。之后,nc会收到一个来自百度的响应:&/p&&div class=&highlight&&&pre&&code class=&language-text&&&span&&/span&HTTP/1.1 200 OK
Date: Fri, 25 Nov :28 GMT
Server: Apache
Last-Modified: Tue, 12 Jan :00 GMT
ETag: &51-47cf7e6ee8400&
Accept-Ranges: bytes
Content-Length: 81
Cache-Control: max-age=86400
Expires: Sat, 26 Nov :28 GMT
Connection: Keep-Alive
Content-Type: text/html
&meta http-equiv=&refresh& content=&0;url=http://www.baidu.com/&&
&/code&&/pre&&/div&&p&和我们前面通过curl去访问&a href=&https://link.zhihu.com/?target=http%3A//baidu.com& class=& external& target=&_blank& rel=&nofollow noreferrer&&&span class=&invisible&&http://&/span&&span class=&visible&&baidu.com&/span&&span class=&invisible&&&/span&&/a&得到的响应格式一样。那么说明刚才收工构造的HTTP请求是一个合法的请求,能够被对方的服务器正确解析。&/p&&p&(实际上,骗过服务器也是非常容易的,假如一个删除好友的请求数据被黑客通过一个动过手脚的路由器截获到,他再对请求数据稍加修改,冒充用户将其发送给服务器,是不是另一个好友也可能被悄悄地删掉了呢?)&/p&&h1&最后&/h1&&p&主要介绍了HTTP的一些常识,并演示了如何通过一些常见的命令工具去探索HTTP的报文内容,当然,能达到这类目的的方法有许多,这算是操作比较简单的方法之一。&/p&&p&学习Web,有必要对HTTP有一个比较深入的理解,本文旨在提供一种简单可行的学习、实验方法,闲来可以通过这样一些简单的手段去探索HTTP下的秘密。&/p&&p&实际上,按照前面的演示,在网络中,每一个应用都没法知道对面到底是个什么东西,只要双方相互收发的数据符合一定的格式和规则,它

我要回帖

更多关于 西电微机原理期末试题 的文章

 

随机推荐