被公司被法院查封怎么办的wwWpplsp地还有在恢复正常com的可能吗

从莎车县到阿拉尔的河是什么河?_百度知道
从莎车县到阿拉尔的河是什么河?
我有更好的答案
位于新疆维吾尔自治区塔里木盆地北部,仅次于伏尔加河。历史上塔里木河河道南北摆动、锡尔—纳伦河、阿姆—喷赤—瓦赫什河和乌拉尔河,为世界第5大内流河,中国最长的内流河,同孔雀河分离,河水复经铁干里克故道流向台特马湖,深处中亚内陆,四周高山环绕塔里木河塔里木河由发源于天山山脉的阿克苏河、发源于喀喇昆仑山的叶尔羌河以及和田河汇流而成,迁徙无定。最后一次在1921年。塔里木河周边因为有塔里木河水的滋润而变成绿洲地带。塔里木河流域远离海洋,大陆性气候非常明显,四季气候悬殊,气候变化剧烈,降水稀少,蒸发强烈,主流东流入孔雀河注入罗布泊。流域面积19.8万平方千米,日照时间长。1952年在尉犁县附近筑坝,最后流入台特马湖,塔里木河全长2137千米
塔里木河,沙车到阿拉尔叫叶尔羌河是塔里木河的分支
我只知道塔里木河
其他1条回答
为您推荐:
换一换
回答问题,赢新手礼包
个人、企业类
违法有害信息,请在下方选择后提交
色情、暴力
我们会通过消息、邮箱等方式尽快将举报结果通知您。?&&&&?&&&&?&&&&?&&&&?&&&&?&&&&?&&&&?&&&&?&&&&?&&&&?&&&&?&&&&?&&&&?&&&&?&&&&?&&&&?&&&&?&&&&?&&&&?&&&&
您当前的位置 :&&&&&&&&&&&&&正文被查封的房子居然还能连卖两次律师法官都惊呆了事发慈溪,房子已过户 当地国土局解释是软件出错了,当事人却觉得疑点重重 日 09:03:47 住在杭州网
  世界上有一种房子,是不能过户的,就是被法院查封的房子。不过慈溪的罗先生,最近却遇到一件奇葩的事情,他家对门的房子,本来早已被查封了,但这两天却开始装修了,更奇怪的是装修方居然还拿得出过户的房产证。而且,这查封的房子,已经两度易主。
  这是怎么回事呢?罗先生有些想不通了。&被法院查封的房子,难道还能过户吗?&昨天,他给本报记者打来了电话,希望查清这件事。
  现场:
  被查封的房子
  居然开始装修了
  罗先生住在慈溪桥头镇潭河沿村。他家对门的房子要从&发小&沈某说起。
  两年前,沈某因为生意需要,向罗先生借钱。当时念在兄弟情义,他借给沈某40万元。但后来生意失败,去年9月,沈某突然失踪了。
  无奈,罗先生向法院起诉。经调解,沈某父亲提出,愿意替儿子偿还债务,并承诺卖房还款。但蹊跷的是,没多久,沈某父亲也失了踪,罗先生只好向法院申请强制执行,查封了他家对门的房子。
  这套房子,有两间,占地面积98.52O。按照法院做出的裁定,这套房子从日起被查封,查封期为2年。
  但这几天,本来空置的房子突然热闹了,几个泥水工进进出出,一派热火朝天的样子。罗先生的母亲看到,很好奇,就去问了工人。他们说,这个房子要开店了。罗母又继续追问,沈某全家不是都走了吗?工人们说,房子的主人姓何,不姓沈。
  罗母立马将情况告诉了儿子。过了几天,屋子的装修现场,来了一个人在指挥工人干活,几个邻居也上前询问,这个人拿出了房子的土地证,说这房子是今年9月花了80多万买来的,卖家叫宋海龙。
  &法院查封的房子,还能过户?&对于这个消息,小村子里一时间议论纷纷。
  进展:
  被查封的房子
  曾两度易主
  听到这个消息,罗先生也有些纳闷。他先给当年帮自己打官司的孙律师打了电话,孙律师说,被法院查封的房子,是不可能过户的。然后他又咨询了当时执行的法官,听到这个消息,法官也惊呆了。
  11月初,罗先生开始了调查:被法院查封的房子,究竟是怎么过户的?
  他先去慈溪市国土局档案室,查询了该房的信息,发现该房子有2次易主的信息:第一次是日,沈某父亲过户给了宋海龙,第二次是今年9月23日,宋某又过户给了目前正在装修的何某。
  而在这份信息中,同时也提到了日,该房曾被慈溪法院查封。
  拿到这份材料,孙律师怎么也不敢相信。于是,他们来到了负责办理房产过户的慈溪市行政服务中心国土窗口进行询问。
  看到材料,工作人员也惊呆了。工作人员只能回复罗先生,会将这一情况向上级汇报。
  前两天,罗先生接到了窗口负责人杨渊博的回电,他解释说,被查封的房子能过户,是电脑的软件系统出了错,当时过户时没发现。这样的情况,以前从没发生过。
  &自己的合法权益,受到了损害,使自己的债权无法实现。&在电话中,罗先生提议,要求房子的所有权尽快变更回来,继续查封。不过,杨渊博说,变更回来需要走一段法律程序,时间无法确定,可能性也不大。
  疑点:
  被查封的房子
  过户疑点重重
  这通电话后,罗先生发现这个事情疑点还不少。
  &被法院查封的房子,能过户已经很不正常了,既然能过户,房子为什么不能恢复呢?难道国土局打算将错就错?&
  不久,他又得到了一条线索,几个村民告诉他,沈某家曾向一罗姓村民借钱,而这个罗姓村民恰好是杨渊博的岳父。罗先生告诉记者,这个房子的过户审批,连续2次都是在慈溪行政服务中心的国土窗口办理的,而窗口的负责人就是杨渊博。
  &如果是电脑系统出了错,为什么整个系统,单单就自己的这一户出了错。而且出错七八个月,竟然没有一个人知道系统出错的事。&他越来越觉得,这个事情,可能有人为操作的因素存在。
  而在自己投诉后,罗先生接到不少的电话,都是来帮国土局说情的,希望&大事化小,小事化了&。
  罗先生说,他最近一次联系慈溪国土局,希望给予一个解决方案。杨渊博告诉他,他们国土局商讨了一个处理方案,是由软件公司出资40万,押给慈溪法院,如果房子能恢复到沈某父亲名下,此事作罢,如果不能恢复,这40万就赔偿给罗先生。
  在电话中,杨渊博多次强调,房子基本上是无法恢复的,只让罗先生耐心等待。之后,罗先生多次致电杨渊博,对方一直没有给予明确的处理时间。不过,在一次沟通中,杨也曾经提到,自己的岳父就住在村子里。
  专业人士:
  国土局有过错
  罗先生可要求赔偿
  带着罗先生的疑问,昨天下午,记者来到了慈溪行政服务中心的国土窗口。
  杨渊博不在,办公室一位沈姓工作人员接待了记者。
  说起沈某房子被过户的事情,沈姓工作人员说,&这个案子已经解决了啊,我们蔡局长都知道的。这事情是软件公司的错&&&
  记者继续追问,那国土局的问题有没有?对方一听,立马走开了。留下一句&我不清楚&。
  记者问,负责人在吗?这位工作人员说,我们这里是办证中心,具体得问局里。
  随后,记者来到慈溪市国土局。门卫给办公室打了电话,对方表示,不接待记者,建议记者去白沙路的一个地方,但没有告知具体位置。
  联系不上国土局,关于被查封房子如何过户的过程,一时间也成了谜。
  记者也就此情况咨询了专业人士。
  专业人士告诉记者,被查封的房子,是罗先生债权的保障,现在房子被过户了,致使罗先生的债权无法实现。这一过错,是由国土局造成的。罗先生可以要求国土局赔偿损失。
  被查封的房子过户后,能否再恢复原状?专业人士表示,这个查封的房子,是在集体土地上的房子,过户仅能在同村村民之间进行过户,并且买房人要符合建房审批条件。
  如果买房人不符合上述条件,那么过户是可以撤销的。如果购房人符合上述条件,过户就很难恢复了。
来源: 作者:邹洪珊 编辑:徐立群最后编辑:徐立群
  凡注有"住在杭州网"或电头为"浙江在线?住在杭州网"的稿件,均为住在杭州网独家版权所有,未经许可不得转载或镜像;授权转载必须注明来源为"浙江在线?住在杭州网",并保留"浙江在线?住在杭州网消息"的电头。
  本网未注明"来源:住在杭州网"的图文等稿件均为转载稿,本网转载出于传递更多信息之目的,并不意味着赞同其观点或证实其内容的真实性。
  联系电话:9。
