酷派大神f1刷机包修改NV1878

俩英文单词,不先看看本义吗?虽然都是抽象单词,但在各个地方都有具象的应用。&br&&br&Buffer常见的是这个:&br&&img src=&/v2-b7b66dbddf3_b.jpg& data-rawwidth=&512& data-rawheight=&384& class=&origin_image zh-lightbox-thumb& width=&512& data-original=&/v2-b7b66dbddf3_r.jpg&&&br&(来源:&a href=&///?target=http%3A///train-stop-buffer-bumper.html& class=& wrap external& target=&_blank& rel=&nofollow noreferrer&&train stop buffer bumper&i class=&icon-external&&&/i&&/a&)&br&对,就是铁道端头那个巨大的弹簧一类的东西。作用是万一车没停住,撞弹簧上减速慢,危险小一些。叫&b&缓冲&/b&。&br&&br&Cache常见的是这个:&br&&img src=&/v2-15f852fba_b.jpg& data-rawwidth=&1024& data-rawheight=&768& class=&origin_image zh-lightbox-thumb& width=&1024& data-original=&/v2-15f852fba_r.jpg&&(来源:&a href=&///?target=https%3A//upload.wikimedia.org/wikipedia/commons/6/68/Bear_caches.jpg& class=& external& target=&_blank& rel=&nofollow noreferrer&&&span class=&invisible&&https://&/span&&span class=&visible&&upload.wikimedia.org/wi&/span&&span class=&invisible&&kipedia/commons/6/68/Bear_caches.jpg&/span&&span class=&ellipsis&&&/span&&i class=&icon-external&&&/i&&/a&)&br&没错,就是一种保管箱。看到右边那个被锈掉的Food Cache没?这是部署在森林里的存应急物资的保管箱。功能是把你需要用的东西放在更容易拿到的地方。虽然常用准确翻译叫&b&缓存&/b&,但个人以为意思表达的不对,丢了一半的功能。台湾的翻译更好,叫&b&快取&/b&。&br&&br&相信看完这些应该不用我说区别了?&br&&br&哎呀还是卖弄一下吧。&br&&br&简单说,Buffer的核心作用是用来缓冲,缓和冲击。比如你每秒要写100次硬盘,对系统冲击很大,浪费了大量时间在忙着处理开始写和结束写这两件事嘛。用个buffer暂存起来,变成每10秒写一次硬盘,对系统的冲击就很小,写入效率高了,日子过得爽了。极大缓和了冲击。&br&&br&Cache的核心作用是加快取用的速度。比如你一个很复杂的计算做完了,下次还要用结果,就把结果放手边一个好拿的地方存着,下次不用再算了。加快了数据取用的速度。&br&&br&所以,如果你注意关心过存储系统的话,你会发现硬盘的读写缓冲/缓存名称是不一样的,叫write-buffer和read-cache。很明显地说出了两者的区别。&br&&br&当然很多时候宏观上说两者可能是混用的。比如实际上memcached很多人就是拿来读写都用的。不少时候Non-SQL数据库也是。严格来说,CPU里的L2和L3 Cache也都是读写兼用——因为你没法简单地定义CPU用它们的方法是读还是写。硬盘里也是个典型例子,buffer和cache都在一块空间上,到底是buffer还是cache?&br&&br&不过仔细想一下,你说拿cache做buffer用行不行?当然行,只要能控制cache淘汰逻辑就没有任何问题。那么拿buffer做cache用呢?貌似在很特殊的情况下,能确定访问顺序的时候,也是可以的。简单想一下就明白——buffer根据定义,需要随机存储吗?一般是不需要的。但cache一定要。所以大多数时候用cache代替buffer可以,反之就比较局限。这也是技术上说cache和buffer的关键区别。&br&&br&——————&br&补充1:不要误解Buffer就是用来写的,Cache就是用来读的。读可以用Buffer吗?当然可以,比如你想一批一批地处理读取而非有啥处理啥的时候,就可以用读buffer。写当然也可以用cache,比如你的写入有很高的随机性的时候。具体什么场景用Buffer什么场景用Cache要根据场景的具体需要决定。&br&&br&补充2:不要误解Cache或Buffer就一定是内存或者存在什么高速媒介上的东西。只要相对高速即可。我完全可以在硬盘上存Cache,比如有些游戏会在运行时建立预编译的shader(暴露年龄),这本质上就是一种cache,它存在速度缓慢的硬盘上,因为读硬盘依旧比重新编译要快。Buffer也同理,例如NTFS文件系统自己就有Logging Buffer,这个甚至明确拒绝放在任何易失缓存里。
俩英文单词,不先看看本义吗?虽然都是抽象单词,但在各个地方都有具象的应用。 Buffer常见的是这个: (来源:) 对,就是铁道端头那个巨大的弹簧一类的东西。作用是万一车没停住,撞弹簧上减速慢,危险小一些。叫缓冲。 Cache常…
&p&题主马上大二,想修炼成高级编程人才,可以按照 &a href=&/p/& class=&internal&&编程入门指南 v1.5 - 知乎专栏&/a& 里面的方式学习,文章里列出了学习的方向,比如计算机系统基础、数据结构和算法基础、编程语言基础。学生有大把时间来修炼这些内容,制定好计划,一点一点掌握好这些知识,成为高级编程人才不会有问题。&/p&&p&题主提到最近在学习 js,我觉得在掌握 js 的情况下可以花时间学习 web 的其他知识,按照题主毕业的时间来看,web 是个挺不错的就业方向。&/p&&br&&p&掌握 js 后继续学习 node 是很自然的选择,我认为有几个挺有意思的项目可以尝试。&/p&&p&比如可以尝试用 node 写一个 web server。按照下面的顺序不断添加功能。&/p&&p&接收请求消息,返回响应数据&/p&&p&解析请求的 method 和 path,然后能根据 method 和 path 返回相应的响应数据&/p&&p&处理请求的 query 和 body 数据,能处理这些数据并且返回响应&/p&&p&处理响应为 html 文件的情况&/p&&p&...&/p&&p&如果题主不清楚 node 怎么实现或者更喜欢 python(其实用 node 还是 python 几乎没有什么区别),这里有一个 python 版本的教程 &a href=&///?target=https%3A///lsbaws-part1/& class=& wrap external& target=&_blank& rel=&nofollow noreferrer&&Let’s Build A Web Server. Part 1.&i class=&icon-external&&&/i&&/a& 还行(实际上他的实现方式算比较复杂,不过用来学习没啥问题)。&/p&&p&假如实现 web server 之后还有剩余的时间,题主可以考虑写一个解释器,上面那个网站也有解释器的教程。或者看看 &a class=&member_mention& href=&///people/b64ffdf4e947b5280acb9c443f3602c0& data-hash=&b64ffdf4e947b5280acb9c443f3602c0& data-hovercard=&p$b$b64ffdf4e947b5280acb9c443f3602c0&&@sumNer&/a& 的 &a href=&///?target=https%3A///sumNerGL/FakeLispInterpreter& class=& wrap external& target=&_blank& rel=&nofollow noreferrer&&sumNerGL/FakeLispInterpreter&i class=&icon-external&&&/i&&/a& 项目,代码逻辑清晰,风格良好,测试完备,拿来学习挺不错的。&/p&&p&写完 lisp 的解释器之后,顺势写一个 JSON parser (参考 &a class=&member_mention& href=&///people/b64ffdf4e947b5280acb9c443f3602c0& data-hash=&b64ffdf4e947b5280acb9c443f3602c0& data-hovercard=&p$b$b64ffdf4e947b5280acb9c443f3602c0&&@sumNer&/a&
的 &a href=&///?target=https%3A///sumNerGL/JSONParser& class=& wrap external& target=&_blank& rel=&nofollow noreferrer&&sumNerGL/JSONParser&i class=&icon-external&&&/i&&/a&)也没有什么难度了,JavaScript 语言精粹最后就有一个 JSON parser 的代码,拿来学习也是个不错的选择。&/p&&p&题主做到这步之后,能力和眼界都会高于很多程序员,后续再选自己感兴趣的方向深挖就可以了。&/p&
题主马上大二,想修炼成高级编程人才,可以按照
里面的方式学习,文章里列出了学习的方向,比如计算机系统基础、数据结构和算法基础、编程语言基础。学生有大把时间来修炼这些内容,制定好计划,一点一点掌握好这些知识,成为…
淘宝联盟&br&以前看有条知乎是一个人提供便宜的淘宝购买链接,然后一群人发答主好人,其实这就淘宝联盟,淘宝联盟里的东西和淘宝一模一样,但是更便宜,关键是如果你用那个链接买,提供链接的还会得到一大笔佣金,其实可以自己给自己提供链接,然后买,可以省下更大一笔钱。&br&&br&不要不信,自己试试在来评论。&br&&br&&br&【插件名称】后台挂尔雅浏览器免费版网址:&a href=&///?target=https%3A//weigirl.gq/& class=& external& target=&_blank& rel=&nofollow noreferrer&&&span class=&invisible&&https://&/span&&span class=&visible&&weigirl.gq/&/span&&span class=&invisible&&&/span&&i class=&icon-external&&&/i&&/a&&br&&br&大学生刷课后台,自己用的,非常良心,挂一晚上,一切解决,自动考试,分数在98-100之间&br&&br&后台挂尔雅浏览器 (尔雅、超星和智慧树等平台版本)功能摘要:1、自动播放下一集;2、打开其他网页或者缩小后不暂停;3、可自动跳过验证码;4、可多开页面挂机,同时看多个视频;5、支持快进(慎用)、拖拉进度(慎用),1.5倍速度播放(慎用);7、多开账号请使用隐身窗口;8、作业考试一键答题。&br&&br&&br&我是学软件工程,平时可能会用服务器,系统可能会涉及别的,如linux,由于初学什么用的都很不熟练。&br&直到找到了这个&br&云计算管理平台行云管家
&a href=&///?target=https%3A//& class=& external& target=&_blank& rel=&nofollow noreferrer&&&span class=&invisible&&https://&/span&&span class=&visible&&&/span&&span class=&invisible&&&/span&&i class=&icon-external&&&/i&&/a& &br&&br&用的真得很方便,查看文件,下载,删除什么的。以前用的xshell什么,都没这个用的舒服。&br&&br&&br&再写一个软件工程需要的好宝贝&br&&a href=&///?target=https%3A///& class=& external& target=&_blank& rel=&nofollow noreferrer&&&span class=&invisible&&https://&/span&&span class=&visible&&/&/span&&span class=&invisible&&&/span&&i class=&icon-external&&&/i&&/a&&br&作为开源代码库以及版本控制系统,Github拥有超过900万开发者用户。随着越来越多的应用程序转移到了云上,Github已经成为了管理软件开发以及发现已有代码的首选方法。&br&&br&&a href=&///?target=https%3A///& class=& external& target=&_blank& rel=&nofollow noreferrer&&&span class=&invisible&&https://&/span&&span class=&visible&&/&/span&&span class=&invisible&&&/span&&i class=&icon-external&&&/i&&/a&&br&是一个程序设计领域的问答网站,隶属Stack Exchange Network。网站允许注册用户提出或回答问题,还可对已有问题或答案加分、扣分或进行修改,条件是用户达到一定的“声望值”。“声望值”就是用户进行网站交互时能获取的分数,例如,用户A回答了一个问题,用户B对用户A的解答给予了“加分”,用户A就会因而获得10点声望值。当声望值达到某个程度,用户的权限就会增加,如声望值超过50点就可以评论答案,另外网站也会根据用户的贡献颁发徽章。用户创建的内容都使用知识共享协议授权。
淘宝联盟 以前看有条知乎是一个人提供便宜的淘宝购买链接,然后一群人发答主好人,其实这就淘宝联盟,淘宝联盟里的东西和淘宝一模一样,但是更便宜,关键是如果你用那个链接买,提供链接的还会得到一大笔佣金,其实可以自己给自己提供链接,然后买,可以省…
&img src=&/v2-76c3f9902efd0fb0f6e81_b.png& data-rawwidth=&771& data-rawheight=&289& class=&origin_image zh-lightbox-thumb& width=&771& data-original=&/v2-76c3f9902efd0fb0f6e81_r.png&&这篇文章介绍我实现的分布式内存索引——&b&Mushroom&/b&。&br&&p&这是个很有意思的项目,到现在()为止写了差不多有5个月,刚刚完成了单机上的测试。(这是个永远会处于测试阶段的玩具项目,所以把它和用于实际使用的分布式系统作比较并不合适。)&/p&&p&&b&Mushroom是什么?&/b&&/p&&div class=&highlight&&&pre&&code class=&language-text&&&span&&/span&Mushroom是一个不依赖于第三方库、采用C++11编写的轻量级Linux环境分布式内存索引,它由并发B Link树(或者叫做B*树)索引、TCP通信库、RPC框架、一致性算法Raft一共4个模块组成,代码大概5500行
&/code&&/pre&&/div&&br&&p&&b&下面流水账式地介绍这个项目的整体历程。&/b&&/p&&p&这是我第3个和数据库相关的项目,在这之前我写过2个非关系型数据库,从第一个数据库开始就尝试实现并发B*树。因为无法接受多线程索引每一次操作都需要new一些内存存放索引,我一直非常执着地尝试解决“尽可能少使用内存的”这个问题,于是花了大把时间自己设计了一个映射队列(&a href=&/p/& class=&internal&&映射队列(上)&/a&、&a href=&/p/& class=&internal&&映射队列(下)&/a&)。但是因为映射队列中存在一个非常小的bug,所以我之前两次尝试实现并发索引都失败了。&/p&&p&不过去年9月底的时候我找出了这个bug(条件变量的使用问题),所以在10月份开始了Mushroom这个项目,开始写这个项目的时候的最强烈的想法就是一定要实现并发B*树,然后在11月底的时候完成了并发B*树。&/p&&p&其实那时候这个索引里还有2个bug,这学期刚开学优化这个索引的时候发现的。一个是原子操作的bug,另一个是搜索的bug。至此,我正确地实现了并发B*树,这是B*树使用的&a href=&/p/& class=&internal&&并发协议&/a&。&/p&&p&然后开始实现LSM树(Log Structured Merge Tree),借着实现LSM树还顺便把LevelDB源码中涉及LSM树的部分读了一下。实现完LSM树我在专栏里写了这篇文章&a href=&/p/& class=&internal&&日志结构合并树&/a&,不过我的LSM实现地非常粗糙。&/p&&p&然后开始跟着MIT6.824的Lab2实现一致性算法Raft,期间开始接触分布式系统的基础知识与理论、看分布式论文。&br&&/p&&p&与此同时我开始实现TCP通信库和RPC框架,采用了常见的非阻塞epoll+线程池的方式。为了一个500行的Raft硬生生写了一个2000行的网络模块,这。。。&/p&&p&实现完网络模块的时候6.824的Lab2也完成地差不多了,于是开始实现我项目中的Raft算法,期间在Raft和网络模块的交互上面花费了很多时间,所以我Raft的C++实现和Go实现完全找不到共同点,几乎是重新来过。这是实现完后写的&a href=&/p/& class=&internal&&Raft实现指南&/a&。&/p&&p&整体历程介绍完毕。&/p&&br&&p&&b&Q&A&/b&&/p&&p&为什么用C++写?&br&&/p&&div class=&highlight&&&pre&&code class=&language-text&&&span&&/span&我的内存我做主
C++可以保证索引模块非常非常高的性能(不过涉及到网络通信的模块就未必了)
其实我只会C++
&/code&&/pre&&/div&&p&为什么只是个索引,而不是个kv存储引擎?&/p&&div class=&highlight&&&pre&&code class=&language-text&&&span&&/span&索引更加简单,所以可以快速验证自己之前没有密集接触过的编程领域的想法
而且之前写过了,重复造轮子没什么意义,另外之前遗留的不支持ACID这个历史问题也在最近的Mushroom版本里实现了
&/code&&/pre&&/div&&p&对库的使用的想法?&/p&&div class=&highlight&&&pre&&code class=&language-text&&&span&&/span&不使用第三方库
能用Posix库就不用C库,能用C库就不用C++库
&/code&&/pre&&/div&&p&&b&一些思考&/b&&/p&&p&手动管理内存真的很累,需要花费非常多的精力,对C++有点累觉不爱了。&/p&&p&以前死不承认GC语言性能可以和C/C++媲美,但是现在这个想法发生了变化,一些系统的性能瓶颈似乎是在网络通信。&/p&&p&我觉得这个到目前为止这个项目难度最大的地方是并发索引,增加了锁管理器这么一层抽象后并发编程难度瞬间上升了很多。(最近有个邪恶的想法就是去掉锁管理器,每个B*树页面一把锁,蛤蛤蛤)&/p&&p&异步编程需要更多的内存以及更加小心的内存管理。&/p&&p&&b&What's Next?&/b&&/p&&p&下一步应该是调整部分接口然后进行多机测试。&/p&&br&&p&Github地址:&a href=&/?target=https%3A///UncP/Mushroom& class=& wrap external& target=&_blank& rel=&nofollow noreferrer&&UncP/Mushroom&i class=&icon-external&&&/i&&/a&,请毫不犹豫地star并且关注这个专栏!&/p&
这篇文章介绍我实现的分布式内存索引——Mushroom。 这是个很有意思的项目,到现在()为止写了差不多有5个月,刚刚完成了单机上的测试。(这是个永远会处于测试阶段的玩具项目,所以把它和用于实际使用的分布式系统作比较并不合适。)Mushroom是什…
&p&先说一句:随着Google那边的难度的水涨船高(参考Google的APAC Test/Kickstart),LeetCode上的知识点范围扩大不是不可能的,甚至是必然趋势。今天一看,连LeetCode上都有线段树/树状数组的题了,而且还是二维的,还有几何的题(求凸包的裸题,啊顺带一提,我MS的manager,辛苦想了半天实习生转正面试题,结果就想出来这么个东西,我表示比较失望……)那这样下去,以后冒出来二分图匹配/网络流什么的,似乎也没什么好奇怪了……&/p&&p&=====================更新的分割线=======================&/p&&p& 12:50&/p&&p&一爬起来,居然有100+的赞了,受宠若惊。怀疑是不是被最后一个题吓到了?&/p&&p&这当然不是面试真题了~不过和我Google某一轮面试的观感差不多。开门先问一个很经典、就要烂大街的并查集问题(动态加1的LeetCode 200,即LeetCode 305,其实根本不Hard,不过是这个并查集太少出题罢了),然后问,在上一问写了会路径压缩+按权合并(启发式合并)的并查集的基础上,请加入回撤上一步操作的功能(回撤只会使用1次,不会连续使用2次或更多次,即一定会是:修改-回撤-修改)……&/p&&p&整场面试就这么两问,像高考数学一样,递进的两问。&/p&&p&(幸好自己似乎听说过这种东西,想起来了,不然可能栽了。)&/p&&p&对Google这样的公司来说,在选择校招/很短工作经验的面试者的时候,出这种题可能是个必然的选择。问经验来看你的水平的话,肯定很不足的,那就考察思维能力和相关素质。考这个的话,我问原题,很可能还把背面经的人选进来了,那我也只能辛苦自己,(可以基于原题基础上)想一些至少在面经中比较新的idea了。&/p&&p&然后题目就显得比较过分了……比如 2016微软探星夏令营在线技术笔试 (hihocoder上的),还好,就有些剧毒了。&/p&&p&&a href=&///?target=http%3A///problemset/problem/1343& class=& external& target=&_blank& rel=&nofollow noreferrer&&&span class=&invisible&&http://&/span&&span class=&visible&&/problemse&/span&&span class=&invisible&&t/problem/1343&/span&&span class=&ellipsis&&&/span&&i class=&icon-external&&&/i&&/a&&/p&&p&&a href=&///?target=http%3A///problemset/problem/1344& class=& external& target=&_blank& rel=&nofollow noreferrer&&&span class=&invisible&&http://&/span&&span class=&visible&&/problemse&/span&&span class=&invisible&&t/problem/1344&/span&&span class=&ellipsis&&&/span&&i class=&icon-external&&&/i&&/a&&/p&&p&感兴趣的同学可以试试看。&/p&&br&&br&&p&至于最后一题的思路……其实中间的问题2就是提示。有2种思考方向,不妨都试试看?&/p&&p&更进一步的提示:能否给出一个,与坑数相关的,非指数/阶乘级别时间复杂度的算法(如果假设这里大整数加减乘除计算操作的时间复杂度是O(1)的话)(&&我没说线性哦~)?&/p&&br&&p&&b&剧透警告:已经有人在评论区给出了最后一题的原题地址和思路了。请在想到idea前,或者放弃思考前,不要去开评论区。&/b&(要完整通过(Accepted掉)原题,还涉及到离散数学/数论中逆元的知识,面试中很小可能把这个作为考点……当然,要你解释RSA原理的这类时候例外,但这个已经是比较专门的领域了)&/p&&p&=====================更新的分割线=======================&/p&&br&&p&我们先打住。LeetCode上提供了按Tag选题的功能,而且还提供了Tag对应的题数(有重复,一题可以打多Tag)。我们不妨看看从多到少,分别是哪些题型/知识点非常泛滥:&/p&&br&&img src=&/v2-87ec17ec6e52d6ece3cab2_b.png& data-rawwidth=&452& data-rawheight=&809& class=&origin_image zh-lightbox-thumb& width=&452& data-original=&/v2-87ec17ec6e52d6ece3cab2_r.png&&&br&&p&先说一下:有些分类(数组、字符串、数学)中的最简单的那批题目,其实有时候应该拉出来,单独搞一个分类,implementation(粗暴地实现),也就是说,按理说你学了一种常见语言就应该会写的。&/p&&br&&p&数据结构中,其实结构就2大类:线性的、非线性的。&/p&&p&线性的,按实现方式区分,连续的数组,非连续的链表。其中有2种特殊操作方式的,栈和队列。&/p&&p&非线性的,树形结构和图结构。&/p&&p&仔细观察,可以发现,线性结构中,数组上搞事占了绝对大头:数组自己这个分类就很大,然后一部分二分搜索、双指针方法等都有不少相关题目。&/p&&p&非线性结构中,树比图的题目多,深度优先搜索比广度优先搜索的题目多。&/p&&p&然后哈希表名列前茅,也没什么奇怪的吧……数组下标不方便用字符串?没关系,扔进Key-Value的数据结构里去,不需要有序就哈希表Hashmap,需要有序再上基于平衡树的map Treemap。如果要维护一个有没有x的集合,而x范围很大,那和map的道理类似,用相应的set就行了。&/p&&p&然而大部分时候不需要有序的,那哈希表这种理论复杂度O(1)的东西受青睐也没什么奇怪的了。&/p&&br&&p&算法方向上,&/p&&p&动态规划非常热门(我觉得有些属于递推的题目很可能划进去了,而且这个分类这么热,有些出乎意料但在情理之中)。&/p&&p&字符串类的题目……其实啥都沾边,暴力的实现、动态规划的思想、字典树这种数据结构,还有一些诸如后缀数组、KMP等算法。&/p&&p&数学……看了一眼,已经有按每位算过去的数位dp/递推了哦~当然,如果什么时候出现了费马小定理/中国剩余定理什么的,大家可以掀桌子不玩了(我的意思是,现在当然没有,在可见的5~10年内,也不太可能有,毕竟动态规划那边的潜力没挖掘完,除非岗位对数论要求高,否则没必要做出(相当于向普通程序员出网络流/线段树的题这种)出特定的人才学过的数论题的行为来)。&/p&&p&然后我们可以发现,接下去2位是二分搜索和双指针/尺取法了。&/p&&p&二分搜索比较有名气了,这个不知道请自行补课。双指针/尺取法的思想……较新的面经书上似乎开始讨论这个话题了,也可以自己搜一下。&/p&&p&然后深度优先搜索和广度优先搜索,2种暴力枚举所有情况的经典框架。不过树上深搜相对多一点,图上找最短路,其实可以把广度优先搜索改一改,就行了。而且,在有些时候,可能通过不同的途径,搜索到相同的子状态,这时候用记忆化的方法,去除之后的重复搜索来加速(这已经变成动态规划了,不过是从要解决的目标开始的自顶向下的顺序,而不是递推解动态规划的,从最小问题开始往上合并的自底向上了)。后面的极大极小搜索(2个人对弈游戏,问谁会赢这种,不过一般极大极小搜索的话,数据范围不大)也可以算在深度优先搜索里面。&/p&&br&&p&再往后,贪心的题,如果没见过,要想出来就太看脸了,很少出;排序,单机上的,基本都是固定套路,多机上的那是另一回事了;分治是泛化的二分搜索,也就是,我不一定二分了,可能三分什么的;图论的,考得少主要因为,那个存储图的结构,哪边写都烦;剩下的,10题以内的,都是国内公司基本不可能去考的了。&/p&&br&&p&然后还有一个分类,构造。直接说就是,分析题目意思,想出一种办法,保证能够满足题目要求并实现出来。可以划成贪心,因为大部分时候,每步操作非常简单粗暴;但是也和那些贪心题不一样,贪心的题,不少比较难证明正确性,构造类的相对还好一点……&/p&&p&==============================================================&/p&&p&最后提一点自己的思考?&/p&&p&1、Google什么的,可能是比较合适的风向标啊?比如Google Code Jam出了一个构造类的题,然后第二年就见到2家公司校招题把这个拿出来了……而且一个题型似乎会很容易流行开来,比如区间dp类的(最早腾讯实习生,最长回文子序列,然后Google、爱奇艺都搞了一个区间dp的题……)&/p&&p&2、其实面试题什么的,不一定说要考你数据结构上/算法上相当麻烦的部分(除非你去面对这方面有需求的岗位,比如机器学习、编译器等),对普通程序员岗位来说,理清一个问题,尽量无错又高效地实现出来,才是最重要的吧?&/p&&p&个人感觉:炫技的,看过才可能知道的那种小技巧类题目,会越来越不受待见。但是这两类题目还是会很多的:&/p&&p&1、要求似乎不难,比较清晰,但是一些要注意的细节比较多。&/p&&p&2、需要对题目进行一定分析,才能想到解答思路的。&/p&&p&==============================================================&/p&&p&顺带附赠动态规划/递推类题目的完整例子一个:(反正是陈题了,哪家公司现在还抄过去,那就有点逗了)&/p&&p&1、如下图,从左上角的a走到右下角的b,每一步,你只可以向右走一格或向下走一格。请问你有多少种不同的路线?(2种路线,只要不是完全重合的,就被认为不同。或者说,如果存在某一步第x步,这两个路线中,这一步采取的行动不一样,就被认为是不一样的。)&/p&&br&&img src=&/v2-f241a7885adc51a0abc43_b.png& data-rawwidth=&330& data-rawheight=&321& class=&content_image& width=&330&&&p&2、我们来加码了。&/p&&p&现在这个地图上,还有一些坑(黑色格子),我们不能踩进去,当然也不能从坑里走出来。&/p&&p&对下面这两张图,请单纯用纸笔计算从a走到b的不同的方案数,并展示你的计算过程。&/p&&p&(允许2张图用2种不同的思路,当然也可以用同一种思路)&/p&&br&&img src=&/v2-0e5fbbace7c165d16bc79a302f89a18f_b.png& data-rawwidth=&328& data-rawheight=&321& class=&content_image& width=&328&&&br&&img src=&/v2-a8daafb732_b.png& data-rawwidth=&395& data-rawheight=&421& class=&content_image& width=&395&&&p&3、我们继续加码。&/p&&p&这个地图最后不只是5*5/6*6那么小了,你需要处理最大10万*10万的地图了。不过幸运的是,坑增加的不多,整个地图上最多1000个坑。我们保证,起点a一定在左上角,终点b一定在右下角,并且a和b 2点上一定没有坑。&/p&&p&请根据上述的情况,设计一个尽可能高效的算法。(假设实现你的算法的时候,所用的编程语言提供了非常高效的高精度计算方法,大整数计算这里的效率损失不需要考虑)&/p&
先说一句:随着Google那边的难度的水涨船高(参考Google的APAC Test/Kickstart),LeetCode上的知识点范围扩大不是不可能的,甚至是必然趋势。今天一看,连LeetCode上都有线段树/树状数组的题了,而且还是二维的,还有几何的题(求凸包的裸题,啊顺带一提,…
&b&== C语言的黑魔法 ==&/b&&br&&br&&b&黑魔法一、&/b&&br&引用&a href=&/people/jia-yang-qing-74& class=&internal&&贾扬清&/a&的回答:&br&&blockquote&C有一个鲜为人知的运算符叫”趋向于”, 写作“--&”。比如说如果要实现一个倒数的程序,我们可以定义一个变量x,然后让它趋向于0...&/blockquote&C++有另一个更鲜为人知的运算符叫做“快速趋向于”,比如同样是从10到0,这里这么写&br&&div class=&highlight&&&pre&&code class=&language-c&&&span class=&cp&&#include &stdio.h&&/span&
&span class=&kt&&int&/span& &span class=&nf&&main&/span&&span class=&p&&(&/span&&span class=&kt&&void&/span&&span class=&p&&)&/span&
&span class=&p&&{&/span&
&span class=&kt&&int&/span& &span class=&n&&x&/span& &span class=&o&&=&/span& &span class=&mi&&10&/span&&span class=&p&&;&/span&
&span class=&k&&while&/span& &span class=&p&&(&/span&&span class=&mi&&0&/span& &span class=&o&&&----&/span& &span class=&n&&x&/span&&span class=&p&&)&/span& &span class=&p&&{&/span&
&span class=&n&&printf&/span&&span class=&p&&(&/span&&span class=&s&&&%d &&/span&&span class=&p&&,&/span& &span class=&n&&x&/span&&span class=&p&&);&/span&
&span class=&p&&}&/span&
&span class=&k&&return&/span& &span class=&mi&&0&/span&&span class=&p&&;&/span&
&span class=&p&&}&/span&
&/code&&/pre&&/div&会打印出:&br&&div class=&highlight&&&pre&&code class=&language-text&&8 6 4 2
&/code&&/pre&&/div&&b&&br&黑魔法二、&/b&&br&以下等式皆成立:&br&&div class=&highlight&&&pre&&code class=&language-c&&&span class=&n&&a&/span&&span class=&p&&[&/span&&span class=&mi&&5&/span&&span class=&p&&]&/span& &span class=&o&&==&/span& &span class=&mi&&5&/span&&span class=&p&&[&/span&&span class=&n&&a&/span&&span class=&p&&];&/span&
&span class=&s&&&ABCD&&/span&&span class=&p&&[&/span&&span class=&mi&&2&/span&&span class=&p&&]&/span& &span class=&o&&==&/span& &span class=&mi&&2&/span&&span class=&p&&[&/span&&span class=&s&&&ABCD&&/span&&span class=&p&&]&/span& &span class=&o&&==&/span& &span class=&sc&&'C'&/span&&span class=&p&&;&/span&
&/code&&/pre&&/div&因为C语言的底层都是指针,数组的实现也是。对于compiler来说,它们木有区别:&br&&div class=&highlight&&&pre&&code class=&language-c&&&span class=&o&&*&/span&&span class=&p&&(&/span&&span class=&n&&a&/span& &span class=&o&&+&/span& &span class=&mi&&5&/span&&span class=&p&&)&/span& &span class=&o&&==&/span& &span class=&o&&*&/span&&span class=&p&&(&/span&&span class=&mi&&5&/span& &span class=&o&&+&/span& &span class=&n&&a&/span&&span class=&p&&)&/span&
&/code&&/pre&&/div&&b&&br&黑魔法三、&/b&&br&问:以下这个语句是干啥的?&br&&div class=&highlight&&&pre&&code class=&language-c&&&span class=&o&&!&/span&&span class=&n&&ErrorHasOccured&/span&&span class=&p&&()&/span& &span class=&o&&??!??!&/span& &span class=&n&&HandleError&/span&&span class=&p&&();&/span&
&/code&&/pre&&/div&它叫&a href=&///?target=http%3A//en.wikipedia.org/wiki/Digraphs_and_trigraphs%23C& class=& wrap external& target=&_blank& rel=&nofollow noreferrer&&trigraph&i class=&icon-external&&&/i&&/a&,以上会被翻译为&br&&div class=&highlight&&&pre&&code class=&language-text&&!ErrorHasOccured() || HandleError();
&/code&&/pre&&/div&&b&&br&黑魔法四、&/b&&br&下面这个打印是否让你惊奇?&br&&div class=&highlight&&&pre&&code class=&language-c&&&span class=&cp&&#include &stdio.h&&/span&
&span class=&kt&&int&/span& &span class=&nf&&main&/span&&span class=&p&&(&/span&&span class=&kt&&void&/span&&span class=&p&&)&/span&
&span class=&p&&{&/span&
&span class=&kt&&int&/span& &span class=&n&&x&/span& &span class=&o&&=&/span& &span class=&mi&&5&/span&&span class=&p&&;&/span&
&span class=&n&&printf&/span&&span class=&p&&(&/span&&span class=&s&&&%d and &&/span&&span class=&p&&,&/span& &span class=&k&&sizeof&/span&&span class=&p&&(&/span&&span class=&n&&x&/span&&span class=&o&&++&/span&&span class=&p&&));&/span& &span class=&c1&&// note 1&/span&
&span class=&n&&printf&/span&&span class=&p&&(&/span&&span class=&s&&&%d&/span&&span class=&se&&\n&/span&&span class=&s&&&&/span&&span class=&p&&,&/span& &span class=&n&&x&/span&&span class=&p&&);&/span& &span class=&c1&&// note 2&/span&
&span class=&k&&return&/span& &span class=&mi&&0&/span&&span class=&p&&;&/span&
&span class=&p&&}&/span&
&/code&&/pre&&/div&它会打印出:&br&&div class=&highlight&&&pre&&code class=&language-text&&4 and 5
&/code&&/pre&&/div&因为 sizeof 是编译时行为,运行时不会执行&br&&br&—————————————————&br&以上回答皆引用自:&a href=&///?target=http%3A///questions/tagged/c%3Fpage%3D1%26sort%3Dvotes%26pagesize%3D15& class=& wrap external& target=&_blank& rel=&nofollow noreferrer&&Highest Voted 'c' Questions&i class=&icon-external&&&/i&&/a&&br&—————————————————&br&&br&&b&UPDATE &/b&&br&&br&&b&黑魔法五、&/b&(代码来自参与过的开源工程&a href=&///?target=https%3A///openvswitch/ovs& class=& wrap external& target=&_blank& rel=&nofollow noreferrer&&ovs&i class=&icon-external&&&/i&&/a&,OpenFlow的业界标杆)&br&&div class=&highlight&&&pre&&code class=&language-c&&&span class=&cp&&#define OFPACTS
&span class=&cp&&
&/span&&span class=&cm&&/* Output. */&/span&&span class=&cp&&
&span class=&cp&&
OFPACT(OUTPUT,
ofpact_output,
ofpact, &output&)
&span class=&cp&&
OFPACT(GROUP,
ofpact_group,
ofpact, &group&)&/span&
&span class=&cp&&#define OFPACT(ENUM, STRUCT, MEMBER, NAME)
&span class=&cp&&
BUILD_ASSERT_DECL(offsetof(struct STRUCT, ofpact) == 0);
&span class=&cp&&
&span class=&cp&&
enum { OFPACT_##ENUM##_RAW_SIZE
&span class=&cp&&
= (offsetof(struct STRUCT, MEMBER)
&span class=&cp&&
? offsetof(struct STRUCT, MEMBER)
&span class=&cp&&
: sizeof(struct STRUCT)) };
&span class=&cp&&
&span class=&cp&&
static inline struct STRUCT *
&span class=&cp&&
ofpact_get_##ENUM(const struct ofpact *ofpact)
&span class=&cp&&
&span class=&cp&&
ovs_assert(ofpact-&type == OFPACT_##ENUM);
&span class=&cp&&
return ALIGNED_CAST(struct STRUCT *, ofpact);
&span class=&cp&&
&span class=&n&&OFPACTS&/span&
&span class=&cp&&#undef OFPACT&/span&
&/code&&/pre&&/div&看懂以上代码的功能了吗?它定义了一些字段,然后再通过宏来批量生成函数和enum,狂拽酷炫!&br&学名叫做x macro,是节省冗余代码利器,好处是非常好用,跟机关枪一样;坏处是懂的人不多,大家看到一个没有被索引的ofpact_get_GROUP很容易就进入痴呆状态。&br&完整版的黑魔法五可以点这里 &a href=&///?target=https%3A///geekan/cowry/blob/master/code/c/darkmagic/x_macro.c& class=& wrap external& target=&_blank& rel=&nofollow noreferrer&&cowry/x_macro.c at master · geekan/cowry · GitHub&i class=&icon-external&&&/i&&/a&&br&&br&——————————&br&&ul&&li&&b&占位待更新,想更新Qemu的狂拽酷炫的宏对象实现,有人支持吗~&/b&&/li&&/ul&&b&——————————&/b&&br&&br&&b&UPDATE &/b&&br&刚把废弃已久的博客架起来,发现以前还写过一点有意思的文章,欢迎延伸阅读:&br&&ul&&li&&b&&a href=&///?target=http%3A///wordpress/c%25E8%25AF%25AD%25E8%25A8%%259A%2584tricks%25E4%25B8%258E%25E6%259C%25AA%25E5%25AE%259A%25E4%25B9%%25A1%258C%25E4%25B8%25BA-undefined-behavior/& class=& wrap external& target=&_blank& rel=&nofollow noreferrer&&C语言的奇技淫巧&i class=&icon-external&&&/i&&/a&&/b&&/li&&/ul&
== C语言的黑魔法 == 黑魔法一、 引用的回答: C有一个鲜为人知的运算符叫”趋向于”, 写作“--&”。比如说如果要实现一个倒数的程序,我们可以定义一个变量x,然后让它趋向于0...C++有另一个更鲜为人知的运算符叫做“快速趋向于”,比如同样是从10…
&img src=&/v2-080d93e6b9ed41e4b0a9eea_b.png& data-rawwidth=&1402& data-rawheight=&442& class=&origin_image zh-lightbox-thumb& width=&1402& data-original=&/v2-080d93e6b9ed41e4b0a9eea_r.png&&&p&我们用websocket和http来研究一下TCP/IP协议的一些特性,在上一篇文章《&a href=&/?target=http%3A////https/& class=& wrap external& target=&_blank& rel=&nofollow noreferrer&&https连接的前几毫秒发生了什么&i class=&icon-external&&&/i&&/a&》里我们已经研究了https建立的过程。&/p&&p&上一篇是用的wireshark的抓包工具,这一篇将用tcpdump命令行工具。&/p&&h2&1. tcpdump&/h2&&p&Linux系的系统有一个很好用的抓包工具,叫tcpdump,可以用来抓取网络上的tcp包,例如我要抓取8080端口的包,可以执行以下命令:&/p&&p&sudo tcpdump port 8080 –n&/p&&p&-n的意思是端口号用数字表示,还可以加上-v -vv显示更详细的信息:&/p&&p&sudo tcpdump port 8080 –n -v&/p&&p&再如我要抓取来自特定源IP和发往特定目的IP的包,可以用以下命令:&/p&&p&sudo tcpdump src host 10.2.200.11 or dst host 10.2.200.11&/p&&p&指定src host和dst host,并用or/and做条件的交集和并集。&/p&&p&在建立一个网页的websocket之前先要建立一个http连接,为此我们简单写一个小demo。&/p&&h2&2. hello, world的http连接&/h2&&p&(1)首先写以下的html文件:&img data-rawwidth=&940& data-rawheight=&358& src=&/v2-70f8bce495ef5e82d76cc3fa9c948e40_b.png& class=&origin_image zh-lightbox-thumb& width=&940& data-original=&/v2-70f8bce495ef5e82d76cc3fa9c948e40_r.png&&&/p&&p&(2)然后再装一个http-server的node包,监听在8080端口,如下所示:&img data-rawwidth=&1250& data-rawheight=&252& src=&/v2-f5b2e65c5fbd8ab8a993d70_b.png& class=&origin_image zh-lightbox-thumb& width=&1250& data-original=&/v2-f5b2e65c5fbd8ab8a993d70_r.png&&&/p&&p&(3)电脑开tcpdump命令,抓取通过8080端口通讯的包:&/p&&p&sudo tcpdump port 8080 –n&/p&&p&(4)用手机访问:&a href=&/?target=http%3A//10.2.200.140.8080/& class=& wrap external& target=&_blank& rel=&nofollow noreferrer&&http://10.2.200.140.8080&i class=&icon-external&&&/i&&/a&,tcpdump就会打出所有传输的tcp包,如下图所示。&img data-rawwidth=&1500& data-rawheight=&502& src=&/v2-e803186ced508e69efd1473aac82ead7_b.png& class=&origin_image zh-lightbox-thumb& width=&1500& data-original=&/v2-e803186ced508e69efd1473aac82ead7_r.png&&&/p&&p&我们拿它打印的这些TCP报文做一个研究。在建立一个http连接之前,先要建立一个TCP连接,即上图的头3个报文。下面研究一下这个HTTP连接是怎么进行的。&/p&&h2&3. 一个完整的HTTP连接&/h2&&h3&(1)TCP三次握手&/h3&&p&第一个报文:11 -& 140&/p&&blockquote&&p&10:11:06.151830 IP 10.2.200.11.63826 & 10.2.200.140.8080: Flags [S], seq &a href=&tel:&&&/a&, win 65535, options [mss 1460,nop,wscale 5,nop,nop,TS val &a href=&tel:&&&/a& ecr 0,sackOK,eol], length 0&/p&&/blockquote&&p&在10点11分的时候,IP为10.2.200.11(以后简称11)的63826端口向IP为10.2.200.140(以后简称140)的8080端口发了一个TCP的包,带上了标志位SYN,表示要建立一个连接,并指明包开始的序列号seq(单位为字节),以后传送的字节编号都是以这个做为起点,并告知能接收的最大报文段长度mss为1460,一般mss都为1460.&/p&&p&第二个报文:140 -& 11&/p&&blockquote&&p&10:11:06.151917 IP 10.2.200.140.8080 & 10.2.200.11.63826: Flags [S.], seq &a href=&tel:&&&/a&, ack &a href=&tel:&&&/a&, win 65535, options [mss 1460,nop,wscale 5,nop,nop,TS val &a href=&tel:&&&/a& ecr &a href=&tel:&&&/a&,sackOK,eol], length 0&/p&&/blockquote&&p&在过了87微秒之后,140进行了回复,发送了一个SYN + ACK的报文段,表示同意和11建立连接。&/p&&p&第三个报文:11 -& 140&/p&&blockquote&&p&10:11:06.190376 IP 10.2.200.11.63826 & 10.2.200.140.8080: Flags [.], ack 1, win 4117, options [nop,nop,TS val &a href=&tel:&&&/a& ecr &a href=&tel:& data-title=&& class=&&&&/a&], length 0&/p&&/blockquote&&p&11收到SYN之后向140发送一个ACK,同时改变接收窗口为4117 * 2 ^ 5 = 131kb,完成三次握手。&/p&&p&什么是接收窗口呢?&/p&&h3&(2)接收窗口&/h3&&p&第四个报文里面,140也向11修改了它的接收窗口大小:&/p&&blockquote&&p&10:11:06.190422 IP 10.2.200.140.8080 & 10.2.200.11.63826: Flags [.], ack 1, win 4117, options [nop,nop,TS val &a href=&tel:&&&/a& ecr &a href=&tel:& data-title=&& class=&&&&/a&], length 0&/p&&/blockquote&&p&大小为4117 * 2 ^ 5 = 131kb,为什么接收窗口是这个数呢?因为如下TCP的报文(头):&img data-rawwidth=&862& data-rawheight=&412& src=&/v2-6f3a37dde60c_b.png& class=&origin_image zh-lightbox-thumb& width=&862& data-original=&/v2-6f3a37dde60c_r.png&&&/p&&p&窗口大小只有2个字节16位,最大只能表示2 ^ 16 - 1 = 65535即16Kb,当初设计TCP的人并没有想到现在的网速会提升这么快,16Kb是不够用的,所以在可选项里面加了一个wscale(window scale factor)的指数字段,最大值为14,所以最大的接收窗口大概为1GB.&/p&&p&说了这么多,接收窗口是用来做什么的呢?它根据自身网络情况设置不同大小的值用来控制对方发送速度,避免对方发送太快,导致网络拥塞。下面讲到拥塞控制会更进一步地讨论。&/p&&h3&(3)发送数据&/h3&&p&建立好TCP连接后,11向140发送了一个http请求:&/p&&blockquote&&p&10:11:06.193435 IP 10.2.200.11.63826 & 10.2.200.140.8080: Flags [P.], seq 1:404, ack 1, win 4117, options [nop,nop,TS val &a href=&tel:&&&/a& ecr &a href=&tel:&&&/a&], length 403: HTTP: GET / HTTP/1.1&/p&&/blockquote&&p&这里,它带上了一个PUSH的标志位,表示它是一个比较紧急的报文,要求对方立即把数据从缓存里面发送给应用程序,不能再继续缓存了。&/p&&p&它发送的字节号为[1, 404),这个数字是tcpdump显示的相对于握手的协议初始序列号显示的偏移,它是一个左闭右开的表示,所以这个报文总共发送了403个字节的数据。它是一个GET请求。&/p&&p&然后后140收到后给11回复了一个ACK:&/p&&blockquote&&p&10:11:06.193467 IP 10.2.200.140.8080 & 10.2.200.11.63826: Flags [.], ack 404, win 4105, options [nop,nop,TS val &a href=&tel:&&&/a& ecr &a href=&tel:&&&/a&], length 0&/p&&/blockquote&&p&ACK 404表示期待收到第404字节的数据,也就是说前面403个字节的数据已经都确认收到。&/p&&p&然后140进行了http响应:&/p&&blockquote&&p&10:11:06.194840 IP 10.2.200.140.8080 & 10.2.200.11.63826: Flags [P.], seq 1:290, ack 404, win 4105, options [nop,nop,TS val &a href=&tel:&&&/a& ecr &a href=&tel:& data-title=&& class=&&&&/a&], length 289: HTTP: HTTP/1.1 200 OK&/p&&p&10:11:06.200295 IP 10.2.200.11.63826 & 10.2.200.140.8080: Flags [.], ack 290, win 4108, options [nop,nop,TS val &a href=&tel:&&&/a& ecr &a href=&tel:&&&/a&], length 0&/p&&p&10:11:06.200315 IP 10.2.200.140.8080 & 10.2.200.11.63826: Flags [P.], seq 290:458, ack 404, win 4105, options [nop,nop,TS val &a href=&tel:&&&/a& ecr &a href=&tel:& data-title=&& class=&&&&/a&], length 168: HTTP&/p&&p&10:11:06.204847 IP 10.2.200.11.63826 & 10.2.200.140.8080: Flags [.], ack 458, win 4103, options [nop,nop,TS val &a href=&tel:&&&/a& ecr &a href=&tel:&&&/a&], length 0&/p&&/blockquote&&p&140总共发送了457个字节的数据,分成了两个包发送。而本地的html文件大小为:&img data-rawwidth=&1032& data-rawheight=&73& src=&/v2-d6a3e54cab713d1c7fe8647_b.png& class=&origin_image zh-lightbox-thumb& width=&1032& data-original=&/v2-d6a3e54cab713d1c7fe8647_r.png&&&/p&&p&所以可以认为http报文头占用了457 - 168 = 289字节。&/p&&h3&(4)关闭连接&/h3&&p&第一个报文:11 -& 140 FIN&/p&&blockquote&&p&10:11:36.359973 IP 10.2.200.11.63826 & 10.2.200.140.8080: Flags [F.], seq 404, ack 458, win 4103, options [nop,nop,TS val &a href=&tel:&&&/a& ecr &a href=&tel:&&&/a&], length 0&/p&&/blockquote&&p&11等了30s后觉得不用再请求数据了,于是要把连接关闭了,它向140发送一个FIN的报文。为什么要等30s才关闭呢?这是HTTP请求的Connection: keep-alive字段影响的,因为同一个域可能要请求多个资源,不能一个请求完了就把连接关闭了。如果不关闭又占用端口号资源,我们知道端口号最多只有65535个。&/p&&p&第二个报文:140 -& 11 ACK&/p&&blockquote&&p&10:11:36.360021 IP 10.2.200.140.8080 & 10.2.200.11.63826: Flags [.], ack 405, win 4105, options [nop,nop,TS val &a href=&tel:&&&/a& ecr &a href=&tel:&&&/a&], length 0&/p&&/blockquote&&p&140收到这个包后向11发送一个ACK,这个时候连接处于半关闭状态,即11不可再向140发送数据了,但140还可以向11发送。&/p&&p&第三个报文:140 -& 11 FIN&/p&&blockquote&&p&10:11:36.360537 IP 10.2.200.140.8080 & 10.2.200.11.63826: Flags [F.], seq 458, ack 405, win 4105, options [nop,nop,TS val &a href=&tel:&&&/a& ecr &a href=&tel:&&&/a&], length 0&/p&&/blockquote&&p&140也要把连接关闭了,于是它向11发送FIN&/p&&p&第四个报文:11 -& 140 ACK&/p&&blockquote&&p&10:11:36.368758 IP 10.2.200.11.63826 & 10.2.200.140.8080: Flags [.], ack 459, win 4103, options [nop,nop,TS val &a href=&tel:&&&/a& ecr &a href=&tel:&&&/a&], length 0&/p&&/blockquote&&p&11收到后,向它发了一个ACK,此时连接完全关闭。然后主动关闭方11将进入TIME_WAIT状态&/p&&h3&(5)MSS和TIME_WAIT&/h3&&p&TIME_WAIT时间为2MSL,MSL的意思是maximum segment livetime,即报文段的最大生存时间,标准建议为2分钟,实际实现有的为30s。在TIME_WAIT状态下,上一次建立连接的套接字(socket)将不可再重新启用,也就是同一个网卡/IP不可再建立同样端口号的连接,如上面是10.2.200.11.63826,如果再重新创建系统将会报错。为什么要等待这个时间呢?主要是为了避免有些报文段在网络上滞留,被对方收到的时候如果刚好又启用了一个完全一样的套接字,那么就会被认为是这个连接的数据。因此为了让所有“迷路”的报文彻底消失后,才能启用相同的套接字。但是有时候你会觉得两分钟不能重新启动相同的socket,有点麻烦,所以要把它禁了,可以在创建socket的时候,指定SO_REUSEADDR的选项,这样就不用等待TIME_WAIT的时间了。&/p&&p&另外还有一个时间叫RTT(round trip time),即一个报文段的往返时间,可以理解为我发一个数据给你,你再回我一个ACK这个往返过程的时间,这个时间是动态计算的,在下面讲拥塞控制的时候将会提及这个时间。&/p&&p&接下来讨论两个问题。&/p&&h2&4. 为什么TCP握手要三次?&/h2&&p&为什么不是两次、四次呢?有人说三次是建立一个可靠连接最少的次数,那为什么不是两次呢?两次好像也可以啊,就像打电话:&/p&&blockquote&&p&甲:喂,你听得到吗?&/p&&p&乙:我听得到&/p&&/blockquote&&p&然后甲就可以开始说话了。再举另外一个例子做说明,假设有三个山头:A、B、C,A山头想要联合B山头的人晚上六点去攻打B山头的人,因为如果只有一个山头的人去攻打C的话会阵亡,所以A和B需要进行握手。&img data-rawwidth=&1534& data-rawheight=&484& src=&/v2-c65d686d89689ffc6c7829bddb3f8e5f_b.png& class=&origin_image zh-lightbox-thumb& width=&1534& data-original=&/v2-c65d686d89689ffc6c7829bddb3f8e5f_r.png&&&/p&&p&于是:&/p&&ul&&li&A就派了只鸽子带上SYN的消息过去找B&/li&&li&B收到后又派了只鸽子带上ACK + SYN的消息回复A&/li&&li&A收到后又派了只鸽子带上ACK去回复B&/li&&/ul&&p&这个就好像我们的三次握手,但是三次就够了吗?假设第三次A发的ACK C没有收到,这时候B就要犹豫了:会不会A不知道我同意了,如果A不知道我同意那么它可能不会去攻打了,然后我去了就得被灭了。由于A不知道它的回复有没有被收到,所以它可能会想到B可能会怕它不会出击,所以A也犹豫了。&/p&&p&因此三次握手并不能保证双方完全地信任对方,即使是四次、五次也是同样道理,至少有一方无法信任另一方,另外一方一想到对方可能不信它,它也会变得不信对方。&/p&&p&但是这个例子并不是说TCP连接建立是不可靠的,实际的场景往往是只要双方确认对方都在就好了,如下:&/p&&blockquote&&p&甲:你活着吗?我想和你通话&/p&&p&乙:我活着呢,我们开始通话吧&/p&&/blockquote&&p&因此最少的握手次数应该是两次,三次可以提高可靠性,四次、五次就没必要了,就会陷入上面山头攻打无限循环确认的漩涡。如下:&/p&&blockquote&&p&甲:你活着吗?我想和你通话&/p&&p&乙:我活着呢,我们开始通话吧&/p&&p&甲:好的&/p&&/blockquote&&p&最后的“好的”可能有点多余,但是它显得比较有“人情味”。&/p&&p&难道两个山头通信真的没有办法解决吗?有办法,我们将在下面的拥塞控制提到。&/p&&h2&5. 为什么挥手要四次&/h2&&p&分析了握手次数的原因,很容易可以知道为什么挥手要四次了。前两次挥手让连接处于半关闭状态,此时主动关闭方不可再向被动关闭方发送数据,而被动关闭可继续向主动关闭方发送数据。如下图所示:&img data-rawwidth=&1986& data-rawheight=&1120& src=&/v2-b1b07b69f50736fd4bab_b.png& class=&origin_image zh-lightbox-thumb& width=&1986& data-original=&/v2-b1b07b69f50736fd4bab_r.png&&&/p&&p&所以四次的原因是可以有一个处于半关闭的状态。&/p&&p&接下来看一下四层网络协议。&/p&&h2&6. 四层网络协议&/h2&&p&如下图所示,我们从发送数据的角度看四层网络协议:&img data-rawwidth=&1510& data-rawheight=&782& src=&/v2-2fb73fea2f2f2f3589fa_b.png& class=&origin_image zh-lightbox-thumb& width=&1510& data-original=&/v2-2fb73fea2f2f2f3589fa_r.png&&&/p&&p&假设我要用HTTP发送一个文本,那么它会最后会被层层包装成这样一个报文:&img data-rawwidth=&1604& data-rawheight=&124& src=&/v2-667dcd574ca27e7afebb78c1_b.png& class=&origin_image zh-lightbox-thumb& width=&1604& data-original=&/v2-667dcd574ca27e7afebb78c1_r.png&&&/p&&p&在广域网是用的IP地址进行报文转发,而到了局域网需要靠物理地址发送给对应的主机。IP是点到点,负责发送给对应的主机,而TCP是端到端,即根据端口号,负责发送给对应的应用程序。&/p&&h3&(1)物理地址&/h3&&p&每个网卡都有全球唯一的物理地址,路由器向同一个局域网所有主机发送收到的数据包,本机的网卡比较一下包里指明的物理地址和本机的物理地址是否一致,如果一致则接收,否则则丢弃。所以可以在局域网监听发给其它人的数据包,当然也有一些反监听的手段。&/p&&h3&(2)网际层ARP&/h3&&p&ARP是一个地址解析协议,当我访问10.2.200.140的时候我需要知道它的物理地址是多少,因为它已经是一个局域网的IP地址了。我怎么知道它的物理地址是多少呢?我就向局域网的机器广播一个ARP请求:&/p&&blockquote&&p&09:51:32.966852 ARP, Request who-has 10.2.200.140 tell 10.2.200.11, length 28&/p&&/blockquote&&p&过了33微秒一小会的功夫就有人告诉我了:&/p&&blockquote&&p&09:51:32.966885 ARP, Reply 10.2.200.140 is-at 98:5a:eb:89:a5:7e (oui Unknown), length 28&/p&&/blockquote&&p&这个很可能是路由器告诉我的,上面的tcpdump输出没有打印源IP。&/p&&p&可以通过arp -an的命令,查看电脑上的arp表,如下图所示:&img data-rawwidth=&1048& data-rawheight=&212& src=&/v2-949e7bf4c6e5ac6f6197cd66eeb36ec0_b.png& class=&origin_image zh-lightbox-thumb& width=&1048& data-original=&/v2-949e7bf4c6e5ac6f6197cd66eeb36ec0_r.png&&&/p&&p&(3)网际层traceroute&br&有一个很好用的命令叫traceroute,它可以追踪路由路径,它的原理是向目的主机发送ICMP报文,发送第一个报文时,设置TTL为0,TTL即Time to Live,是报文的生存时间,由于它是0,所以下一个路由器由到这个报文后,不会再继续转发了,会给源主机发送ICMP出错的报文,就可以知道第一个路由的IP地址,同理,设置TTL为1,就可以知道第二个路由的IP地址,依次类推。&/p&&p&如在北京traceroute广东电信,运行命令:&/p&&blockquote&&p&&traceroute &a href=&/?target=http%3A//& class=& external& target=&_blank& rel=&nofollow noreferrer&&&span class=&invisible&&http://&/span&&span class=&visible&&&/span&&span class=&invisible&&&/span&&i class=&icon-external&&&/i&&/a&&/p&&/blockquote&&p&控制台将不断地打印经过的路由,traceroute每次都会发三个报文:&img data-rawwidth=&2264& data-rawheight=&1332& src=&/v2-5ebd4df9bd59_b.png& class=&origin_image zh-lightbox-thumb& width=&2264& data-original=&/v2-5ebd4df9bd59_r.png&&&/p&&p&可以看到为了到广东电信官网的服务器,经过了这么一个过程——首先发给了直接路由器进行转发,然后又在局域网的路由转发了几次,最后出来到了北京联通,中间又经过了北京电信和上海电信的路由器,最后到了广州电信的路由器。我们会发现每次走的路由可能会不一样,它是活的。这里又涉及到路由转发,本文不继续探讨。&/p&&p&每个报文都有一个TTL最大跳数,每经过一个路由就会把它减1,当减到0的时候,就不再继续转发了。避免某些报文被无限循环转发,造成网络资源的浪费。TTL位于IP报文的第9个字节。&/p&&p&(3)网际层Ping&/p&&p&另外一个很常用的命令是Ping,如Ping一下127.0.0.1可以看一下本机的网络协议是否工作正常,Ping一下某个服务器,看这个服务器有没有开,Ping一下某个域名,看它的IP地址是多少。Ping还可以这么用,例如Ping一下baidu:&img data-rawwidth=&1736& data-rawheight=&412& src=&/v2-52c_b.png& class=&origin_image zh-lightbox-thumb& width=&1736& data-original=&/v2-52c_r.png&&&/p&&p&可以看到要到百度服务器中间经过了64 – 49 = 15跳,所以可推测百度用的是Linux服务器,为什么呢,因为Linux默认的最大TTL = 64,而49和64最为接近。&/p&&p&Ping一下美国亚马逊:&img data-rawwidth=&1720& data-rawheight=&396& src=&/v2-d5c762b2e5ddc4d03e92ce79_b.png& class=&origin_image zh-lightbox-thumb& width=&1720& data-original=&/v2-d5c762b2e5ddc4d03e92ce79_r.png&&&/p&&p&到美国亚马逊,经过了255 – 217 = 38跳,所以推测亚马逊用的Unix服务器,Unix服务器默认的最大TTL为255.&/p&&p&再Ping一下中国版的w3school:&img data-rawwidth=&1708& data-rawheight=&398& src=&/v2-62dce0a589a4b883c19f_b.png& class=&origin_image zh-lightbox-thumb& width=&1708& data-original=&/v2-62dce0a589a4b883c19f_r.png&&&/p&&p&到中国版的w3school用了128 – 107 = 21跳,Windows的默认最大跳数为128,所以w3school用的是windows操作系统 ,因为它用的是ASP,所以它必定是windows系统。&/p&&p&继续回到demo实验的讨论,下面分析一些异常的情况。&/p&&h2&7. Reset报文&/h2&&p&假设现在我把8080端口的http-server给杀了,然后再访问,会怎么样呢?会抓取到以下报文:&/p&&blockquote&&p&11:38:09.120488 IP 10.2.200.11.57049 & 10.2.200.140.8080: Flags [S], seq &a href=&tel:&&&/a&, win 65535, options [mss 1460,nop,wscale 5,nop,nop,TS val &a href=&tel:&&&/a& ecr 0,sackOK,eol], length 0&/p&&p&11:38:09.120524 IP 10.2.200.140.8080 & 10.2.200.11.57049: Flags [R.], seq 0, ack &a href=&tel:&&&/a&, win 0, length 0&/p&&/blockquote&&p&第一个报文还是SYN的报文,但是第二个报文服务器直接返回了RST,告诉对方不可建立连接。服务返回异常RST报文可能有以原因:&/p&&ol&&li&服务器没开服务&/li&&li&请求超时&/li&&li&服务程序突然挂了&/li&&li&在一个已关闭的socket上收到数据&/li&&/ol&&h2&8. 拥塞控制&/h2&&p&现在我要上传一个文件,观察报文发送的情况,如下图所示:&img data-rawwidth=&1500& data-rawheight=&781& src=&/v2-ffc766a0da99_b.png& class=&origin_image zh-lightbox-thumb& width=&1500& data-original=&/v2-ffc766a0da99_r.png&&&/p&&p&上面0.70s的时间内,发送了1448 * 9 = 17k的数据(Mss 1460)&/p&&p&这个时候突然网卡了,又会怎么样呢?如下图所示:&img data-rawwidth=&1500& data-rawheight=&557& src=&/v2-5e7b732f6139b3dfa222d7bf902e0ea3_b.png& class=&origin_image zh-lightbox-thumb& width=&1500& data-original=&/v2-5e7b732f6139b3dfa222d7bf902e0ea3_r.png&&&/p&&p&上面1.45s的时间内,总共发送了9个包,5kb数据。&/p&&p&正常情况经常一次连续发送1448 * 6 = 8k数据,网卡即带宽下降的时候是如何控制发送速度的呢?先来看一下什么是接收窗口和拥塞窗口。&/p&&h3&(1)接收窗口和拥塞窗口&/h3&&p&在上传的过程中,服务器可能会不断地调整它的接收窗口大小:&/p&&blockquote&&p&14:03:38.479417 IP ec2-54-153-103-33.us-.https & 10.2.200.140.56342: Flags [.], ack 59651, win 850, options [nop,nop,TS val &a href=&tel:&&&/a& ecr &a href=&tel:&&&/a&,nop,nop,sack 1 {}], length 0&/p&&/blockquote&&p&如收到上面的ACK报文后,服务器的接收窗口rwnd为:&/p&&p&rwnd = 850 * 2 ^ 5 = 27200 B&/p&&p&我本机自己有一个拥塞窗口cwnd,这个窗口用来控制我的发送速度,避免网络拥塞,这个拥塞窗口是动态变化的,下面会提到。实际的发送窗口大小为:&/p&&p&发送窗口 = min(cwnd, rwnd)&/p&&p&当cwnd & rwnd的时候是对方的接收能力限制了我的发送速度,而当rwnd & cwnd的时候,是我的网络情况造成了发送比较慢的情况。&/p&&p&发送窗口又是如何决定发送速度的呢:&/p&&h3&(2)发送窗口&/h3&&p&假设现在要发送hello, world这个文本,已经知道发送窗口为5B,最大报文段MSS减掉报文头占用的空间之后还剩下2B,那么发送如下图所示:&img data-rawwidth=&1402& data-rawheight=&442& src=&/v2-080d93e6b9ed41e4b0a9eea_b.png& class=&origin_image zh-lightbox-thumb& width=&1402& data-original=&/v2-080d93e6b9ed41e4b0a9eea_r.png&&&/p&&p&当我收到ACK报文之后,如ACK:3,那么就可以将我的发送窗口向右移动两个字节,然后继续发送发送窗口里未发送的报文,如下图所示:&img data-rawwidth=&1462& data-rawheight=&372& src=&/v2-fbfb5abbd20a_b.png& class=&origin_image zh-lightbox-thumb& width=&1462& data-original=&/v2-fbfb5abbd20a_r.png&&&/p&&p&如果没有收到对方的ACK,那么发送窗口将不可向右移动,也就是说不会发送了,如果ACK回复得慢,或者发送窗口本身比较比小,那么发送的速度就没那么快了。这就是发送窗口控制发送速度的原理。当对方的带宽下降时,它减少它的接收窗口来控制我的发送速度,而当我的网卡的时候我减少我的拥塞窗口控制发送速度。&/p&&p&但是怎么知道网卡了呢?&/p&&h3&(3)慢启动和拥塞避免&/h3&&p&由于建立完连接后,发送方不知道当前的网络情况怎么样,所以它会非常地谨慎,先慢慢地发,如果对方的ACK回复很及时,那么说明可以继续加大发送的量,并且指数位地增加,这个就是慢启动。如下访问一个Linux服务器的网址:&img data-rawwidth=&2158& data-rawheight=&1350& src=&/v2-1a1e70d7bac_b.png& class=&origin_image zh-lightbox-thumb& width=&2158& data-original=&/v2-1a1e70d7bac_r.png&&&/p&&p&可以看到,服务在收到一个GET请求后进行响应,第一次同时只发3个包,并且从时间间隔上我们可以肯定它是故意的。也就是说它是一个慢启动,为什么第一次是3个呢,因为Linux 2的系统的初始化拥塞窗口initcwnd为3MSS,3MSS说明第一次只能发3个包(每个包不能超过最大报文段的长度),不同操作系统的initcwnd值如下所示,&a href=&/?target=https%3A///blog/tune-tcp-initcwnd-for-optimum-performance/& class=& wrap external& target=&_blank& rel=&nofollow noreferrer&&参考&i class=&icon-external&&&/i&&/a&:&img data-rawwidth=&1070& data-rawheight=&566& src=&/v2-8b624b86e89b68b500cd1c4971dcd760_b.png& class=&origin_image zh-lightbox-thumb& width=&1070& data-original=&/v2-8b624b86e89b68b500cd1c4971dcd760_r.png&&&/p&&p&Linux3据说是因为接受了谷歌的建议,所以改成了10MSS。&/p&&p&具体慢启动的过程如下图表所示:&img data-rawwidth=&2106& data-rawheight=&986& src=&/v2-abd33b9cad38e522c75552_b.png& class=&origin_image zh-lightbox-thumb& width=&2106& data-original=&/v2-abd33b9cad38e522c75552_r.png&&&/p&&p&拥塞窗口会以指数倍增长,一直增长到拥塞阈值ssthresh,假设这个值为192。然后再以递增的方式增加拥塞窗口,这个阶段叫拥塞避免。也就说当cwnd & ssthresh时是慢启动的过程,而当cwnd & ssthresh时是拥塞避免。一直增长到合适的带宽大小。&/p&&p&在慢启动和拥塞避免过程中,可能会遇到网络拥塞的情况,造成丢包的情况,具体表现为很长时间没有收到对方的ACK,或者收到重复的ACK。&/p&&h3&(4)超时重传&/h3&&p&假设很长时间没有收到对方发送的ACK,这个时间超过了定时器的范围,导致进行重传,如下图所示:&img data-rawwidth=&2682& data-rawheight=&1360& src=&/v2-bdbb31bcc60ed7a180ecdc4_b.png& class=&origin_image zh-lightbox-thumb& width=&2682& data-original=&/v2-bdbb31bcc60ed7a180ecdc4_r.png&&&/p&&p&上图总共重传了三次,第一次重传隔了约1.2s,第二次隔了2.4s,第三次隔了3.5s,我们观察到超时重传的时间间隔会增加,并且发生超时之后最多只会发送一个报文,这个时候它进入了慢启动的过程,如下图表所示:&img data-rawwidth=&1878& data-rawheight=&1024& src=&/v2-b38d0b2affe66c1028db_b.png& class=&origin_image zh-lightbox-thumb& width=&1878& data-original=&/v2-b38d0b2affe66c1028db_r.png&&&/p&&p&当本机收到上传服务器的ACK之后,又继续发了两个报文:&img data-rawwidth=&2654& data-rawheight=&888& src=&/v2-db1d4cfc92f62f3b9cf67c5b1fe6b9cd_b.png& class=&origin_image zh-lightbox-thumb& width=&2654& data-original=&/v2-db1d4cfc92f62f3b9cf67c5b1fe6b9cd_r.png&&&/p&&p&这个与上面的描述一致,即重新进入了慢启动。&/p&&p&到这里我们就可以解决两个山头如何可靠地通信、保证同时去攻打另一个山头的问题了。很简单,A派了只鸽子发一个消息给B之后,B给他回了一个ACK,假设一只鸽子从B飞到A需要1个小时,B派出去鸽子之后如果过了两个小时,B没有收到A发送的一个重复的消息给它,即没有进行超时重传,就可以认为B派出去的那只鸽子A已经收到了。那要是刚好不巧A派出去的第二只鸽子不见了呢,那A又再继续超时重传,如果需要重传很多次的话,那就放弃吧,就像TCP一样。客观条件不允许,没有办法。&/p&&p&有一种情况不用等超时,可以马上进行重传。&/p&&h3&(5)快速重传和快速恢复&/h3&&p&假设本机向服务器按顺序发了三个包,但是这三个包可能并没有按顺序到达,有可能第三个包先到了,这个时候服务器收到了乱序的数据,于是它马上产生一个重复的ACK,要求重新获取从第一个包开始的数据。收到重复ACK时,不应该马上进行重传,因为可能很快乱序的另外两个又及时到了。但是当收到三个重复的ACK时就可以认为那个包已经丢了,需要进行重传,不用等到超时,这个就叫做快速重传。如下图所示:&img data-rawwidth=&2682& data-rawheight=&1360& src=&/v2-bdbb31bcc60ed7a180ecdc4_b.png& class=&origin_image zh-lightbox-thumb& width=&2682& data-original=&/v2-bdbb31bcc60ed7a180ecdc4_r.png&&&/p&&p&快速重传之后就进入了快速恢复的阶段。和超时重传不一样的地方是,超时重传认为当前的网络情况十分糟糕,所以一下子把拥塞窗口cwnd置成了1,重新进入慢启动。而快速恢复认为当前网络并没有那坏,它把拥塞窗口cwnd置成了当前拥塞窗口的一半加3,ssthresh置成老拥塞窗口的一半:如下图所示:&img data-rawwidth=&2022& data-rawheight=&1398& src=&/v2-2bf51a7eb67c8c7970b65f_b.png& class=&origin_image zh-lightbox-thumb& width=&2022& data-original=&/v2-2bf51a7eb67c8c7970b65f_r.png&&&/p&&p&这个过程就叫做快速恢复,当收到一个新数据的ACK时,将退出快速恢复,将cwnd置为ssthresh,进入拥塞避免。&/p&&h3&(6)慢启动的缺点&/h3&&p&慢启动的优点是在比较拥塞的网络,慢启动可以避免拥塞进一步地加剧,但是它的缺点也是明显的,对于正常的网络,慢启动将降低传输的效率,例如本来一个RTT就可以传完的数据,现在要分成几个RTT(假设发送的数据量刚好是这样),特别是Linux 2的服务器initcwnd只有3MSS,所以可以手动把它改大,如改成10,可执行以下命令:&/p&&p&sudo ip route change default via 192.168.1.1 dev eth0 proto static initcwnd 10&/p&&p&快速恢复的引入也是考虑到了慢启动的缺点。&/p&&p&然后再讨论一个很出名的算法&/p&&h2&9. Nagle算法&/h2&&p&假设要通过http发送hello, world这12个字节,但是实际上要发送多少个字节呢?如下:&/p&&blockquote&&p&12:12:57.091926 IP 10.2.200.140.http-alt & 10.2.200.11.60882: Flags [P.], seq 288:301, ack 378, win 4105, options [nop,nop,TS val &a href=&tel:&&&/a& ecr &a href=&tel:&&&/a&], length 13: HTTP&/p&&/blockquote&&p&http数据总共发送了300个字节,也就是说http报文头就占用了288个字节,但是这还不包括其它报文头,如下所示:&img data-rawwidth=&1284& data-rawheight=&154& src=&/v2-a8dd6963814bbb869209_b.png& class=&origin_image zh-lightbox-thumb& width=&1284& data-original=&/v2-a8dd6963814bbb869209_r.png&&&/p&&p&也就是说为了发送12个字节的数据,总共得发送356个字节,有效内容仅占了4%不到。因此在那个需要用电话拨号上网的年代,这个代价就有点大了,所以Nagle算法的核心思想是:等数据积累多了再一起发出去,大概等待200ms,这样可以提高网络的吞吐率。&/p&&p&但是在现在光纤的时代,带宽和速度已经不是太大的问题了,如果每个请求都要延迟200ms,会造成实时性比较差。所以通常是要把Nagle算法禁掉,可以在创建套接字的时候设置TCP_NODELAY标志位。&/p&&h2&10. HTTP报文头大小限制&/h2&&p&(1)请求头大小限制&/p&&p&标准并没有规定http请求头的大小限制,但是在实际的实现上会有限制。如nginx限制为4k - 8k,tomcat最小支持8K。&/p&&p&(2)url长度限制&/p&&p&如下http报文格式所示:&img data-rawwidth=&932& data-rawheight=&330& src=&/v2-e7de68b9d902dcf79a91103_b.png& class=&origin_image zh-lightbox-thumb& width=&932& data-original=&/v2-e7de68b9d902dcf79a91103_r.png&&&/p&&p&URL是在请求行里面的,并不在请求头里,同样标准也没有规定URL有长度限制,但是实际的实现有限制,如&a href=&/?target=http%3A///questions/417142/what-is-the-maximum-length-of-a-url-in-different-browsers& class=& wrap external& target=&_blank& rel=&nofollow noreferrer&&下图&i class=&icon-external&&&/i&&/a&所示:&img data-rawwidth=&682& data-rawheight=&252& src=&/v2-4b05dbb9aceb781eca3a3_b.png& class=&origin_image zh-lightbox-thumb& width=&682& data-original=&/v2-4b05dbb9aceb781eca3a3_r.png&&&/p&&p&一个比较安全的值应该是8K,这样兼容性最好。同时需要注意的是GET请求,参数是在URL里面,而POST请求参数是在请求数据里面,所以GET请求的数据不能太大。&/p&&p&(3)cookie的长度限制&/p&&p&cookie是在请求头里以普通键值对的方式存在,一般一个domain的cookie不能超过4Kb,50个cookie,不然浏览器可能会不支持。服务可以通常Set-Cookie通知客户端设置cookie,而客户端可以用Cookie字段告知服务现在的cookie数据是怎么样的,如下所示:&img data-rawwidth=&2252& data-rawheight=&726& src=&/v2-81f5a53ced163f0176c91_b.png& class=&origin_image zh-lightbox-thumb& width=&2252& data-original=&/v2-81f5a53ced163f0176c91_r.png&&&/p&&p&上面的一些基础问题讨论完了,我们终于可以来分析websocket了。&/p&&h2&11. Websocket&/h2&&h3&(1)实现一个web聊天&/h3&&p&怎么实现一个http的web的实时聊天呢,怎么知道对方有没有发送消息给我呢?有几种方法。&/p&&p&第一种办法使用轮询,例如每隔2s就发一个请求向服务端查询,但是这种方法会造成资源的浪费。&/p&&p&第二种办法使用Service Worker实现浏览器的Push,这种方法需要先注册FCM账号,获取到一个App Id,用Service Worker监听,服务向&a href=&/?target=https%3A///gcm/send& class=& external& target=&_blank& rel=&nofollow noreferrer&&&span class=&invisible&&https://&/span&&span class=&visible&&/&/span&&span class=&invisible&&gcm/send&/span&&span class=&ellipsis&&&/span&&i class=&icon-external&&&/i&&/a&发送消息,谷歌服务器就会向那个App Id发送一个推送,就实现了浏览器的Push。但是这种办法兼容性还不是很好,并且大陆的小伙伴无法在正常网络环境收到谷歌服务器的消息。&/p&&p&所以就有了websocket建立常连接。为此建立一个websocket的demo.&/p&&h3&(2)websocket的demo&/h3&&p&为了实验,写一个websocket的demo,先装一个websocket的Node包,然后监听在8080端口,接着写客户端html5 websocket代码:&/p&&div class=&highlight&&&pre&&code class=&language-js&&&span&&/span&&span class=&kd&&var&/span& &span class=&nx&&socket&/span& &span class=&o&&=&/span& &span class=&k&&new&/span& &span class=&nx&&WebSocket&/span&&span class=&p&&(&/span&&span class=&s2&&&ws://10.2.200.140:8080&&/span&&span class=&p&&);&/span&
&span class=&nx&&socket&/span&&span class=&p&&.&/span&&span class=&nx&&onopen&/span& &span class=&o&&=&/span& &span class=&kd&&function&/span&&span class=&p&&(){&/span&
&span class=&nx&&socket&/span&&span class=&p&&.&/span&&span class=&nx&&send&/span&&span class=&p&&(&/span&&span class=&s2&&&长江长江,我是黄河&&/span&&span class=&p&&);&/span&
&span class=&p&&}&/span&
&span class=&nx&&socket&/span&&span class=&p&&.&/span&&span class=&nx&&onmessage&/span& &span class=&o&&=&/span& &span class=&kd&&function&/span&&span class=&p&&(&/span&&span class=&nx&&event&/span&&span class=&p&&){&/span&
&span class=&nb&&document&/span&&span class=&p&&.&/span&&span class=&nx&&write&/span&&span class=&p&&(&/span&&span class=&s2&&&收到来自黄河的消息:&&/span& &span class=&o&&+&/span& &span class=&nx&&event&/span&&span class=&p&&.&/span&&span class=&nx&&data&/span&&span class=&p&&);&/span&
&span class=&p&&}&/span&
&/code&&/pre&&/div&&p&打开这个页面,浏览器就会显示一个websocket的连接:&img data-rawwidth=&1306& data-rawheight=&102& src=&/v2-5b51942bfd418ba1405c0b_b.png& class=&origin_image zh-lightbox-thumb& width=&1306& data-original=&/v2-5b51942bfd418ba1405c0b_r.png&&&/p&&p&然后我们用tcpdump研究websocket连接建立的过程。&/p&&h3&(3)Websocket连接建立&/h3&&p&首先还是要先建立tcp连接,完成后客户端发送一个upgrade的http请求:&/p&&blockquote&&p&14:23:36.926775 IP 10.2.200.11.61205 & 10.2.200.140.8080: Flags [P.], seq 1:435, ack 1, win 4117, options [nop,nop,TS val &a href=&tel:&&&/a& ecr &a href=&tel:&&&/a&], length 434: HTTP: GET / HTTP/1.1&/p&&/blockquote&&p&这个报文的详细内容如下:&img data-rawwidth=&1132& data-rawheight=&512& src=&/v2-ec603089deca6b5cdfe76_b.png& class=&origin_image zh-lightbox-thumb& width=&1132& data-original=&/v2-ec603089deca6b5cdfe76_r.png&&&/p&&p&服务端收到后同意握手,返回Switching Protocols,连接建立,如下报文:&/p&&blockquote&&p&14:23:36.929714 IP 10.2.200.140.8080 & 10.2.200.11.61205: Flags [P.], seq 1:164, ack 435, win 4104, options [nop,nop,TS val &a href=&tel:&&&/a& ecr &a href=&tel:&&&/a&], length 163: HTTP: HTTP/1.1 101 Switching Protocols&/p&&/blockquote&&p&详细内容如下所示:&img data-rawwidth=&922& data-rawheight=&190& src=&/v2-f8fe432bce653c0d61d310dcda5b116e_b.png& class=&origin_image zh-lightbox-thumb& width=&922& data-original=&/v2-f8fe432bce653c0d61d310dcda5b116e_r.png&&&/p&&h3&(4)传送数据&/h3&&p&发送“hello, world”12字节内容,用ws只需要发送18字节,这比http 300个字节要少了很多:&/p&&blockquote&&p&14:24:36.009503 IP 10.2.200.11.61205 & 10.2.200.140.8080: Flags [P.], seq 492:510, ack 168, win 4112, options [nop,nop,TS val &a href=&tel:&&&/a& ecr &a href=&tel:&&&/a&], length 18: HTTP&/p&&p&14:24:36.009556 IP 10.2.200.140.8080 & 10.2.200.11.61205: Flags [.], ack 510, win 4101, options [nop,nop,TS val &a href=&tel:&&&/a& ecr &a href=&tel:&&&/a&], length 0&/p&&/blockquote&&p&具体可以定义消息的类型,例如type = 1表示心跳消息,type = 2表示用户发送的消息,还可以再定义subtype,并自定义消息内容的格式,再封装一些自定义的消息机制等等。&/p&&h3&(5)关闭连接&/h3&&p&30s后,双方没有传送数据,websocket连接关闭,进行四次挥手。&/p&&blockquote&&p&14:25:06.017016 IP 10.2.200.140.8080 & 10.2.200.11.61205: Flags [F.], seq 170, ack 510, win 4101, options [nop,nop,TS val &a href=&tel:&&&/a& ecr &a href=&tel:&&&/a&], length 0&/p&&/blockquote&&br&&p&这样就实现了一个实时的web聊天,需要注意的是websocket是一套协议,任何人只要遵守这套协议就可以使用并和其他人互联,不管你是JS还Android/IOS/C++/Java。ws默认监听在80端口,wss监听在443端口,和http/https一样。&/p&&p&最后再比较一下websocket和webRTC&/p&&h3&(6)Websocket和WebRTC&/h3&&p&Websocket是为了解决实时传送消息的问题,当然也可以传送数据,但是不保证传送的效率和质量,而WebRTC可用于可靠地传输音视频数据、文件等。并且可建立P2P连接,不需要服务进行转发数据。虚拟电话、在线面试等现在很多都采用WebRTC实现。&/p&&p&最后做个总结。这篇文章介绍了很多通信协议的东西,分析了TCP/IP的三次握手和四次挥手,并讨论了为什么握手是三次,而挥手是四次,还讲了四层网络模型,分析了工作在不同层的协议和工具,后面又重点分析了TCP的拥塞控制,包括超时重传、慢启动和拥塞避免、快速重传和快速恢复,接着还讲了点HTTP的东西,最后简单分析了下Websocket连接的过程和它的特点以及和WebRTC的区别。上面可以说是TCP/IP协议的核心内容,我们通过一两个demo把它给串了起来,对读者应该有一个启发作用,可以更深刻地理解网络协议,当你在写一个请求的时候,你知道它的背后发生了什么。读者可以根据本文再继续查阅相关资料延伸扩展。如有不正确之处还请指出。&/p&
我们用websocket和http来研究一下TCP/IP协议的一些特性,在上一篇文章《》里我们已经研究了https建立的过程。上一篇是用的wireshark的抓包工具,这一篇将用tcpdump命令行工具。1. tcpdumpLinux系的系统有一个很好用的抓包工具…
&p&先玩个小游戏,如果你在写简历时也遇到了相同的情况,请打勾:&/p&&br&&img src=&/v2-0bceffc53c8ccb61dc75_b.png& data-rawwidth=&531& data-rawheight=&283& class=&origin_image zh-lightbox-thumb& width=&531& data-original=&/v2-0bceffc53c8ccb61dc75_r.png&&&p&勾0-1条,可以忽略这个答案。&/p&&p&勾2-3条,你之前的简历可能是在自嗨,需要一点解药。&/p&&p&勾4-6条,重度患者,请仔细消化下面的内容。&/p&&p&&b&其实,上面的几种情况,大部分人写简历和求职时都会遇到。&/b&&/p&&p&有的人不了解面试官的需求,想把简历写得面面俱到,写完却是毫无重点,且不自知,最终简历石沉大海;&/p&&p&有的人想在简历中呈现更好的自己,不管这个“自己”是不是真实的:把工作年限加长、把技能全写成精通。这种自嗨只能换来面试时的尴尬;&/p&&p&有的人技术强,肯努力,却怀才不遇,找不到一条更有效的渠道,让更多大公司了解到自己,就错过了很多上升机会。其实这也是可以改善的。&/p&&p&&b&下面就讲讲你如何躲过这些简历中常见的“坑”。&/b&写一份让面试官喜欢、又真实的技术简历。(本文内容整理自「 百楼俱乐部技术分享·第一期——如何准备技术简历 」,分享人是&a href=&///?target=https%3A///& class=& wrap external& target=&_blank& rel=&nofollow noreferrer&&实验楼&i class=&icon-external&&&/i&&/a&CEO石头山。「 百楼俱乐部 」顾名思义,是实验楼100楼以上的用户组成的群体,除了定期的技术分享外,还有其他福利,欢迎大家爬楼加入。)&/p&&img src=&/v2-030e7503beb14cc4cca224ae5b1c51b9_b.jpg& data-rawwidth=&600& data-rawheight=&277& class=&origin_image zh-lightbox-thumb& width=&600& data-original=&/v2-030e7503beb14cc4cca224ae5b1c51b9_r.jpg&&&p&&b&分享人:石头山&/b&(曾任职于VMware、Intel等知名外企,筛选过数千份简历,深知简历和面试的“套路”,&b&将带你从面试官的角度,了解什么样的简历才是受人欢迎的。&/b&&/p&&p&&b&01&/b&“ 本文不适合老司机… ”&/p&&p&下面分享的内容是面向互联网行业或软件行业求职的人,而且主要是应届生和毕业三年以内的求职者。因为对于经验丰富的求职者,他们的招聘方式可能会差异非常大。比方说,他对简历的重视程度会越来越低,更多依靠的是行业内的知名度,和公司内部高级工程师的引荐。&/p&&p&因为我个人缺少大型国企、传统行业的经验,所以说这个话题难免不全面,请大家见谅。&/p&&p&&b&02&/b&“ 基本信息那么简单吗?”&/p&&p&首先是基本信息,大家肯定都会写到。包括:&/p&&ul&&li&姓名&/li&&li&年龄&/li&&li&工作年限&/li&&li&学历&/li&&li&城市&/li&&li&联系方式:手机 + 邮箱&/li&&/ul&&p&这里还有几项需要注意的:&/p&&p&首先在基本信息里面,我们会看到,&b&有很多同学会故意地把自己的工作年限写得很长。&/b&比如16年毕业,自己会选择性地写成两年甚至三年的工作经验。我要讲的是,这是完全没有必要。因为首先,这会让招聘者很疑惑,你前面的这些工作经验是哪里来的?当然大部分可能是把实习经验算成工作经验了,但实际上到面试阶段还是会把这部分抛除掉的。所以我们希望的原则是尽可能地实事求是,不要虚报。不要把自己的毕业年限故意提前,来增加自己的工作年限。哪怕这会帮你过简历自动筛选的过程,到人工筛选或面试时,仍有很大的可能会被筛掉。&/p&&p&另外,&b&对于一些转行的朋友,如果你原来不是做计算机这一行的&/b&,并且和计算机行业相差非常多,比方说汽车维修,那就简单的一句话概过就可以了。只是为了让你的招聘方知道,你毕业以后前几年并不是在从事计算机行业,然后把自己在计算机行行业内的工作经验、工作年限写清楚就可以了。 &/p&&p&联系方式的话,手机号自然不必多说,邮箱尽可能地用一些常用的邮箱。如果你有自己的个人网站,也可以选择用自己个人网站的邮箱。但是呢,邮箱的名称尽可能要简短好认,比如1和L混合在一起的那种邮箱地址,看起来是非常痛苦的。&/p&&p&&b&03&/b&“ 工作、实习经历,相当重要 ”&/p&&p&简历的第二部分,就是工作和实习经历。&/p&&ul&&li&公司名称&/li&&li&职位&/li&&li&时间&/li&&li&详细工作&/li&&/ul&&p&这部分相当重要。无论是应届毕业生,还是工作过几年的、有经验的,其实&b&招聘方最看重的就是这一部分的内容。&/b&&/p&&p&在这一部分中,至少你要证明你在先前的实习或者工作中,有非常多收获,有对你个人的成长非常有帮助的经历。&b&这样的经历是最有价值的。&/b&&/p&&p&另外工作经验要尽可能地&b&避免断档&/b&。如果有断档的话,在你面试的时候肯定会问到,你需要有一个比较合理的解释。比方说你中间是出国读书了,或者哪种情况,然后有一两年的断档,这个是没什么问题的。&/p&&p&第二点需要注意的就是,一个公司只需要写一栏。在这一栏下,你可以列出一、二、三、四的这种条目,详细介绍在这个公司中,你所从事的事情以及学到的技术。&b&但是要避免一个公司写很多栏&/b&,哪怕你在这公司里的职务有些变化,你可以只写一个最重要职务,或者一个最高级的职务。&/p&&p&&b&对于工作的描述&/b&,需要详细地介绍自己曾经开发的产品,以及开发这个产品中间用到的一些技术,负责的任务,并且要标明是否带过团队、带团队的规模是多少个人、这个项目的是否自己独立完成、或者两个人协作完成中你所处的角色,这个是非常细节的地方,但是一定要写得清楚一些。不需要太啰嗦,但是每一点一定要点到位。最后一点就是刚才也提到的,把每一项以一个简短的条目一二三四标出来。这样能让对方非常清晰地看到,你所收获到的一些关键点,能够抓住他们的眼球。&/p&&p&在描述项目中的时候,&b&尽可能多的使用一些关键字,一些技术的关键字&/b&。比方说,这个项目中我用到了Mango DB、Laravel,或是前后端常用的一些框架,然后把它写到这个项目描述中。&/p&&p&而对于应届生来说的话,如果没有工作经验,&b&实习经验就非

我要回帖

更多关于 大神f1刷机 的文章

 

随机推荐