搜狐媒体平台-搜狐网站>IT

他一出家就成中国最帅和尚

眼眸深邃、轮廓分明、身材颀长,活生生的一幅画。

大学副教授与在押服刑女结婚

这在监狱民警看来,那么令人不可思议。

从零构建TCP/IP协议(这次叫PCT协议)

声明:本文由入驻搜狐公众平台的作者撰写,除搜狐官方账号外,观点仅代表作者本人,不代表搜狐立场。举报

  这篇博客是读完《图解TCP/IP协议》和《TCP/IP协议详解卷一:协议》之后的总结:)

  我从0构建了一个可靠的双工的有序的基于流的协议,叫做PCT协议。

  

  OSI七层模型和TCP/IP四层模型

  谈到计算机网络,就一定会说起OSI七层模型和TCP/IP四层模型,不过我们先从为何分层 说起。

  为什么要分层

  软件开发的过程中,我们经常听到的词语是"解耦","高内聚,低耦合"等等诸如此类的 词语,又常听见写Java的同学念叨着"桥接模式","面向接口"等词语,那么他们说的这些 词语的核心问题是什么呢?我们先从一个简单的问题看起:

  现在我们需要做一个推送系统,要对接Android和iOS两个系统,大家都知道,Apple有统一 的推送渠道,APNs,所以我们只要接入这个就好,但是Android的推送在国内是百家争鸣, 就拿之前我为公司接入推送通知来举例,要接入极光,小米,可能要接入华为推送。

  那我要怎么从具体的推送里抽象出来呢?运用面向对象的想法,我们很容易就能想到, 我们有一个父类,叫 BasePush,他的子类就是具体的 MiPush, JPush, HMSPush。 父类中有 push_by_id 和 push_by_tag 等方法,子类重写。这样我们在具体实现的时候 实例化子类,并且调用对应的方法就好。这种思想其实就是面向接口编程,在Java中我们 可以转变一下编程的写法,把继承变成接口。在Python中我们就可以直接脑补这种写法。 用图来表示,纯粹面向对象的时候我们的想法是这样的:

  

  如果我们把上面的图倒过来,就变成了面向接口:

  

  在使用面向接口之后,我们就是做了这样一种假设:

  defpush(pusher, id): pusher.push_by_id(id)

  即,传给push函数的pusher实例一定存在 push_by_id 方法。正是基于这样一种假设, 我们得以把具体业务代码和具体的推送商划分开来,这就是所谓的抽象,也就是一种分层。

  要分层的原因也就显现出来了,为了把不同的东西错综复杂的关系划分开来,也就是古话 说的"快刀斩乱麻"的这种感觉。

  两种网络模型

  日常编程里我们用的最多的就是TCP了,UDP也是有的,但是很少,举一些常见的例子:

  • DNS -> UDP

  • 连接MySQL -> TCP

  • 连接Redis -> TCP

  • RPC -> TCP

  • 访问网站 -> TCP

  当然了,这只是常见实现方式如此,其实用UDP也是可以实现的。这篇博客里我们暂时不讨论 UDP。我们先来看TCP/IP四层是怎么分层的:

  ascii 表格其实挺好看的,最后渲染的时候因为宽字符的原因格式有点乱掉了,下同

  我们直接把 TCP/IP 四层协议 映射到 OSI七层协议 上看:

  +--------------+---------------+----------------+| OSI 七层协议 | 例如 | 对应TCP/IP四层 |+--------------+---------------+----------------+| 应用层 | HTTP协议 | |+--------------+---------------+ || 表示层 | | 应用层 |+--------------+---------------+ || 会话层 | | |+--------------+---------------+----------------+| 传输层 | TCP | 传输层 |+--------------+---------------+----------------+| 网络层 | IP | 网际层 |+--------------+---------------+----------------+| 数据链路层 | 因特网,Wi-Fi | |+--------------+---------------+ 网络接口层 || 物理层 | 双绞线,光缆 | |+--------------+---------------+----------------+

  接下来我们将从底层逐层向上来解析网络,最后我们将简略的介绍TCP(TCP的知识足够 写好几本书,一篇博客里远远介绍不完。不信可以看看TCP/IP协议详解那三卷书加起来 有多厚)。

  物理层

  物理层,顾名思义,就是物理的,可见的东西。也就是平时我们所说的光纤,Wi-Fi(无线电波) 等,我们知道计算机是用0和1来表示的,对应到不同的介质里是不同的表现形式, 因此为了把物理层的实现屏蔽掉,我们把这些都分到一层里,例如Wi-Fi通过波的 波峰与波谷可以表示出0和1的状态(我们平时会说成1和-1,对应计算机里其实就是1和0)。 对应到电里,我们可以用高电压和低电压来表示出1和0。如同最开始讲的例子一样, 我们不管具体的介质是什么,只知道,我们用的这个介质有办法表示1和0。

  数据链路层

  如果我们去邮局写一封信,填完收件人之后,邮局派发的顺序可能是,先投递到指定的 国家,然后投递到具体的省,然后市。。。逐次投递下去。那么我们玩电脑的时候,计算机 要怎么把A发给B的信息准确送达呢?

  肯定大家都要有一个地址,上一节我们知道了,不同的介质都有他的方式表示1和0,那么 我们给介质的两端加上地址,我们叫做MAC地址,如何?就拿路由器来说吧,路由器的 MAC地址叫做 router ,手机的MAC地址叫做 phoner,为了表示成0和1,我们分别取 字符串的ASCII的二进制来表示,路由器叫做 1110010 1101111 1110101 1110100 1100101 1110010, 而手机则叫做:1110000 1101000 1101111 1101110 1100101 1110010,现在我们终于可以发信息 了,最少是相邻的两个东西可以透过某种介质来发信息,所以我们定下这样的协议:

  协议,其实就是一种约定 :)

  • 最开始我们发送111表示信息开始

  • 然后,我们先有48个bit表示发送者的MAC地址,再有48个bit表示接受者的MAC地址

  • 之后,就是我们要发送的信息

  • 最后我们发送000表示结束,如果开头和结尾不是这样的,那么说明这是假的信息。

  知道上面为啥手机叫 phoner 而不叫 phone 了嘛 :) 就是为了保证地指名长度一样

  "hello" 的二进制表示是 "1101000 1100101 1101100 1101100 1101111",如果路由器要向 手机发送 "hello"的话,那么就发送这样一串二进制(用换行分割,这样更容易看清楚):

  11101110010 01101111 01110101 01110100 01100101 0111001001110000 01101000 01101111 01101110 01100101 0111001001101000 01100101 01101100 01101100 01101111000

  这样表示看起来可行,不过遇到一个问题,就是如果这一串二进制中间就出现了000怎么办? 因为计算机读取的时候是从头开始读的,这样子计算机就会乱掉。

  为了解决这个问题,我们修改一下协议,在111之后加上发送者地址+接受者地址+所要发送的 信息的长度。我们用 16个bit来表示,也就是说这中间不能发送多于 2 ** 16 个bit。

  所以协议变成了:

  • 最开始我们发送111表示信息开始

  • 随后我们用16个bit表示包的长度

  • 然后,我们先有48个bit表示发送者的MAC地址,再有48个bit表示接受者的MAC地址

  • 之后,就是我们要发送的信息

  • 最后我们发送000表示结束,如果开头和结尾不是这样的,那么说明这是假的信息。

  发送者地址+接收者地址+hello的bit长度是 6 * 8 + 6 * 8 + 5 * 8 = 136,二进制表示 为: 00000000 10001000

  所以发送的整个信息变成了:

  11100000000 1000100001110010 01101111 01110101 01110100 01100101 0111001001110000 01101000 01101111 01101110 01100101 0111001001101000 01100101 01101100 01101100 01101111000

  网络层

  现在我们终于可以发送信息了。不过有个缺点,我们只能在相邻的时候才可以发送信息, 那有没有办法可以借助两两传递,在不同的地方也发送信息呢?有,那就是我们的网络层 也就是ip(我们能遇到的最通俗易懂的一个名词了,暂时把它当作网络层的代名词也不为过)。

  刚刚我们已经学会了一种技术,就是分配一个地址,刚刚的叫做MAC地址,我们用来做 相邻两个节点的定位。其实这个地址也可以用来在多个节点之间找人,基于这样一种 技术:每个节点都知道和自己相邻的节点的MAC地址,那么,比如这样一种连接方式:

  A - B - C - E / - D -

  A向E发送消息,就可以这样:

  • A向B和D发消息:给我发到E去

  • B和D接到之后发现来源是A,所以就只给C发消息:给我发到E去

  • C接到消息之后发现来源是B和D,所以就给E发消息:给我发到E去

  • E接到消息之后发现接收方是自己,所以就把消息吞了

  你别说,这种方式好像真的行得通呢,除了有一个显著的问题,A向E发送一份消息, 最后E收到了两份,这个我们需要到后面进行去重。我们先打上一个TODO的标签吧。

  还有一个细节问题,不知道大家发现了么,刚才我们说过,MAC地址是相邻两个节点 通信用的,里面有来源地址和目标地址,如果我们向上面这样传输的话,每个节点都 只是把里面的信息传过去,但是来源地址却改要改写成自己的MAC地址,要不然的话, B就不知道信息是A发来的还是C发来的呀,对不对?那问题就来了,E要怎么知道信息 其实是从A发过来的呢?

  没办法了,我们只好在传输的信息里把真正的来源地址写进去,所以我们又定了一个 协议,我们管它叫做ip:

  • MAC携带的信息的开始,是来源的ip地址,32个bit表示

  • 然后是目标的ip地址,32个bit表示

  • 然后是我要带的信息

  那和上面的数据链路层的协议合一下起来,假设来源地址是 192.168.1.1,目标地址是 192.168.1.2,发送的信息还是 "hello",整个包就像这样:

  111(开始)00000000 11001000(长度)01110010 01101111 01110101 01110100 01100101 01110010(来源MAC地址)01110000 01101000 01101111 01101110 01100101 01110010(目标MAC地址)11000000 10101000 00000001 00000001(来源ip地址)11000000 10101000 00000001 00000010(目标ip地址)01101000 01100101 01101100 01101100 01101111(字符串"hello")000(结束)

  这样是不是就很科学?那必须的。哎呀,终于可以跨节点发送消息了,小开心~

  可是还是有问题,如果我想确定A发的信息一定送达了E怎么办?怎么提供可靠性?IP这一层 并不提供可靠性,只是说尽量送达。看来有必要再来一层!

  传输层

  我们知道,一台计算机上可能有很多个程序在运行,那怎么区分不同的程序呢?所以我们 给程序加上了id,叫做pid。那计算机网络通信的时候怎么区分呢?又假设n个进程想和另外 一台机器上的某一个进程通信呢?怎么办?

  不如我们再分配一个id吧,他们共同持有这个id就好了。我们把这个id叫做端口(port)。 这样子的话,通过ip地址我们可以确定计算机,通过端口我们可以确定一个或多个进程。

  我们继续造协议,不过这一次我们想要这个协议贼可靠,所以要多做一些工作。其实要是 按照七层协议来实现的话,完全不必在这一层干这么多事情,不同的层干不同的事情嘛, 对不对。不过为了理解TCP协议,我们呀,也跟着来自己捏造一个协议,不如叫PCT好了。

  继续,我们要在ip带的信息里规定好我们这样发:

  • 首先是来源地址的端口号,8个bit来表示,因为ip里面已经待了ip地址,我这里就不重复带了

  • 然后是目标地址的端口号,8个bit来表示

  这样,简单的PCT协议就做好了。

  还有一个问题,就是我们要保证发出去的信息是有序的,因为可能有的信息走光纤, 有的信息走Wi-Fi,他们传输速率不一样嘛。

  所以我们在协议里这样写:

  • 首先是来源地址的端口号,8个bit来表示,因为ip里面已经待了ip地址,我这里就不重复带了

  • 然后是目标地址的端口号,8个bit来表示

  • 然后是这个包的序号,8个bit来表示

  但是我们说好了要把这个协议打造成一个可靠的协议,可不能食言。我想想,怎么让他 可靠呢,无非就是我发一个信息,你告诉我你收到了,要是你不告诉我,我就发到你告诉我 为止。差不多就是这么个意思。但是呢,又不想构造多个不同的协议,你知道,编程的时候 要是写一堆的if-else树那可就很蛋疼了。再改改协议:

  • 首先是来源地址的端口号,8个bit来表示,因为ip里面已经待了ip地址,我这里就不重复带了

  • 然后是目标地址的端口号,8个bit来表示

  • 然后是这个包的序号,8个bit来表示

  • 然后是想确认的包的序号,8个bit来表示

  咦,点睛之笔耶,这个确认的包的序号,因为我们是双向通信,我发他信息的时候还可以顺便 确认我收到了他的包啊,真是一箭双雕。

  TCP是一个面向流的协议,什么叫流?车流,水流,车流比较形象。车和车之间是分开的, 但是速度一快起来,就可以把它们看成连起来的。TCP也是这样,单个包之间是分开的, 但是却可以看作是连起来,为什么呢?因为每个包里都带了ip地址和端口号,ip地址和端口 号一样的,就可以看作是连起来的 :)

  所以我们可以想象一下,我们的ip地址是192.168.1.1, 端口号是 1, 目标的ip地址是 192.168.1.2, 端口号是 2。那我们发送这样的包:

  111(开始)00000000 11101000(长度)01110010 01101111 01110101 01110100 01100101 01110010(来源MAC地址)01110000 01101000 01101111 01101110 01100101 01110010(目标MAC地址)11000000 10101000 00000001 00000001(来源ip地址)11000000 10101000 00000001 00000010(目标ip地址)00000001(来源的端口号)00000010(目标的端口号)00000001(发送的包的序号是1)00000000(已经确认的包的序号是0,表示啥都没有嘛)01101000 01100101 01101100 01101100 01101111(字符串"hello")000(结束)

  duang,就这样,我们构建起了属于自己的可靠的基于流的双工的协议 :)

  顺便我们还完成了上面的TODO,通过序号我们就可以判断这个包是不是重复了,哈哈哈, 一箭n雕~

  TCP三次握手四次挥手滑动窗口拥塞控制等就不讲了,还是去看《TCP/IP协议详解卷一》吧 :)

  应用层

  这下我们终于可以放心大胆的发送消息了,PCT协议是个负责任的协议,如果能送到,他就一定 会送到,并且是有序的,要是网络坏掉了,实在连不上,他就会告诉我网络连不上。

  这样子来编程方便多了呀。

  现在我想知道浏览器和服务器是怎么通信的。我们来看看百度。

  $ telnet www.baidu.com 80Trying 183.232.231.173...Connected to www.baidu.com.Escape character is '^]'.GET / HTTP/1.1HTTP/1.1 302 Moved TemporarilyDate: Sat, 12 Aug 2017 10:45:14 GMTContent-Type: text/htmlContent-Length: 215Connection: Keep-AliveLocation: https://www.baidu.com/search/error.htmlServer: BWS/1.1X-UA-Compatible: IE=Edge,chrome=1BDPAGETYPE: 3Set-Cookie: BDSVRTM=0; path=/<html><head><title>302 Found</title></head><body bgcolor="white"><center><h1>302 Found</h1></center><hr><center>pr-nginx_1-0-350_BRANCH BranchTime : Tue Aug 8 20:41:04 CST 2017</center></body></html>^]telnet> Connection closed.

  输入 GET / HTTP/1.1 之后回车,百度就给我返回了下面的一长串,然后浏览器再根据 返回的内容进行渲染,这又是一个大话题了,不讲了不讲了,收工 :)

  来自:Jiajun's Blog