住在杭州网微信
【推荐作者】
浙报传媒地产研究院院长、本网首席评论员
本网总策划、
房地产二十大知名记者
资深记者、
记者部主管
资深记者、
记者部主管
原创记者、
【推荐文章】
【新房上市】
[] &&&&[] &&&&[] &&&&[] &&&&[] &&&&[] &&&&[] &&&&[] &&&&[] &&&&[] &&&&[] &&&&[] &&&&[] &&&&[] &&&&[] &&&&[] &&&&[] &&&&[] &&&&[] &&&&[] &&&&
增值电信业务经营许可证:浙B2- | 广告经营许可证号:6
浙江在线新闻网站、杭州市建设委员会 联合主办  浙江在线新闻网站版权所有误删了电脑里重要的资料,怎么找回?_百度知道
误删了电脑里重要的资料,怎么找回?
我有更好的答案
然后点击“扫描”按钮。在文件列表中选择想要恢复的文件,然后在文件前面勾选,再点击“保存”按钮。选择一个安全的路径来保存要恢复的数据,选择好以后点击“确认”按钮mini tool 就可以恢复删除的数据。打开软件,在软件的主界面中点击“删除恢复”按钮,开始恢复操作。选择误删除数据所在的磁盘分区(这里以选择C分区为例)
采纳率:38%
来自团队:
为您推荐:
其他类似问题
您可能关注的内容
换一换
回答问题,赢新手礼包
个人、企业类
违法有害信息,请在下方选择后提交
色情、暴力
我们会通过消息、邮箱等方式尽快将举报结果通知您。阅览室(提示保持安静) 1、 放轻你的脚步,倾听周围感激的心声。 2、 别说话,听听书在说些什么。 ...
有两种情况, 1,对方将你设置了黑名单,所以你每次打过去都被转接到通信助理那去了,你可以换个手机打试...
来电提醒(短信呼) 一、业务介绍 客户关机或手机掉网(地下室/电梯里)时,来电转入语音应答系统记录来...
The number you have dailed cannot be connected at ...
根据您的描述,建议您按照如下步骤尝试操作: 1、重启手机(以便让手机重新进行搜索及注册) 2、在待机...
一、苹果手机老是提示输入ID密码是因为苹果手机开启自动icloud备份功能,只需关闭自动icloud...
手机提示“暂停服务”,您可以通过以下方式检测:1.请确认您的手机是不是被设置为飞航模式;2.请确认您...
1.一棵大树,可以制造千万根火柴;一根火柴,可以毁掉千万棵大树 2.司机一滴酒,亲人两行泪 3.喝进...
方法: 1.首先打开Excel表格 2.然后选择工具栏的“编辑”菜单 3.然后选择“链接”这个选项 ...
首先打开qq界面,进入基本设置 2. 打开信息展示栏,可以根据你需要设置的消息提醒框进行设置
开通余额变动提醒通知后就能动态掌握你的帐户余额信息,工行网上银行开通短信提醒的步骤是:登录工行个人网...
iphone 来短信不能提醒的原因:信息推送已关闭。 打开操作为: 第一步:点击桌面“设置”图标。 ...
在系统设置,状态和提醒栏里有个消息提醒,把会话消息提醒那个收到消息时浮现关掉就可以了!
中行网银怎么设置手机短信提醒 中行网银手机短信提醒(也就是开通中银e信服务)可以为您提供以下服务: ...
iphone如何关闭短信二次提醒你好,设置--通知--信息--重复提醒---1次,这样就好了,不会重...
关闭微信自动弹出消息的方法: 登陆微信,点击“设置”,再选择“通用”。 进入通用设置界面,点击“新消...
原因: 1、系统反应慢,可能是之前已经浏览过的消息,但是第二天还显示有新消息或者是确实有新消息但是网...
QQ客户端有个设置,在你点击自己的头像然后会出来最右栏,下方就有设置,里面有个消息通知,第三行,把“...
你好,可能的原因是正在打开程序的信息推送。 苹果手机收到短信却没有提醒怎么回事呢?这种情况下,我们首...&blockquote&&p&作者介绍:黄日成,手Q游戏中心后台开发,腾讯高级工程师。从事C++服务后台开发4年多,主要负责手Q游戏中心后台基础系统、复杂业务系统开发,主导过手Q游戏公会、企鹅电竞App-对战系统等项目的后台系统设计,有丰富的后台架构经验。&/p&&/blockquote&&h4&引言&/h4&&p&作为文章”《&a href=&https://link.zhihu.com/?target=https%3A//www.qcloud.com/community/article/Futm_source%3DCommunity%26utm_medium%3Darticleutm_campaign%3DCommunity& class=& wrap external& target=&_blank& rel=&nofollow noreferrer&&从TCP三次握手说起—浅析TCP协议中的疑难杂症&/a&》”的姊妹篇,很早就计划写篇关于UDP的文章,尽管UDP协议远没TCP协议那么庞大、复杂,但是,要想将UDP描述清楚,用好UDP却要比TCP难不少,于是文章从下笔写,到最终写成,断断续续拖了好几个月。&/p&&p&对应系列的上一篇:&/p&&p&&a href=&https://link.zhihu.com/?target=https%3A//www.qcloud.com/community/article/Futm_source%3DCommunity%26utm_medium%3Darticleutm_campaign%3DCommunity& class=& wrap external& target=&_blank& rel=&nofollow noreferrer&&告知你不为人知的UDP-连接性和负载均衡&/a&&/p&&h4&&b&3. UDP疑难杂症&/b&&/h4&&p&&b&3.1 UDP的传输方式:面向报文&/b&&/p&&p&面向报文的传输方式决定了UDP的数据发送方式是一份一份的,也就是应用层交给UDP多长的报文,UDP就照样发送,即一次发送一个报文。那么UDP的报文大小由哪些影响因素呢?UDP数据包的理论长度是多少,合适的UDP数据包应该是多少呢?&/p&&p&&b&(1)UDP报文大小的影响因素,主要有以下3个&/b&&/p&&blockquote&&p&[1] UDP协议本身,UDP协议中有16位的UDP报文长度,那么UDP报文长度不能超过2^16=65536.&br&[2] 以太网(Ethernet)数据帧的长度,数据链路层的MTU(最大传输单元)。&br&[3] socket的UDP发送缓存区大小&/p&&/blockquote&&p&&b&(2) UDP数据包最大长度&/b&&br&根据UDP协议,从UDP数据包的包头可以看出,UDP的最大包长度是2^16-1的个字节。由于UDP包头占8个字节,而在IP层进行封装后的IP包头占去20字节,所以这个是UDP数据包的最大理论长度是2^16 - 1 - 8 - 20 = 65507字节。如果发送的数据包超过65507字节,send或sendto函数会错误码1(Operation not permitted, Message too long),当然啦,一个数据包能否发送65507字节,还和UDP发送缓冲区大小(linux下UDP发送缓冲区大小为:cat /proc/sys/net/core/wmem_default)相关,如果发送缓冲区小于65507字节,在发送一个数据包为65507字节的时候,send或sendto函数会错误码1(Operation not permitted, No buffer space available)。&/p&&p&&b&(3) UDP数据包理想长度&/b&&br&理论上UDP报文最大长度是65507字节,实际上发送这么大的数据包效果最好吗?我们知道UDP是不可靠的传输协议,为了减少UDP包丢失的风险,我们最好能控制UDP包在下层协议的传输过程中不要被切割。相信大家都知道MTU这个概念。 MTU最大传输单元,这个最大传输单元实际上和链路层协议有着密切的关系,EthernetII帧的结构DMAC+SMAC+Type+Data+CRC由于以太网传输电气方面的限制,每个以太网帧都有最小的大小64字节,最大不能超过1518字节,对于小于或者大于这个限制的以太网帧我们都可以视之为错误的数据帧,一般的以太网转发设备会丢弃这些数据帧。由于以太网EthernetII最大的数据帧是1518字节,除去以太网帧的帧头(DMAC目的MAC地址48bit=6Bytes+SMAC源MAC地址48bit=6Bytes+Type域2bytes)14Bytes和帧尾CRC校验部分4Bytes那么剩下承载上层协议的地方也就是Data域最大就只能有1500字节这个值我们就把它称之为MTU。&br&在下层数据链路层最大传输单元是1500字节的情况下,要想IP层不分包,那么UDP数据包的最大大小应该是1500字节 – IP头(20字节) – UDP头(8字节) = 1472字节。不过鉴于Internet上的标准MTU值为576字节,所以建议在进行Internet的UDP编程时,最好将UDP的数据长度控制在 (576-8-20)548字节以内。&/p&&p&&b&3.2 UDP数据包的发送和接收问题&br&(1) UDP的通信有界性&/b&&br&在阻塞模式下,UDP的通信是以数据包作为界限的,即使server端的缓冲区再大也要按照client发包的次数来多次接收数据包,server只能一次一次的接收,client发送多少次,server就需接收多少次,即客户端分几次发送过来,服务端就必须按几次接收。&/p&&p&&b&(2) UDP数据包的无序性和非可靠性&/b&&br&client依次发送1、2、3三个UDP数据包,server端先后调用3次接收函数,可能会依次收到3、2、1次序的数据包,收包可能是1、2、3的任意排列组合,也可能丢失一个或多个数据包。&/p&&p&&b&(3) UDP数据包的接收&/b&&br&client发送两次UDP数据,第一次 500字节,第二次300字节,server端阻塞模式下接包,第一次recvfrom( 1000 ),收到是 1000,还是500,还是300,还是其他?&/p&&blockquote&&p&由于UDP通信的有界性,接收到只能是500或300,又由于UDP的无序性和非可靠性,接收到可能是300,也可能是500,也可能一直阻塞在recvfrom调用上,直到超时返回(也就是什么也收不到)。&/p&&/blockquote&&p&在假定数据包是不丢失并且是按照发送顺序按序到达的情况下,server端阻塞模式下接包,先后三次调用:recvfrom( 200),recvfrom( 1000),recvfrom( 1000),接收情况如何呢?&/p&&blockquote&&p&由于UDP通信的有界性,第一次recvfrom( 200)将接收第一个500字节的数据包,但是因为用户空间buf只有200字节,于是只会返回前面200字节,剩下300字节将丢弃。第二次recvfrom( 1000)将返回300字节,第三次recvfrom( 1000)将会阻塞。&/p&&/blockquote&&p&&b&(4) UDP包分片问题&/b&&br&如果MTU是1500,Client发送一个8000字节大小的UDP包,那么Server端阻塞模式下接包,在不丢包的情况下,recvfrom(9000)是收到1500,还是8000。如果某个IP分片丢失了,recvfrom(9000),又返回什么呢?&br&根据UDP通信的有界性,在buf足够大的情况下,接收到的一定是一个完整的数据包,UDP数据在下层的分片和组片问题由IP层来处理,提交到UDP传输层一定是一个完整的UDP包,那么recvfrom(9000)将返回8000。如果某个IP分片丢失,udp里有个CRC检验,如果包不完整就会丢弃,也不会通知是否接收成功,所以UDP是不可靠的传输协议,那么recvfrom(9000)将阻塞。&/p&&p&&b&3.3 UDP丢包问题&/b&&/p&&p&在不考虑UDP下层IP层的分片丢失,CRC检验包不完整的情况下,造成UDP丢包的因素有哪些呢?&/p&&p&&b&[1] UDP socket缓冲区满造成的UDP丢包&/b&&br&通过 cat /proc/sys/net/core/rmem_default 和cat /proc/sys/net/core/rmem_max可以查看socket缓冲区的缺省值和最大值。如果socket缓冲区满了,应用程序没来得及处理在缓冲区中的UDP包,那么后续来的UDP包会被内核丢弃,造成丢包。在socket缓冲区满造成丢包的情况下,可以通过增大缓冲区的方法来缓解UDP丢包问题。但是,如果服务已经过载了,简单的增大缓冲区并不能解决问题,反而会造成滚雪球效应,造成请求全部超时,服务不可用。&/p&&p&&b&[2] UDP socket缓冲区过小造成的UDP丢包&/b&&br&如果Client发送的UDP报文很大,而socket缓冲区过小无法容下该UDP报文,那么该报文就会丢失。&/p&&p&&b&[3] ARP缓存过期导致UDP丢包&/b&&br&ARP的缓存时间约10分钟,APR缓存列表没有对方的MAC地址或缓存过期的时候,会发送ARP请求获取MAC地址,在没有获取到MAC地址之前,用户发送出去的UDP数据包会被内核缓存到arp_queue这个队列中,默认最多缓存3个包,多余的UDP包会被丢弃。被丢弃的UDP包可以从/proc/net/stat/arp_cache的最后一列的unresolved_discards看到。当然我们可以通过echo 30 & /proc/sys/net/ipv4/neigh/eth1/unres_qlen来增大可以缓存的UDP包。&br&UDP的丢包信息可以从cat /proc/net/udp 的最后一列drops中得到,而倒数第四列inode是丢失UDP数据包的socket的全局唯一的虚拟i节点号,可以通过这个inode号结合lsof(lsof -P -n | grep )来查到具体的进程。&/p&&p&&b&3.4 UDP冗余传输&/b&&/p&&p&在外网通信链路不稳定的情况下,有什么办法可以降低UDP的丢包率呢?一个简单的办法来采用冗余传输的方式。如下图,一般采用较多的是延时双发,双发指的是将原本单发的前后连续的两个包合并成一个大包发送,这样发送的数据量是原来的两倍。这种方式提高丢包率的原理比较简单,例如本例的冗余发包方式,在偶数包全丢的情况下,依然能够还原出完整的数据,也就是在这种情况下,50%的丢包率,依然能够达到100%的数据接收。&/p&&figure&&img src=&https://pic1.zhimg.com/v2-a01badcf3d87a89b6f6a1a79_b.jpg& data-rawwidth=&678& data-rawheight=&296& class=&origin_image zh-lightbox-thumb& width=&678& data-original=&https://pic1.zhimg.com/v2-a01badcf3d87a89b6f6a1a79_r.jpg&&&/figure&&h4&4 UDP真的比TCP要高效吗&/h4&&p&相信很多同学都认为UDP无连接,无需重传和处理确认,UDP比较高效。然而UDP在大多情况下并不一定比TCP高效,TCP发展至今天,为了适应各种复杂的网络环境,其算法已经非常丰富,协议本身经过了很多优化,如果能够合理配置TCP的各种参数选项,那么在多数的网络环境下TCP是要比UDP更高效的。&/p&&p&&b&4.1 影响UDP高效因素&/b&&/p&&p&&b&(1) 无法智能利用空闲带宽导致资源利用率低&/b&&br&一个简单的事实是UDP并不会受到MTU的影响,MTU只会影响下层的IP分片,对此UDP一无所知。在极端情况下,UDP每次都是发小包,包是MTU的几百分之一,这样就造成UDP包的有效数据占比较小(UDP头的封装成本);或者,UDP每次都是发巨大的UDP包,包大小MTU的几百倍,这样会造成下层IP层的大量分片,大量分片的情况下,其中某个分片丢失了,就会导致整个UDP包的无效。由于网络情况是动态变化的,UDP无法根据变化进行调整,发包过大或过小,从而导致带宽利用率低下,有效吞吐量较低。而TCP有一套智能算法,当发现数据必须积攒的时候,就说明此时不积攒也不行,TCP的复杂算法会在延迟和吞吐量之间达到一个很好的平衡。&/p&&p&&b&(2) 无法动态调整发包&/b&&br&由于UDP没有确认机制,没有流量控制和拥塞控制,这样在网络出现拥塞或通信两端处理能力不匹配的时候,UDP并不会进行调整发送速率,从而导致大量丢包。在丢包的时候,不合理的简单重传策略会导致重传风暴,进一步加剧网络的拥塞,从而导致丢包率雪上加霜。更加严重的是,UDP的无秩序性和自私性,一个疯狂的UDP程序可能会导致这个网络的拥塞,挤压其他程序的流量带宽,导致所有业务质量都下降。&/p&&p&&b&(3) 改进UDP的成本较高&/b&&br&可能有同学想到针对UDP的一些缺点,在用户态做些调整改进,添加上简单的重传和动态发包大小优化。然而,这样的改进并比简单的,UDP编程可是比TCP要难不少的,考虑到改造成本,为什么不直接用TCP呢?当然可以拿开源的一些实现来抄一下(例如:libjingle),或者拥抱一下Google的QUIC协议,然而,这些都需要不少成本的。&br&上面说了这么多,难道真的不该用UDP了吗?其实也不是的,在某些场景下,我们还是必须UDP才行的。那么UDP的较为合适的使用场景是哪些呢?&/p&&h4&&b&5 UDP的使用场合&/b&&/h4&&p&&b&5.1 通信实时性和持续性&/b&&/p&&p&在分组交换通信当中,协议栈的成本主要表现在以下两方面:&/p&&blockquote&&p&[1] 封装带来的空间复杂度[2] 缓存带来的时间复杂度&/p&&/blockquote&&p&以上两者是对立影响的,如果想减少封装消耗,那么就必须缓存用户数据到一定量在一次性封装发送出去,这样每个协议包的有效载荷将达到最大化,这无疑是节省了带宽空间,带宽利用率较高,但是延时增大了。如果想降低延时,那么就需要将用户数据立马封装发出去,这样显然会造成消耗更多的协议头等消耗,浪费带宽空间。&br&因此,我们进行协议选择的时候,需要重点考虑一下空间复杂度和时间复杂度间的平衡。通信的持续性对两者的影响比较大,根据通信的持续性有两种通信类型:[1] 短连接通信 [2] 长连接通信。对于短连接通信,一方面如果业务只需要发一两个包并且对丢包有一定的容忍度,同时业务自己有简单的轮询或重复机制,那么采用UDP会较为好些。在这样的场景下,如果用TCP,仅仅握手就需要3个包,这样显然有点不划算,一个典型的例子是DNS查询。另一方面,如果业务实时性要求非常高,并且不能忍受重传,那么首先就是UDP了或者只能用UDP了,例如NTP 协议,重传NTP消息纯属添乱(为什么呢?重传一个过期的时间包过来,还不如发一个新的UDP包同步新的时间过来)。如果NTP协议采用TCP,撇开握手消耗较多数据包交互的问题,由于TCP受Nagel算法等影响,用户数据会在一定情况下会被内核缓存延后发送出去,这样时间同步就会出现比较大的偏差,协议将不可用。&/p&&p&&b&5.2 多点通信&/b&&/p&&p&对于一些多点通信的场景,如果采用有连接的TCP,那么就需要和多个通信节点建立其双向连接,然后有时在NAT环境下,两个通信节点建立其直接的TCP连接不是一个容易的事情,在涉及NAT穿越的时候,UDP协议的无连接性使得穿透成功率更高(原因详见:由于UDP的无连接性,那么其完全可以向一个组播地址发送数据或者轮转地向多个目的地持续发送相同的数据,从而更为容易实现多点通信。)&br&一个典型的场景是多人实时音视频通信,这种场景下实时性要求比较高,可以容忍一定的丢包率。比如:对于音频,对端连续发送p1、p2、p3三个包,另一端收到了p1和p3,在没收到p2的保持p1的最后一个音(也是为什么有时候网络丢包就会听到嗞嗞嗞嗞嗞嗞…或者卟卟卟卟卟卟卟卟…重音的原因),等到到p3就接着播p3了,不需要也不能补帧,一补就越来越大的延时。对于这样的场景就比较合适用UDP了,如果采用TCP,那么在出现丢包的时候,就可能会出现比较大的延时。&/p&&p&&b&5.3 UDP使用原则&/b&&/p&&p&通常情况下,UDP的使用范围是较小的,在以下的场景下,使用UDP才是明智的&/p&&blockquote&&p&[1] 实时性要求很高,并且几乎不能容忍重传&br&例子:NTP协议,实时音视频通信,多人动作类游戏中人物动作、位置&br&[2] TCP实在不方便实现多点传输的情况&br&[3] 需要进行NAT穿越&br&[4] 对网络状态很熟悉,确保udp网络中没有氓流行为,疯狂抢带宽&br&[5] 熟悉UDP编程&/p&&/blockquote&&p&参考资料&br&&a href=&https://link.zhihu.com/?target=http%3A//blog.csdn.net/dog250/article/details/6896949& class=& wrap external& target=&_blank& rel=&nofollow noreferrer&&Netfilter,iptables/OpenVPN/TCP guard&/a&&/p&
作者介绍:黄日成,手Q游戏中心后台开发,腾讯高级工程师。从事C++服务后台开发4年多,主要负责手Q游戏中心后台基础系统、复杂业务系统开发,主导过手Q游戏公会、企鹅电竞App-对战系统等项目的后台系统设计,有丰富的后台架构经验。引言作为文章”《
&blockquote&作者介绍:黄日成,手Q游戏中心后台开发,腾讯高级工程师。从事C++服务后台开发4年多,主要负责手Q游戏中心后台基础系统、复杂业务系统开发,主导过手Q游戏公会、企鹅电竞App-对战系统等项目的后台系统设计,有丰富的后台架构经验。本文获得了小时光茶社的授权&/blockquote&&h2&7. 疑症(7)TCP的延迟确认机制&/h2&&p&按照TCP协议,确认机制是累积的,也就是确认号X的确认指示的是所有X之前但不包括X的数据已经收到了。确认号(ACK)本身就是不含数据的分段,因此大量的确认号消耗了大量的带宽,虽然大多数情况下,ACK还是可以和数据一起捎带传输的,但是如果没有捎带传输,那么就只能单独回来一个ACK,如果这样的分段太多,网络的利用率就会下降。为缓解这个问题,RFC建议了一种延迟的ACK,也就是说,ACK在收到数据后并不马上回复,而是延迟一段可以接受的时间,延迟一段时间的目的是看能不能和接收方要发给发送方的数据一起回去,因为TCP协议头中总是包含确认号的,如果能的话,就将数据一起捎带回去,这样网络利用率就提高了。延迟ACK就算没有数据捎带,那么如果收到了按序的两个包,那么只要对第二包做确认即可,这样也能省去一个ACK消耗。由于TCP协议不对ACK进行ACK的,RFC建议最多等待2个包的积累确认,这样能够及时通知对端Peer,我这边的接收情况。&/p&&p&Linux实现中,有延迟ACK和快速ACK,并根据当前的包的收发情况来在这两种ACK中切换。一般情况下,ACK并不会对网络性能有太大的影响,延迟ACK能减少发送的分段从而节省了带宽,而快速ACK能及时通知发送方丢包,避免滑动窗口停等,提升吞吐率。关于ACK分段,有个细节需要说明一下,ACK的确认号,是确认按序收到的最后一个字节序,对于乱序到来的TCP分段,接收端会回复相同的ACK分段,只确认按序到达的最后一个TCP分段。TCP连接的延迟确认时间一般初始化为最小值40ms,随后根据连接的重传超时时间(RTO)、上次收到数据包与本次接收数据包的时间间隔等参数进行不断调整。&/p&&h2&8. 疑症(8)TCP的重传机制以及重传的超时计算&/h2&&h4&【1】TCP的重传超时计算&/h4&&p&TCP交互过程中,如果发送的包一直没收到ACK确认,是要一直等下去吗?显然不能一直等(如果发送的包在路由过程中丢失了,对端都没收到又如何给你发送确认呢?),这样协议将不可用,既然不能一直等下去,那么该等多久呢?等太长时间的话,数据包都丢了很久了才重发,没有效率,性能差;等太短时间的话,可能ACK还在路上快到了,这时候却重传了,造成浪费,同时过多的重传会造成网络拥塞,进一步加剧数据的丢失。也是,我们不能去猜测一个重传超时时间,应该是通过一个算法去计算,并且这个超时时间应该是随着网络的状况在变化的。为了使我们的重传机制更高效,如果我们能够比较准确知道在当前网络状况下,一个数据包从发出去到回来的时间RTT——Round Trip Time,那么根据这个RTT我们就可以方便设置TimeOut——RTO(Retransmission TimeOut)了。&br&为了计算这个RTO,RFC793中定义了一个经典算法,算法如下:&/p&&div class=&highlight&&&pre&&code class=&language-text&&&span&&/span&[1] 首先采样计算RTT值
[2] 然后计算平滑的RTT,称为Smoothed Round Trip Time (SRTT),SRTT = ( ALPHA * SRTT ) + ((1-ALPHA) * RTT)
[3] RTO = min[UBOUND,max[LBOUND,(BETA*SRTT)]]
&/code&&/pre&&/div&&p&其中:UBOUND是RTO值的上限;例如:可以定义为1分钟,LBOUND是RTO值的下限,例如,可以定义为1秒;ALPHA is a smoothing factor (e.g., .8 to .9), and BETA is a delay variance factor (e.g., 1.3 to 2.0). 然而这个算法有个缺点就是:在算RTT样本的时候,是用第一次发数据的时间和ack回来的时间做RTT样本值,还是用重传的时间和ACK回来的时间做RTT样本值?不管是怎么选择,总会造成会要么把RTT算过长了,要么把RTT算过短了。如下图:(a)就计算过长了,而(b)就是计算过短了。&br&&br&&/p&&figure&&img src=&https://pic1.zhimg.com/v2-85cb32ba7f2aace636d6695_b.jpg& data-rawwidth=&745& data-rawheight=&332& class=&origin_image zh-lightbox-thumb& width=&745& data-original=&https://pic1.zhimg.com/v2-85cb32ba7f2aace636d6695_r.jpg&&&/figure&&p&针对上面经典算法的缺陷,于是提出Karn / Partridge Algorithm对经典算法进行了改进(算法大特点是——忽略重传,不把重传的RTT做采样),但是这个算法有问题:如果在某一时间,网络闪动,突然变慢了,产生了比较大的延时,这个延时导致要重转所有的包(因为之前的RTO很小),于是,因为重转的不算,所以,RTO就不会被更新,这是一个灾难。于是,为解决上面两个算法的问题,又有人推出来了一个新的算法,这个算法叫Jacobson / Karels Algorithm(参看RFC6289),这个算法的核心是:除了考虑每两次测量值的偏差之外,其变化率也应该考虑在内,如果变化率过大,则通过以变化率为自变量的函数为主计算RTT(如果陡然增大,则取值为比较大的正数,如果陡然减小,则取值为比较小的负数,然后和平均值加权求和),反之如果变化率很小,则取测量平均值。&br&公式如下:(其中的DevRTT是Deviation RTT的意思)&/p&&div class=&highlight&&&pre&&code class=&language-text&&&span&&/span&SRTT = SRTT + α (RTT – SRTT)
—— 计算平滑RTT
DevRTT = (1-β)*DevRTT + β*(|RTT-SRTT|) ——计算平滑RTT和真实的差距(加权移动平均)
RTO= u * SRTT + ? *DevRTT —— 神一样的公式
(其中:在Linux下,α = 0.125,β = 0.25, μ = 1,? = 4 ——这就是算法中的“调得一手好参数”,nobody knows why, it just works…) 最后的这个算法在被用在今天的TCP协议中并工作非常好
&/code&&/pre&&/div&&p&知道超时怎么计算后,很自然就想到定时器的设计问题。一个简单直观的方案就是为TCP中的每一个数据包维护一个定时器,在这个定时器到期前没收到确认,则进行重传。这种设计理论上是很合理的,但是实现上,这种方案将会有非常多的定时器,会带来巨大内存开销和调度开销。既然不能每个包一个定时器,那么多少个包一个定时器才好呢,这个似乎比较难确定。可以换个思路,不要以包量来确定定时器,以连接来确定定时器会不会比较合理呢?目前,采取每一个TCP连接单一超时定时器的设计则成了一个默认的选择,并且RFC2988给出了每连接单一定时器的设计建议算法规则:&/p&&div class=&highlight&&&pre&&code class=&language-text&&&span&&/span&[1]. 每一次一个包含数据的包被发送(包括重发),如果还没开启重传定时器,则开启它,使得它在RTO秒之后超时(按照当前的RTO值)。
[2]. 当接收到一个ACK确认一个新的数据, 如果所有的发出数据都被确认了,关闭重传定时器。
[3]. 当接收到一个ACK确认一个新的数据,还有数据在传输,也就是还有没被确认的数据,重新启动重传定时器,使得它在RTO秒之后超时(按照当前的RTO值)。
当重传定时器超时后,依次做下列3件事情:
[4.1]. 重传最早的尚未被TCP接收方ACK的数据包
[4.2]. 重新设置RTO 为 RTO * 2(“还原定时器”),但是新RTO不应该超过RTO的上限(RTO有个上限值,这个上限值最少为60s)
[4.3]. 重启重传定时器。
&/code&&/pre&&/div&&p&上面的建议算法体现了一个原则:没被确认的包必须可以超时,并且超时的时间不能太长,同时也不要过早重传。规则[1][3][4.3]共同说明了只要还有数据包没被确认,那么定时器一定会是开启着的(这样满足 没被确认的包必须可以超时的原则)。规则[4.2]说明定时器的超时值是有上限的(满足 超时的时间不能太长 )。规则[3]说明,在一个ACK到来后重置定时器可以保护后发的数据不被过早重传;因为一个ACK到来了,说明后续的ACK很可能会依次到来,也就是说丢失的可能性并不大。规则[4.2]也是在一定程度上避免过早重传,因为,在出现定时器超时后,有可能是网络出现拥塞了,这个时候应该延长定时器,避免出现大量的重传进一步加剧网络的拥塞。&/p&&h4&【2】TCP的重传机制&/h4&&p&通过上面我们可以知道,TCP的重传是由超时触发的,这会引发一个重传选择问题,假设TCP发送端连续发了1、2、3、4、5、6、7、8、9、10共10包,其中4、6、8这3个包全丢失了,由于TCP的ACK是确认最后连续收到序号,这样发送端只能收到3号包的ACK,这样在TIME_OUT的时候,发送端就面临下面两个重传选择:&/p&&div class=&highlight&&&pre&&code class=&language-text&&&span&&/span&[1].仅重传4号包
[2].重传3号后面所有的包,也就是重传4~10号包
&/code&&/pre&&/div&&p&对于,上面两个选择的优缺点都比较明显。方案[1],优点:按需重传,能够最大程度节省带宽。缺点:重传会比较慢,因为重传4号包后,需要等下一个超时才会重传6号包。方案[2],优点:重传较快,数据能够较快交付给接收端。缺点:重传了很多不必要重传的包,浪费带宽,在出现丢包的时候,一般是网络拥塞,大量的重传又可能进一步加剧拥塞。&br&上面的问题是由于单纯以时间驱动来进行重传的,都必须等待一个超时时间,不能快速对当前网络状况做出响应,如果加入以数据驱动呢?TCP引入了一种叫Fast Retransmit(快速重传 )的算法,就是在连续收到3次相同确认号的ACK,那么就进行重传。这个算法基于这么一个假设,连续收到3个相同的ACK,那么说明当前的网络状况变好了,可以重传丢失的包了。&br&快速重传解决了timeout的问题,但是没解决重传一个还是重传多个的问题。出现难以决定是否重传多个包问题的根源在于,发送端不知道那些非连续序号的包已经到达接收端了,但是接收端是知道的,如果接收端告诉一下发送端不就可以解决这个问题吗?于是,RFC2018提出了Selective Acknowledgment (SACK,选择确认)机制,SACK是TCP的扩展选项,包括(1)SACK允许选项(Kind=4,Length=2,选项只允许在有SYN标志的TCP包中),(2)SACK信息选项(Kind=5,Length)。一个SACK的例子如下图,红框说明:接收端收到了0----6500的数据了,这样发送端就可以选择重传丢失的,,的包。&br&&figure&&img src=&https://pic1.zhimg.com/v2-ab22eccce41f1a509a62c3_b.jpg& data-rawwidth=&1163& data-rawheight=&438& class=&origin_image zh-lightbox-thumb& width=&1163& data-original=&https://pic1.zhimg.com/v2-ab22eccce41f1a509a62c3_r.jpg&&&/figure&&/p&&p&SACK依靠接收端的接收情况反馈,解决了重传风暴问题,这样够了吗?接收端能不能反馈更多的信息呢?显然是可以的,于是,RFC2883对对SACK进行了扩展,提出了D-SACK,也就是利用第一块SACK数据中描述重复接收的不连续数据块的序列号参数,其他SACK数据则描述其他正常接收到的不连续数据。这样发送方利用第一块SACK,可以发现数据段被网络复制、错误重传、ACK丢失引起的重传、重传超时等异常的网络状况,使得发送端能更好调整自己的重传策略。D-SACK,有几个优点:&/p&&div class=&highlight&&&pre&&code class=&language-text&&&span&&/span&1)发送端可以判断出,是发包丢失了,还是接收端的ACK丢失了。(发送方,重传了一个包,发现并没有D-SACK那个包,那么就是发送的数据包丢了;否则就是接收端的ACK丢了,或者是发送的包延迟到达了)
2)发送端可以判断自己的RTO是不是有点小了,导致过早重传(如果收到比较多的D-SACK就该怀疑是RTO小了)。
3)发送端可以判断自己的数据包是不是被复制了。(如果明明没有重传该数据包,但是收到该数据包的D-SACK)
4)发送端可以判断目前网络上是不是出现了有些包被delay了,也就是出现先发的包却后到了。
&/code&&/pre&&/div&&h2&9. 疑症(9)TCP的流量控制&/h2&&p&我们知道TCP的窗口(window)是一个16bit位字段,它代表的是窗口的字节容量,也就是TCP的标准窗口最大为2^16-1=65535个字节。另外在TCP的选项字段中还包含了一个TCP窗口扩大因子,option-kind为3,option-length为3个字节,option-data取值范围0-14。窗口扩大因子用来扩大TCP窗口,可把原来16bit的窗口,扩大为31bit。这个窗口是接收端告诉发送端自己还有多少缓冲区可以接收数据。于是发送端就可以根据这个接收端的处理能力来发送数据,而不会导致接收端处理不过来。也就是,发送端是根据接收端通知的窗口大小来调整自己的发送速率的,以达到端到端的流量控制。尽管流量控制看起来简单明了,就是发送端根据接收端的限制来控制自己的发送就好了,但是细心的同学还是会有些疑问的。&/p&&div class=&highlight&&&pre&&code class=&language-text&&&span&&/span&1)发送端是怎么做到比较方便知道自己哪些包可以发,哪些包不能发呢?
2)如果接收端通知一个零窗口给发送端,这个时候发送端还能不能发送数据呢?如果不发数据,那一直等接收端口通知一个非0窗口吗,如果接收端一直不通知呢?
3)如果接收端处理能力很慢,这样接收端的窗口很快被填满,然后接收处理完几个字节,腾出几个字节的窗口后,通知发送端,这个时候发送端马上就发送几个字节给接收端吗?发送的话会不会太浪费了,就像一艘万吨油轮只装上几斤的油就开去目的地一样。对于发送端产生数据的能力很弱也一样,如果发送端慢吞吞产生几个字节的数据要发送,这个时候该不该立即发送呢?还是累积多点在发送?
&/code&&/pre&&/div&&h4&【1】.疑问1)的解决:&/h4&&p&发送方要知道那些可以发,哪些不可以发,一个简明的方案就是按照接收方的窗口通告,发送方维护一个一样大小的发送窗口就可以了,在窗口内的可以发,窗口外的不可以发,窗口在发送序列上不断后移,这就是TCP中的滑动窗口。如下图所示,对于TCP发送端其发送缓存内的数据都可以分为4类&/p&&div class=&highlight&&&pre&&code class=&language-text&&&span&&/span&[1]-已经发送并得到接收端ACK的;
[2]-已经发送但还未收到接收端ACK的;
[3]-未发送但允许发送的(接收方还有空间);
[4]-未发送且不允许发送(接收方没空间了)。
其中,[2]和[3]两部分合起来称之为发送窗口。
&/code&&/pre&&/div&&figure&&img src=&https://pic2.zhimg.com/v2-fca43d210df50c93e428dfd04fbbbf32_b.jpg& data-rawwidth=&660& data-rawheight=&270& class=&origin_image zh-lightbox-thumb& width=&660& data-original=&https://pic2.zhimg.com/v2-fca43d210df50c93e428dfd04fbbbf32_r.jpg&&&/figure&&p&下面两图演示的窗口的滑动情况,收到36的ACK后,窗口向后滑动5个byte。&br&&figure&&img src=&https://pic4.zhimg.com/v2-65ca805b9a6d731d2bc3bc9a83b6eb87_b.jpg& data-rawwidth=&660& data-rawheight=&210& class=&origin_image zh-lightbox-thumb& width=&660& data-original=&https://pic4.zhimg.com/v2-65ca805b9a6d731d2bc3bc9a83b6eb87_r.jpg&&&/figure&&/p&&figure&&img src=&https://pic4.zhimg.com/v2-4c22a2b58db2f0b885a0dc_b.jpg& data-rawwidth=&660& data-rawheight=&210& class=&origin_image zh-lightbox-thumb& width=&660& data-original=&https://pic4.zhimg.com/v2-4c22a2b58db2f0b885a0dc_r.jpg&&&/figure&&h4&【2】.疑问2)的解决&/h4&&p&由问题1)我们知道,发送端的发送窗口是由接收端控制的。下图,展示了一个发送端是怎么受接收端控制的。&br&&figure&&img src=&https://pic2.zhimg.com/v2-dc8ff42a8ac36c3f64b06f_b.jpg& data-rawwidth=&666& data-rawheight=&836& class=&origin_image zh-lightbox-thumb& width=&666& data-original=&https://pic2.zhimg.com/v2-dc8ff42a8ac36c3f64b06f_r.jpg&&&/figure&&/p&&p&由上图我们知道,当接收端通知一个zero窗口的时候,发送端的发送窗口也变成了0,也就是发送端不能发数据了。如果发送端一直等待,直到接收端通知一个非零窗口在发数据的话,这似乎太受限于接收端,如果接收端一直不通知新的窗口呢?显然发送端不能干等,起码有一个主动探测的机制。为解决0窗口的问题,TCP使用了Zero Window Probe技术,缩写为ZWP。&/p&&p&发送端在窗口变成0后,会发ZWP的包给接收方,来探测目前接收端的窗口大小,一般这个值会设置成3次,每次大约30-60秒(不同的实现可能会不一样)。如果3次过后还是0的话,有的TCP实现就会发RST掉这个连接。正如有人的地方就会有商机,那么有等待的地方就很有可能出现DDoS攻击点。攻击者可以在和Server建立好连接后,就向Server通告一个0窗口,然后Server端就只能等待进行ZWP,于是攻击者会并发大量的这样的请求,把Server端的资源耗尽。&/p&&h4&【3】疑问点3)的解决&/h4&&p&疑点3)本质就是一个避免发送大量小包的问题。造成这个问题原因有二:1)接收端一直在通知一个小的窗口; 2)发送端本身问题,一直在发送小包。这个问题,TCP中有个术语叫Silly Window Syndrome(糊涂窗口综合症)。解决这个问题的思路有两,1)接收端不通知小窗口,2)发送端积累一下数据在发送。&br&思路1)是在接收端解决这个问题,David D Clark’s 方案,如果收到的数据导致window size小于某个值,就ACK一个0窗口,这就阻止发送端在发数据过来。等到接收端处理了一些数据后windows size 大于等于了MSS,或者buffer有一半为空,就可以通告一个非0窗口。思路2)是在发送端解决这个问题,有个著名的Nagle’s algorithm。Nagle 算法的规则&/p&&div class=&highlight&&&pre&&code class=&language-text&&&span&&/span& [1]如果包长度达到 MSS ,则允许发送;
[2]如果该包含有 FIN ,则允许发送;
[3]设置了 TCP_NODELAY 选项,则允许发送;
[4]设置 TCP_CORK 选项时,若所有发出去的小数据包(包长度小于 MSS )均被确认,则允许发送;
[5]上述条件都未满足,但发生了超时(一般为 200ms ),则立即发送。
&/code&&/pre&&/div&&p&规则[4]指出TCP连接上最多只能有一个未被确认的小数据包。从规则[4]可以看出Nagle算法并不禁止发送小的数据包(超时时间内),而是避免发送大量小的数据包。由于Nagle算法是依赖ACK的,如果ACK很快的话,也会出现一直发小包的情况,造成网络利用率低。TCP_CORK选项则是禁止发送小的数据包(超时时间内),设置该选项后,TCP会尽力把小数据包拼接成一个大的数据包(一个 MTU)再发送出去,当然也不会一直等,发生了超时(一般为 200ms ),也立即发送。Nagle 算法和CP_CORK 选项提高了网络的利用率,但是增加是延时。从规则[3]可以看出,设置TCP_NODELAY 选项,就是完全禁用Nagle 算法了。&/p&&p&这里要说一个小插曲,Nagle算法和延迟确认(Delayed Acknoledgement)一起,当出现( write-write-read)的时候会引发一个40ms的延时问题,这个问题在HTTP svr中体现的比较明显。场景如下:&br&客户端在请求下载HTTP svr中的一个小文件,一般情况下,HTTP svr都是先发送HTTP响应头部,然后在发送HTTP响应BODY(特别是比较多的实现在发送文件的实施采用的是sendfile系统调用,这就出现write-write-read模式了)。当发送头部的时候,由于头部较小,于是形成一个小的TCP包发送到客户端,这个时候开始发送body,由于body也较小,这样还是形成一个小的TCP数据包,根据Nagle算法,HTTP svr已经发送一个小的数据包了,在收到第一个小包的ACK后或等待200ms超时后才能在发小包,HTTP svr不能发送这个body小TCP包;&/p&&p&&br&客户端收到http响应头后,由于这是一个小的TCP包,于是客户端开启延迟确认,客户端在等待Svr的第二个包来在一起确认或等待一个超时(一般是40ms)在发送ACK包;这样就出现了你等我、然而我也在等你的死锁状态,于是出现最多的情况是客户端等待一个40ms的超时,然后发送ACK给HTTP svr,HTTP svr收到ACK包后在发送body部分。大家在测HTTP svr的时候就要留意这个问题了。&/p&&h2&10. 疑症(10)TCP的拥塞控制&/h2&&p&谈到拥塞控制,就要先谈谈拥塞的因素和本质。本质上,网络上拥塞的原因就是大家都想独享整个网络资源,对于TCP,端到端的流量控制必然会导致网络拥堵。这是因为TCP只看到对端的接收空间的大小,而无法知道链路上的容量,只要双方的处理能力很强,那么就可以以很大的速率发包,于是链路很快出现拥堵,进而引起大量的丢包,丢包又引发发送端的重传风暴,进一步加剧链路的拥塞。另外一个拥塞的因素是链路上的转发节点,例如路由器,再好的路由器只要接入网络,总是会拉低网络的总带宽,如果在路由器节点上出现处理瓶颈,那么就很容易出现拥塞。由于TCP看不到网络的状况,那么拥塞控制是必须的并且需要采用试探性的方式来控制拥塞,于是拥塞控制要完成两个任务:[1]公平性;[2]拥塞过后的恢复。&/p&&p&&br&TCP发展到现在,拥塞控制方面的算法很多,其中Reno是目前应用最广泛且较为成熟的算法,下面着重介绍一下Reno算法(RFC5681)。介绍该算法前,首先介绍一个概念duplicate acknowledgment(冗余ACK、重复ACK)一般情况下一个ACK被称为冗余ACK,要同时满足下面几个条件(对于SACK,那么根据SACK的一些信息来进一步判断)&/p&&div class=&highlight&&&pre&&code class=&language-text&&&span&&/span&[1] 接收ACK的那端已经发出了一些还没被ACK的数据包
[2] 该ACK没有捎带data
[3] 该ACK的SYN和FIN位都是off的,也就是既不是SYN包的ACK也不是FIN包的ACK。
[4] 该ACK的确认号等于接收ACK那端已经收到的ACK的最大确认号
[5] 该ACK通知的窗口等接收该ACK的那端上一个收到的ACK的窗口
&/code&&/pre&&/div&&p&Reno算法包含4个部分:[1]慢热启动算法 – Slow S [2]拥塞避免算法 – Congestion A [3]快速重传 - Fast R [4]快速恢复算法 – Fast Recovery。TCP的拥塞控制主要原理依赖于一个拥塞窗口(cwnd)来控制,根据前面的讨论,我们知道有一个接收端通告的接收窗口(rwnd)用于流量控制;加上拥塞控制后,发送端真正的发送窗口=min(rwnd, cwnd)。关于cwnd的单位,在TCP中是以字节来做单位的,我们假设TCP每次传输都是按照MSS大小来发送数据,因此你可以认为cwnd按照数据包个数来做单位也可以理解,下面如果没有特别说明是字节,那么cwnd增加1也就是相当于字节数增加1个MSS大小。&/p&&h4&【1】慢热启动算法 – Slow Start&/h4&&p&慢启动体现了一个试探的过程,刚接入网络的时候先发包慢点,探测一下网络情况,然后在慢慢提速。不要一上来就拼命发包,这样很容易造成链路的拥堵,出现拥堵了在想到要降速来缓解拥堵这就有点成本高了,毕竟无数的先例告诫我们先污染后治理的成本是很高的。慢启动的算法如下(cwnd全称Congestion Window):&/p&&div class=&highlight&&&pre&&code class=&language-text&&&span&&/span&1)连接建好的开始先初始化cwnd = N,表明可以传N个MSS大小的数据。
2)每当收到一个ACK,++ 呈线性上升
3)每当过了一个RTT,cwnd = cwnd*2; 呈指数让升
4)还有一个慢启动门限ssthresh(slow start threshold),是一个上限,当cwnd &= ssthresh时,就会进入&拥塞避免算法 - Congestion Avoidance&
&/code&&/pre&&/div&&p&根据RFC5681,如果MSS & 2190 bytes,则N = 2;如果MSS & 1095 bytes,则N = 4;如果2190 bytes &= MSS &= 1095 bytes,则N = 3;一篇Google的论文《An Argument for Increasing TCP’s Initial Congestion Window》建议把cwnd 初始化成了 10个MSS。Linux 3.0后采用了这篇论文的建议。&/p&&h4&【2】拥塞避免算法 – Congestion Avoidance&/h4&&p&慢启动的时候说过,cwnd是指数快速增长的,但是增长是有个门限ssthresh(一般来说大多数的实现ssthresh的值是65535字节)的,到达门限后进入拥塞避免阶段。在进入拥塞避免阶段后,cwnd值变化算法如下:&/p&&div class=&highlight&&&pre&&code class=&language-text&&&span&&/span& 1)每收到一个ACK,调整cwnd 为 (cwnd + 1/cwnd) * MSS个字节
2)每经过一个RTT的时长,cwnd增加1个MSS大小。
&/code&&/pre&&/div&&p&TCP是看不到网络的整体状况的,那么TCP认为网络拥塞的主要依据是它重传了报文段。前面我们说过TCP的重传分两种情况:&/p&&div class=&highlight&&&pre&&code class=&language-text&&&span&&/span& 1)出现RTO超时,重传数据包。这种情况下,TCP就认为出现拥塞的可能性就很大,于是它反应非常'强烈'
[1] 调整门限ssthresh的值为当前cwnd值的1/2。
[2] reset自己的cwnd值为1
[3] 然后重新进入慢启动过程。
2)在RTO超时前,收到3个duplicate ACK进行重传数据包。这种情况下,收到3个冗余ACK后说明确实有中间的分段丢失,然而后面的分段确实到达了接收端,因为这样才会发送冗余ACK,这一般是路由器故障或者轻度拥塞或者其它不太严重的原因引起的,因此此时拥塞窗口缩小的幅度就不能太大,此时进入快速重传。
&/code&&/pre&&/div&&h4&【3】快速重传 - Fast Retransimit 做的事情有:&/h4&&div class=&highlight&&&pre&&code class=&language-text&&&span&&/span&1) 调整门限ssthresh的值为当前cwnd值的1/2。
2) 将cwnd值设置为新的ssthresh的值
3) 重新进入拥塞避免阶段。
&/code&&/pre&&/div&&p&在快速重传的时候,一般网络只是轻微拥堵,在进入拥塞避免后,cwnd恢复的比较慢。针对这个,“快速恢复”算法被添加进来,当收到3个冗余ACK时,TCP最后的[3]步骤进入的不是拥塞避免阶段,而是快速恢复阶段。&/p&&h4&【4】快速恢复算法 – Fast Recovery :&/h4&&p&快速恢复的思想是“数据包守恒”原则,即带宽不变的情况下,在网络同一时刻能容纳数据包数量是恒定的。当“老”数据包离开了网络后,就能向网络中发送一个“新”的数据包。既然已经收到了3个冗余ACK,说明有三个数据分段已经到达了接收端,既然三个分段已经离开了网络,那么就是说可以在发送3个分段了。于是只要发送方收到一个冗余的ACK,于是cwnd加1个MSS。快速恢复步骤如下(在进入快速恢复前,cwnd 和 sshthresh已被更新为:sshthresh = cwnd /2,cwnd = sshthresh):&/p&&div class=&highlight&&&pre&&code class=&language-text&&&span&&/span&1)把cwnd设置为ssthresh的值加3,重传Duplicated ACKs指定的数据包
2)如果再收到 duplicated Acks,那么cwnd = cwnd +1
3)如果收到新的ACK,而非duplicated Ack,那么将cwnd重新设置为【3】中1)的sshthresh的值。然后进入拥塞避免状态。
&/code&&/pre&&/div&&p&细心的同学可能会发现快速恢复有个比较明显的缺陷就是:它依赖于3个冗余ACK,并假定很多情况下,3个冗余的ACK只代表丢失一个包。但是3个冗余ACK也很有可能是丢失了很多个包,快速恢复只是重传了一个包,然后其他丢失的包就只能等待到RTO超时了。超时会导致ssthresh减半,并且退出了Fast Recovery阶段,多个超时会导致TCP传输速率呈级数下降。出现这个问题的主要原因是过早退出了Fast Recovery阶段。为解决这个问题,提出了New Reno算法,该算法是在没有SACK的支持下改进Fast Recovery算法(SACK改变TCP的确认机制,把乱序等信息会全部告诉对方,SACK本身携带的信息就可以使得发送方有足够的信息来知道需要重传哪些包,而不需要重传哪些包),具体改进如下:&/p&&div class=&highlight&&&pre&&code class=&language-text&&&span&&/span&1)发送端收到3个冗余ACK后,重传冗余ACK指示可能丢失的那个包segment1,如果segment1的ACK通告接收端已经收到发送端的全部已经发出的数据的话,那么就是只丢失一个包,如果没有,那么就是有多个包丢失了。
2)发送端根据segment1的ACK判断出有多个包丢失,那么发送端继续重传窗口内未被ACK的第一个包,直到sliding window内发出去的包全被ACK了,才真正退出Fast Recovery阶段。
&/code&&/pre&&/div&&p&我们可以看到,拥塞控制在拥塞避免阶段,cwnd是加性增加的,在判断出现拥塞的时候采取的是指数递减。为什么要这样做呢?这是出于公平性的原则,拥塞窗口的增加受惠的只是自己,而拥塞窗口减少受益的是大家。这种指数递减的方式实现了公平性,一旦出现丢包,那么立即减半退避,可以给其他新建的连接腾出足够的带宽空间,从而保证整个的公平性。&/p&&p&至此,TCP的疑难杂症基本介绍完毕了,总的来说TCP是一个有连接的、可靠的、带流量控制和拥塞控制的端到端的协议。TCP的发送端能发多少数据,由发送端的发送窗口决定(当然发送窗口又被接收端的接收窗口、发送端的拥塞窗口限制)的,那么一个TCP连接的传输稳定状态应该体现在发送端的发送窗口的稳定状态上,这样的话,TCP的发送窗口有哪些稳定状态呢?TCP的发送窗口稳定状态主要有上面三种稳定状态:&/p&&h4&【1】接收端拥有大窗口的经典锯齿状&/h4&&p&大多数情况下都是处于这样的稳定状态,这是因为,一般情况下机器的处理速度就是比较快,这样TCP的接收端都是拥有较大的窗口,这时发送端的发送窗口就完全由其拥塞窗口cwnd决定了;网络上拥有成千上万的TCP连接,它们在相互争用网络带宽,TCP的流量控制使得它想要独享整个网络,而拥塞控制又限制其必要时做出牺牲来体现公平性。于是在传输稳定的时候TCP发送端呈现出下面过程的反复&/p&&div class=&highlight&&&pre&&code class=&language-text&&&span&&/span&[1]用慢启动或者拥塞避免方式不断增加其拥塞窗口,直到丢包的发生;
[2]然后将发送窗口将下降到1或者下降一半,进入慢启动或者拥塞避免阶段(要看是由于超时丢包还是由于冗余ACK丢包);过程如下图:
&/code&&/pre&&/div&&figure&&img src=&https://pic4.zhimg.com/v2-4cdab334fda537cc71b20bead1674c0e_b.jpg& data-rawwidth=&1024& data-rawheight=&359& class=&origin_image zh-lightbox-thumb& width=&1024& data-original=&https://pic4.zhimg.com/v2-4cdab334fda537cc71b20bead1674c0e_r.jpg&&&/figure&&h4&【2】接收端拥有小窗口的直线状态&/h4&&p&这种情况下是接收端非常慢速,接收窗口一直很小,这样发送窗口就完全有接收窗口决定了。由于发送窗口小,发送数据少,网络就不会出现拥塞了,于是发送窗口就一直稳定的等于那个较小的接收窗口,呈直线状态。&/p&&h4&【3】两个直连网络端点间的满载状态下的直线状态&/h4&&p&这种情况下,Peer两端直连,并且只有位于一个TCP连接,那么这个连接将独享网络带宽,这里不存在拥塞问题,在他们处理能力足够的情况下,TCP的流量控制使得他们能够跑慢整个网络带宽。&/p&&p&通过上面我们知道,在TCP传输稳定的时候,各个TCP连接会均分网络带宽的。相信大家学生时代经常会发生这样的场景,自己在看视频的时候突然出现视频卡顿,于是就大叫起来,哪个开了迅雷,赶紧给我停了。其实简单的下载加速就是开启多个TCP连接来分段下载就达到加速的效果,假设宿舍的带宽是1000K/s,一开始两个在看视频,每人平均网速是500k/s,这速度看起视频来那叫一个顺溜。突然其中一个同学打打开迅雷开着99个TCP连接在下载爱情动作片,这个时候平均下来你能分到的带宽就剩下10k/s,这网速下你的视频还不卡成幻灯片。在通信链路带宽固定(假设为W),多人公用一个网络带宽的情况下,利用TCP协议的拥塞控制的公平性,多开几个TCP连接就能多分到一些带宽(当然要忽略有些用UDP协议带来的影响),然而不管怎么最多也就能把整个带宽抢到,于是在占满整个带宽的情况下,下载一个大小为FS的文件,那么最快需要的时间是FS/W,难道就没办法加速了吗?&/p&&p&答案是有的,这样因为网络是网状的,一个节点是要和很多几点互联的,这就存在多个带宽为W的通信链路,如果我们能够将要下载的文件,一半从A通信链路下载,另外一半从B通信链路下载,这样整个下载时间就减半了为FS/(2W),这就是p2p加速。相信大家学生时代在下载爱情动作片的时候也遇到过这种情况,明明外网速度没这么快的,自己下载的爱情动作片的速度却达到几M/s,那是因为,你的左后或右后的宿友在帮你加速中。我们都知道P2P模式下载会快,并且越多人下载就越快,那么问题来了,P2P下载加速理论上的加速比是多少呢?&/p&&h2&11.附加题1:P2P理论上的加速比&/h2&&p&传统的C/S模式传输文件,在跑满Client带宽的情况下传输一个文件需要耗时FS/BW,如果有n个客户端需要下载文件,那么总耗时是n(FS/BW),当然啦,这并不一定是串行传输,可以并行来传输的,这样总耗时也就是FS/BW了,但是这需要服务器的带宽是n个client带宽的总和nBW。C/S模式一个明显的缺点是服务要传输一个文件n次,这样对服务器的性能和带宽带来比较大的压力,我可以换下思路,服务器将文件传给其中一个Client后,让这些互联的Client自己来交互那个文件,那服务器的压力就减少很多了。这就是P2P网络的好处,P2P利用各个节点间的互联,提倡“人人为我,我为人人”。&br&知道P2P传输的好处后,我们来谈下理论上的最大加速比,为了简化讨论,一个简单的网络拓扑图如下,有4个相互互联的节点,并且每个节点间的网络带宽是BW,传输一个大小为FS的文件最快的时间是多少呢?假设节点N1有个大小为FS的文件需要传输给N2,N3,N4节点,一种简单的方式就是:节点N1同时将文件传输给节点N2,N3,N4耗时FS/BW,这样大家都拥有文件FS了。大家可以看出,整个过程只有节点1在发送文件,其他节点都是在接收,完全违反了P2P的“人人为我,我为人人”的宗旨。那怎么才能让大家都做出贡献了呢?解决方案是切割文件。&/p&&div class=&highlight&&&pre&&code class=&language-text&&&span&&/span& [1]首先,节点N1 文件分成3个片段FS2,FS3,FS4 ,接着将FS2发送给N2,FS3发送给N3,FS4发送给N4,耗时FS/(3*BW);
[2]然后,N2,N3,N4执行“人人为我,我为人人”的精神,将自己拥有的F2,F3,F4分别发给没有的其他的节点,这样耗时FS/(3*BW)完成交换。
&/code&&/pre&&/div&&p&于是总耗时为2FS/(3BW)完成了文件FS的传输,可以看出耗时减少为原来的2/3了,如果有n个节点,那么时间就是原来的2/(n-1),也就是加速比是2/(n-1),这就是加速的理论上限了吗?还没发挥最多能量的,相信大家已经看到分割文件的好处了,上面的文件分割粒度还是有点大,以至于,在第二阶段[2]传输过程中,节点N1无所事事。为了最大化发挥大家的作用,我们需要将FS2,FS3,FS4在进行分割,假设将它们都均分为K等份,这样就有FS21,FS22…FS2K、FS31,FS32…FS3K、FS41,FS42…FS4K,一共3K个分段。于是下面就开始进行加速分发:&/p&&div class=&highlight&&&pre&&code class=&language-text&&&span&&/span&[1]节点N1将分段FS21,FS31,FS41分别发送给N2,N3,N4节点。耗时,FS/(3K*BW)
[2]节点N1将分段FS22,FS32,FS42分别发送给N2,N3,N4节点,同时节点N2,N3,N4将阶段[1]收到的分段相互发给没有的节点。耗时,FS/(3K*BW)
。。。。。。
[K]节点N1将分段FS2K,FS3K,FS4K分别发送给N2,N3,N4节点,同时节点N2,N3,N4将阶段[K-1]收到的分段相互发给没有的节点。耗时,FS/(3K*BW)
[K+1]节点N2,N3,N4将阶段[K]收到的分段相互发给没有的节点。耗时,FS/(3K*BW)
&/code&&/pre&&/div&&p&于是总的耗时为(K+1) (FS/(3KBW)) = FS/(3BW) + FS/(3KBW),当K趋于无穷大的时候,文件进行无限细分的时候,耗时变成了FS/(3*BW),也就是当节点是n+1的时候,加速比是n。这就是理论上的最大加速比了,最大加速比是P2P网络节点个数减1。&br&&figure&&img src=&https://pic2.zhimg.com/v2-19c99dc59cbf_b.jpg& data-rawwidth=&611& data-rawheight=&432& class=&origin_image zh-lightbox-thumb& width=&611& data-original=&https://pic2.zhimg.com/v2-19c99dc59cbf_r.jpg&&&/figure&&/p&&h2&12.附加题2:系统调用listen() 的backlog参数指的是什么&/h2&&p&要说明backlog参数的含义,首先需要说一下Linux的协议栈维护的TCP连接的两个连接队列:[1]SYN半连接队列;[2]accept连接队列&/p&&div class=&highlight&&&pre&&code class=&language-text&&&span&&/span& [1]SYN半连接队列:Server端收到Client的SYN包并回复SYN,ACK包后,该连接的信息就会被移到一个队列,这个队列就是SYN半连接队列(此时TCP连接处于 非同步状态 )
[2]accept连接队列:Server端收到SYN,ACK包的ACK包后,就会将连接信息从[1]中的队列移到另外一个队列,这个队列就是accept连接队列(这个时候TCP连接已经建立,三次握手完成了)
用户进程调用accept()系统调用后,该连接信息就会从[2]中的队列中移走。
&/code&&/pre&&/div&&p&相信不少同学就backlog的具体含义进行争论过,有些认为backlog指的是[1]和[2]两个队列的和。而有些则认为是backlog指的是[2]的大小。其实,这两个说法都对,在linux kernel 2.2之前backlog指的是[1]和[2]两个队列的和。而2.2以后,就指的是[2]的大小,那么在kernel 2.2以后,[1]的大小怎么确定的呢?两个队列的作用分别是什么呢?&/p&&h4&【1】SYN半连接队列的作用&/h4&&p&对于SYN半连接队列的大小是由(/proc/sys/net/ipv4/tcp_max_syn_backlog)这个内核参数控制的,有些内核似乎也受listen的backlog参数影响,取得是两个值的最小值。当这个队列满了,Server会丢弃新来的SYN包,而Client端在多次重发SYN包得不到响应而返回(connection time out)错误。但是,当Server端开启了syncookies,那么SYN半连接队列就没有逻辑上的最大值了,并且/proc/sys/net/ipv4/tcp_max_syn_backlog设置的值也会被忽略。&/p&&h4&【2】accept连接队列&/h4&&p&accept连接队列的大小是由backlog参数和(/proc/sys/net/core/somaxconn)内核参数共同决定,取值为两个中的最小值。当accept连接队列满了,协议栈的行为根据(/proc/sys/net/ipv4/tcp_abort_on_overflow)内核参数而定。 如果tcp_abort_on_overflow=1,server在收到SYN_ACK的ACK包后,协议栈会丢弃该连接并回复RST包给对端,这个是Client会出现(connection reset by peer)错误。如果tcp_abort_on_overflow=0,server在收到SYN_ACK的ACK包后,直接丢弃该ACK包。这个时候Client认为连接已经建立了,一直在等Server的数据,直到超时出现read timeout错误。&/p&&p&&a href=&https://link.zhihu.com/?target=http%3A//www.51testing.com/html/48/574.html& class=& wrap external& target=&_blank& rel=&nofollow noreferrer&&loadrunner_http长连接设置&/a&&/p&&h2&参考资料&/h2&&p&&a href=&https://link.zhihu.com/?target=http%3A//blog.csdn.net/dog250/article/details/6612496& class=& wrap external& target=&_blank& rel=&nofollow noreferrer&&Netfilter,iptables/OpenVPN/TCP guard:-(&/a&&br&&a href=&https://link.zhihu.com/?target=http%3A//coolshell.cn/articles/11564.html& class=& wrap external& target=&_blank& rel=&nofollow noreferrer&&TCP 的那些事儿(上) | | 酷 壳 - CoolShell&/a&&br&&a href=&https://link.zhihu.com/?target=http%3A//coolshell.cn/articles/11609.html& class=& external& target=&_blank& rel=&nofollow noreferrer&&&span class=&invisible&&http://&/span&&span class=&visible&&coolshell.cn/articles/1&/span&&span class=&invisible&&1609.html&/span&&span class=&ellipsis&&&/span&&/a&&br&&a href=&https://link.zhihu.com/?target=http%3A//www.tcpipguide.com/free/t_TCPMessageSegmentFormat.htm& class=& wrap external& target=&_blank& rel=&nofollow noreferrer&&TCP Message (Segment) Format&/a&&/p&&p&&a href=&https://link.zhihu.com/?target=https%3A//www.qcloud.com/community/& class=& wrap external& target=&_blank& rel=&nofollow noreferrer&&腾云阁 - 腾讯云&/a&&/p&
作者介绍:黄日成,手Q游戏中心后台开发,腾讯高级工程师。从事C++服务后台开发4年多,主要负责手Q游戏中心后台基础系统、复杂业务系统开发,主导过手Q游戏公会、企鹅电竞App-对战系统等项目的后台系统设计,有丰富的后台架构经验。本文获得了小时光茶社的…
&blockquote&作者介绍:黄日成,手Q游戏中心后台开发,腾讯高级工程师。从事C++服务后台开发4年多,主要负责手Q游戏中心后台基础系统、复杂业务系统开发,主导过手Q游戏公会、企鹅电竞App-对战系统等项目的后台系统设计,有丰富的后台架构经验。本文获得了小时光茶社的授权&/blockquote&&p&说到TCP协议,相信大家都比较熟悉了,对于TCP协议总能说个一二三来,但是TCP协议又是一个非常复杂的协议,其中有不少细节点让人头疼点。本文就是来说说这些头疼点的,浅谈一些TCP的疑难杂症。那么从哪说起呢?当然是从三次握手和四次挥手说起啦,可能大家都知道TCP是三次交互完成连接的建立,四次交互来断开一个连接,那为什么是三次握手和四次挥手呢?反过来不行吗?&/p&&h4&1. 疑症(1)TCP的三次握手、四次挥手&/h4&&p&下面两图大家再熟悉不过了,TCP的三次握手和四次挥手见下面左边的”TCP建立连接”、”TCP数据传送”、”TCP断开连接”时序图和右边的”TCP协议状态机”&/p&&p&&figure&&img src=&https://pic2.zhimg.com/v2-afdab8207d64_b.jpg& data-rawwidth=&875& data-rawheight=&976& class=&origin_image zh-lightbox-thumb& width=&875& data-original=&https://pic2.zhimg.com/v2-afdab8207d64_r.jpg&&&/figure&图1:TCP三次握手、四次挥手时序图&/p&&p&&figure&&img src=&https://pic3.zhimg.com/v2-665b70b42ff06ae083c89_b.jpg& data-rawwidth=&562& data-rawheight=&801& class=&origin_image zh-lightbox-thumb& width=&562& data-original=&https://pic3.zhimg.com/v2-665b70b42ff06ae083c89_r.jpg&&&/figure&图2:TCP协议状态机&/p&&p&要弄清TCP建立连接需要几次交互才行,我们需要弄清建立连接进行初始化的目标是什么。TCP进行握手初始化一个连接的目标是:分配资源、初始化序列号(通知peer对端我的初始序列号是多少),知道初始化连接的目标,那么要达成这个目标的过程就简单了,握手过程可以简化为下面的四次交互:&/p&&blockquote&&p&1)client端首先发送一个SYN包告诉Server端我的初始序列号是X。&br&2)Server端收到SYN包后回复给client一个ACK确认包,告诉client说我收到了。&br&3)接着Server端也需要告诉client端自己的初始序列号,于是Server也发送一个SYN包告诉client我的初始序列号是Y。&br&4)Client收到后,回复Server一个ACK确认包说我知道了。&/p&&/blockquote&&p&整个过程4次交互即可完成初始化,但是,细心的同学会发现两个问题:&br&[1]. Server发送SYN包是作为发起连接的SYN包,还是作为响应发起者的SYN包呢?怎么区分?比较容易引起混淆&br&[2].Server的ACK确认包和接下来的SYN包可以合成一个SYN ACK包一起发送的,没必要分别单独发送,这样省了一次交互同时也解决了问题[1]. 这样TCP建立一个连接,三次握手在进行最少次交互的情况下完成了Peer两端的资源分配和初始化序列号的交换。&/p&&p&大部分情况下建立连接需要三次握手,也不一定都是三次,有可能出现四次握手来建立连接的。如下图,当Peer两端同时发起SYN来建立连接的时候,就出现了四次握手来建立连接(对于有些TCP/IP的实现,可能不支持这种同时打开的情况)。&br&&/p&&figure&&img src=&https://pic3.zhimg.com/v2-763de0dafc8_b.jpg& data-rawwidth=&601& data-rawheight=&199& class=&origin_image zh-lightbox-thumb& width=&601& data-original=&https://pic3.zhimg.com/v2-763de0dafc8_r.jpg&&&/figure&&br&&br&&p&在三次握手过程中,细心的同学可能会有以下疑问:&br&(2). 初始化序列号X、Y是可以是写死固定的吗,为什么不能呢?&br&(3). 假如Client发送一个SYN包给Server后就挂了或是不管了,这个时候这个连接处于什么状态呢?会超时吗?为什么呢?&/p&&p&TCP进行断开连接的目标是:回收资源、终止数据传输。由于TCP是全双工的,需要Peer两端分别各自拆除自己通向Peer对端的方向的通信信道。这样需要四次挥手来分别拆除通信信道,就比较清晰明了了。&/p&&blockquote&&p&1)Client发送一个FIN包来告诉Server我已经没数据需要发给Server了。&br&2)Server收到后回复一个ACK确认包说我知道了。&br&3)然后server在自己也没数据发送给client后,Server也发送一个FIN包给Client告诉Client我也已经没数据发给client了。&br&4)Client收到后,就会回复一个ACK确认包说我知道了。&br&到此,四次挥手,这个TCP连接就可以完全拆除了。在四次挥手的过程中,细心的同学可能会有以下疑问:&/p&&/blockquote&&p&(4). Client和Server同时发起断开连接的FIN包会怎么样呢,TCP状态是怎么转移的?&br&(5). 左侧图中的四次挥手过程中,Server端的ACK确认包能不能和接下来的FIN包合并成一个包呢,这样四次挥手就变成三次挥手了。&br&(6). 四次挥手过程中,首先断开连接的一端,在回复最后一个ACK后,为什么要进行TIME_WAIT呢(超时设置是 2*MSL,RFC793定义了MSL为2分钟,Linux设置成了30s),在TIME_WAIT的时候又不能释放资源,白白让资源占用那么长时间,能不能省了TIME_WAIT呢,为什么?&/p&&h4&2. 疑症(2),TCP连接的初始化序列号能否固定&/h4&&p&如果初始化序列号(缩写为ISN:Inital Sequence Number)可以固定,我们来看看会出现什么问题。假设ISN固定是1,Client和Server建立好一条TCP连接后,Client连续给Server发了10个包,这10个包不知怎么被链路上的路由器缓存了(路由器会毫无先兆地缓存或者丢弃任何的数据包),这个时候碰巧Client挂掉了,然后Client用同样的端口号重新连上Server,Client又连续给Server发了几个包,假设这个时候Client的序列号变成了5。接着,之前被路由器缓存的10个数据包全部被路由到Server端了,Server给Client回复确认号10,这个时候,Client整个都不好了,这是什么情况?我的序列号才到5,你怎么给我的确认号是10了,整个都乱了。&br&RFC793中,建议ISN和一个假的时钟绑在一起,这个时钟会在每4微秒对ISN做加一操作,直到超过2^32,又从0开始,这需要4小时才会产生ISN的回绕问题,这几乎可以保证每个新连接的ISN不会和旧的连接的ISN产生冲突。这种递增方式的ISN,很容易让攻击者猜测到TCP连接的ISN,现在的实现大多是在一个基准值的基础上进行随机的。&/p&&h4&3. 疑症(3),初始化连接的SYN超时问题&/h4&&p&Client发送SYN包给Server后挂了,Server回给Client的SYN-ACK一直没收到Client的ACK确认,这个时候这个连接既没建立起来,也不能算失败。这就需要一个超时时间让Server将这个连接断开,否则这个连接就会一直占用Server的SYN连接队列中的一个位置,大量这样的连接就会将Server的SYN连接队列耗尽,让正常的连接无法得到处理。目前,Linux下默认会进行5次重发SYN-ACK包,重试的间隔时间从1s开始,下次的重试间隔时间是前一次的双倍,5次的重试时间间隔为1s, 2s, 4s, 8s, 16s,总共31s,第5次发出后还要等32s都知道第5次也超时了,所以,总共需要 1s + 2s + 4s+ 8s+ 16s + 32s = 63s,TCP才会把断开这个连接。由于,SYN超时需要63秒,那么就给攻击者一个攻击服务器的机会,攻击者在短时间内发送大量的SYN包给Server(俗称 SYN flood 攻击),用于耗尽Server的SYN队列。对于应对SYN 过多的问题,linux提供了几个TCP参数:tcp_syncookies、tcp_synack_retries、tcp_max_syn_backlog、tcp_abort_on_overflow 来调整应对。&/p&&h4&4. 疑症(4) ,TCP的Peer两端同时断开连接&/h4&&p&由上面的”TCP协议状态机 “图可以看出,TCP的Peer端在收到对端的FIN包前发出了FIN包,那么该Peer的状态就变成了FIN_WAIT1,Peer在FIN_WAIT1状态下收到对端Peer对自己FIN包的ACK包的话,那么Peer状态就变成FIN_WAIT2,Peer在FIN_WAIT2下收到对端Peer的FIN包,在确认已经收到了对端Peer全部的Data数据包后,就响应一个ACK给对端Peer,然后自己进入TIME_WAIT状态;但是如果Peer在FIN_WAIT1状态下首先收到对端Peer的FIN包的话,那么该Peer在确认已经收到了对端Peer全部的Data数据包后,就响应一个ACK给对端Peer,然后自己进入CLOSEING状态,Peer在CLOSEING状态下收到自己的FIN包的ACK包的话,那么就进入TIME WAIT 状态。于是,TCP的Peer两端同时发起FIN包进行断开连接,那么两端Peer可能出现完全一样的状态转移 FIN_WAIT1——&CLOSEING——-&TIME_WAIT,也就会Client和Server最后同时进入TIME_WAIT状态。同时关闭连接的状态转移如下图所示:&br&&figure&&img src=&https://pic1.zhimg.com/v2-75c4329226ceacb5cea6a5b61e7b1734_b.jpg& data-rawwidth=&666& data-rawheight=&526& class=&origin_image zh-lightbox-thumb& width=&666& data-original=&https://pic1.zhimg.com/v2-75c4329226ceacb5cea6a5b61e7b1734_r.jpg&&&/figure&&br&&/p&&h4&5. 疑症(5)四次挥手能不能变成三次挥手呢??&/h4&&p&答案是可能的。TCP是全双工通信,Cliet在自己已经不会在有新的数据要发送给Server后,可以发送FIN信号告知Server,这边已经终止Client到对端Server那边的数据传输。但是,这个时候对端Server可以继续往Client这边发送数据包。于是,两端数据传输的终止在时序上是独立并且可能会相隔比较长的时间,这个时候就必须最少需要2+2 = 4 次挥手来完全终止这个连接。但是,如果Server在收到Client的FIN包后,在也没数据需要发送给Client了,那么对Client的ACK包和Server自己的FIN包就可以合并成为一个包发送过去,这样四次挥手就可以变成三次了(似乎linux协议栈就是这样实现的)&/p&&h4&6. 疑症(6) TCP的头号疼症TIME_WAIT状态&/h4&&p&要说明TIME_WAIT的问题,需要解答以下几个问题&/p&&ul&&li&&p&Peer两端,哪一端会进入TIME_WAIT呢?为什么?&br&相信大家都知道,TCP主动关闭连接的那一方会最后进入TIME_WAIT。那么怎么界定主动关闭方呢?是否主动关闭是由FIN包的先后决定的,就是在自己没收到对端Peer的FIN包之前自己发出了FIN包,那么自己就是主动关闭连接的那一方。对于疑症(4) 中描述的情况,那么Peer两边都是主动关闭的一方,两边都会进入TIME_WAIT。为什么是主动关闭的一方进行TIME_WAIT呢,被动关闭的进入TIME_WAIT可以不呢?我们来看看TCP四次挥手可以简单分为下面三个过程&/p&&blockquote&&p&过程一.主动关闭方发送FIN;&br&过程二.被动关闭方收到主动关闭方的FIN后发送该FIN的ACK,被动关闭方发送FIN;&br&过程三.主动关闭方收到被动关闭方的FIN后发送该FIN的ACK,被动关闭方等待自己FIN的ACK&/p&&/blockquote&&p&问题就在过程三中,据TCP协议规范,不对ACK进行ACK,如果主动关闭方不进入TIME_WAIT,那么主动关闭方在发送完ACK就走了的话,如果最后发送的ACK在路由过程中丢掉了,最后没能到被动关闭方,这个时候被动关闭方没收到自己FIN的ACK就不能关闭连接,接着被动关闭方会超时重发FIN包,但是这个时候已经没有对端会给该FIN回ACK,被动关闭方就无法正常关闭连接了,所以主动关闭方需要进入TIME_WAIT以便能够重发丢掉的被动关闭方FIN的ACK。&/p&&/li&&li&&p&TIME_WAIT状态是用来解决或避免什么问题呢?&br&TIME_WAIT主要是用来解决以下几个问题:&/p&&blockquote&&p&1)上面解释为什么主动关闭方需要进入TIME_WAIT状态中提到的: 主动关闭方需要进入TIME_WAIT以便能够重发丢掉的被动关闭方FIN包的ACK。如果主动关闭方不进入TIME_WAIT,那么在主动关闭方对被动关闭方FIN包的ACK丢失了的时候,被动关闭方由于没收到自己FIN的ACK,会进行重传FIN包,这个FIN包到主动关闭方后,由于这个连接已经不存在于主动关闭方了,这个时候主动关闭方无法识别这个FIN包,协议栈会认为对方疯了,都还没建立连接你给我来个FIN包?,于是回复一个RST包给被动关闭方,被动关闭方就会收到一个错误(我们见的比较多的:connect reset by peer,这里顺便说下 Broken pipe,在收到RST包的时候,还往这个连接写数据,就会收到 Broken pipe错误了),原本应该正常关闭的连接,给我来个错误,很难让人接受。&br&2)防止已经断开的连接1中在链路中残留的FIN包终止掉新的连接2(重用了连接1的所有的5元素(源IP,目的IP,TCP,源端口,目的端口)),这个概率比较低,因为涉及到一个匹配问题,迟到的FIN分段的序列号必须落在连接2的一方的期望序列号范围之内,虽然概率低,但是确实可能发生,因为初始序列号都是随机产生的,并且这个序列号是32位的,会回绕。&br&3)防止链路上已经关闭的连接的残余数据包(a lost duplicate packet or a wandering duplicate packet) 干扰正常的数据包,造成数据流的不正常。这个问题和2)类似。&/p&&/blockquote&&/li&&li&&p&TIME_WAIT会带来哪些问题呢?&br&TIME_WAIT带来的问题注意是源于:一个连接进入TIME_WAIT状态后需要等待2*MSL(一般是1到4分钟)那么长的时间才能断开连接释放连接占用的资源,会造成以下问题&/p&&blockquote&&p&1) 作为服务器,短时间内关闭了大量的Client连接,就会造成服务器上出现大量的TIME_WAIT连接,占据大量的tuple,严重消耗着服务器的资源。&br&2) 作为客户端,短时间内大量的短连接,会大量消耗的Client机器的端口,毕竟端口只有65535个,端口被耗尽了,后续就无法在发起新的连接了。&br&(由于上面两个问题,作为客户端需要连本机的一个服务的时候,首选UNIX域套接字而不是TCP)&/p&&/blockquote&&p&TIME_WAIT很令人头疼,很多问题是由TIME_WAIT造成的,但是TIME_WAIT又不是多余的不能简单将TIME_WAIT去掉,那么怎么来解决或缓解TIME_WAIT问题呢?可以进行TIME_WAIT的快速回收和重用来缓解TIME_WAIT的问题。有没一些清掉TIME_WAIT的技巧呢?&/p&&/li&&/ul&&p&PS:由于TCP协议中的疑难杂症较多,本文分成两篇,方便大家有更好的阅读体验。&/p&&p&本文首发&a href=&https://link.zhihu.com/?target=https%3A//www.qcloud.com/community/article/Futm_source%3DCommunity%26utm_medium%3Darticleutm_campaign%3DCommunity& class=& wrap external& target=&_blank& rel=&nofollow noreferrer&&腾云阁-腾讯云技术社区&/a&&/p&
作者介绍:黄日成,手Q游戏中心后台开发,腾讯高级工程师。从事C++服务后台开发4年多,主要负责手Q游戏中心后台基础系统、复杂业务系统开发,主导过手Q游戏公会、企鹅电竞App-对战系统等项目的后台系统设计,有丰富的后台架构经验。本文获得了小时光茶社的…
&p&作为大一C语言的老师,我来简单回答下吧。实际上班级同学的大作业,都要求500行以上的代码。下面是之前年级同学做的一些游戏作业效果:&/p&&figure&&img src=&https://pic2.zhimg.com/v2-b8a704ac792c433fecaf7d_b.png& data-caption=&& data-rawwidth=&939& data-rawheight=&598& class=&origin_image zh-lightbox-thumb& width=&939& data-original=&https://pic2.zhimg.com/v2-b8a704ac792c433fecaf7d_r.png&&&/figure&&p&&br&&/p&&p&下面是对应的一些作业视频集锦:&/p&&p&2014级: &a href=&//link.zhihu.com/?target=http%3A//pan.baidu.com/s/1EVmX4& class=& external& target=&_blank& rel=&nofollow noreferrer&&&span class=&invisible&&http://&/span&&span class=&visible&&pan.baidu.com/s/1EVmX4&/span&&span class=&invisible&&&/span&&/a&&/p&&p&2015级: &a href=&//link.zhihu.com/?target=http%3A//pan.baidu.com/s/1o75mduy& class=& external& target=&_blank& rel=&nofollow noreferrer&&&span class=&invisible&&http://&/span&&span class=&visible&&pan.baidu.com/s/1o75mdu&/span&&span class=&invisible&&y&/span&&span class=&ellipsis&&&/span&&/a&&/p&&p&2016级:&a href=&//link.zhihu.com/?target=https%3A//pan.baidu.com/s/1nuXHXtZ& class=& external& target=&_blank& rel=&nofollow noreferrer&&&span class=&invisible&&https://&/span&&span class=&visible&&pan.baidu.com/s/1nuXHXt&/span&&span class=&invisible&&Z&/span&&span class=&ellipsis&&&/span&&/a&&/p&&p&&br&&/p&&p&我的教学思路,是讲较少的语法,只讲必须用到的规范性语法知识。学数组前就带着同学们step by step,用printf输出实现打飞机、flappy bird、反弹球等游戏,大概是这样的效果:&/p&&figure&&img src=&https://pic3.zhimg.com/v2-f70bb104db1c750f4a628fa_b.png& data-caption=&& data-rawwidth=&390& data-rawheight=&399& class=&content_}

我要回帖

更多关于 www,58,com 的文章

更多推荐

版权声明:文章内容来源于网络,版权归原作者所有,如有侵权请点击这里与我们联系,我们将及时删除。

点击添加站长微信