mt.sohu.com true 云技术实践 https://mt.sohu.com/20170818/n507313069.shtml report 13340 这篇博客是读完《图解TCP/IP协议》和《TCP/IP协议详解卷一:协议》之后的总结:)我从0构建了一个可靠的双工的有序的基于流的协议,叫做PCT协议。OSI七
阅读(0) 举报
欢迎举报抄袭、转载、暴力色情及含有欺诈和虚假信息的不良文章。

热门关注

搜生活

搜生活+关注

搜狐公众平台官方账号

MAGIC杨梦晶

MAGIC杨梦晶+关注

生活时尚&搭配博主 /生活时尚自媒体 /时尚类书籍作者

搜狐教育

搜狐教育+关注

搜狐网教育频道官方账号

星吧GEO

星吧GEO+关注

全球最大华文占星网站-专业研究星座命理及测算服务机构

热门图片

  • 热点视频
  • 影视剧
  • 综艺
  • 原创
锦绣缘

同步热播-锦绣缘

主演:黄晓明/陈乔恩/乔任梁/谢君豪/吕佳容/戚迹
神雕侠侣

大结局-神雕侠侣

主演:陈晓/陈妍希/张馨予/杨明娜/毛晓彤/孙耀琦
封神英雄榜

同步热播-封神英雄榜

主演:陈键锋/李依晓/张迪/郑亦桐/张明明/何彦霓

六颗子弹

主演:尚格·云顿/乔·弗拉尼甘/Bianca Bree
龙虎少年队2

龙虎少年队2

主演:艾斯·库珀/ 查宁·塔图姆/ 乔纳·希尔

《奔跑吧兄弟》

baby14岁写真曝光

《我看你有戏》

李冰冰向成龙撒娇争宠

《明星同乐会》

李湘遭闺蜜曝光旧爱

《非你莫属》

美女模特教老板走秀

《一站到底》

曝搬砖男神奇葩择偶观

搜狐视频娱乐播报

柳岩被迫成赚钱工具

大鹏嘚吧嘚

大屁小P虐心恋

匆匆那年第16集

匆匆那年大结局

隐秘而伟大第二季

乔杉遭粉丝骚扰

The Kelly Show

男闺蜜的尴尬初夜

我来说两句排行榜

客服热线:86-10-58511234

客服邮箱:kf@vip.sohu.com