所谓二层组播,即数据帧的转发是面向二层的,根据组播MAC地址来决定数据帧的转发方向,而三层组播,即所谓的IP组播,则根据三层组播地址,即组播IP地址来进行数据帧的转发。
二层组播相关协议包括IGMP 和GMRP协议。让我们从分析组播MAC地址开始,逐步而深入的了解二层组播。
组播MAC地址
所谓组播MAC地址,是一类逻辑的MAC地址,该MAC地址代表一个组播组,所有属于该组的成员都接收以该组对应的组播MAC地址为目的地址的数据帧。
注意的是,组播MAC地址是一个逻辑的MAC地址,也就是说,在网络上,没有一个设备的MAC地址是一个组播MAC地址。组播MAC地址跟单播MAC地址(物理MAC地址)的区别是,组播MAC地址六个字节中,最高字节(第六字节)的最低位为1,而单播MAC地址则为0,如下图所示:
为了更进一步了解组播的概念,我们先从MAC层的数据帧接收过程说起。
MAC层数据帧的接收
在网卡的内部保留一张接收地址列表(可以理解为一个可读写的随机存储器),其中至少有两个MAC地址,即网卡的物理MAC地址和全1的广播MAC地址。每当计算机想接收一个组播数据,也就是说要加入一个组播组,那么上层软件会给网络层一个通知,网络层做完自己的处理后,也会发一个通知给数据链路层,于是,数据链路层根据网络层想加入的组播组的组地址(一般是一个组播的IP地址),根据一定的规则映射为一个组播的MAC地址,然后把该MAC地址加入接收地址列表。
每当数据链路层接收到一个数据帧的时候,就提取该数据帧的帧头,找出目的MAC地址,跟接收地址列表中的地址项目比较,如果在列表中遇到一个地址,跟该数据帧的目的MAC地址是相同的,就停止比较,接收该数据帧,并把该数据帧放到上层协议对应的接收队列中;如果在整个接收列表中没有找到一个匹配的MAC地址,则丢弃该数据帧。
现假设接收到的数据帧是发给自己的单播数据帧,于是该数据帧的目的MAC地址就是自己的硬件地址。数据链路层接收到该数据帧,跟接收列表中的地址比较,第一次比较就会通过,因为接收地址列表中的第一个MAC地址就是自己的硬件地址。所以在任何情况下,发给自己的数据帧一定能接收下来;
假设接收到的数据帧是一个广播数据帧,则在比较的时候,最后一项是匹配的,因为接收地址列表中肯定包含一个广播的MAC地址,这样就保证了任何广播数据报都会被正确接收;
假设上层软件想接收组播组G的数据,经过一番映射到数据链路层之后,数据链路层会在自己的接收数据列表中添加一项组播组G对应的MAC地址,假设为MAC_G,当计算机接收到一个数据帧,该数据帧的目的地址为MAC_G的时候,该数据帧会被接收并传递到上层,因为接收列表中有一项MAC_G记录。
组播转发表
在前面交换机的转发方式一节中讲到,交换机在转发组播数据包的时候,也是根据一个转发表来进行的,但跟单播转发表不同的是,该组播转发表对应的出口不是一个元素/端口,而是一组元素/端口,即一个出口列表。在转发数据的时候,交换机根据组播数据帧的目的组播地址查找组播转发表,如果在组播转发表中查不到相应的转发项,则把该组播数据帧当做广播数据帧处理,向所有端口上转发该数据帧(如果实现了VLAN,则仅仅向接收端口所属同一个VLAN的端口上转发);如果能查找到一个结果,则结果肯定是一个接口列表,于是,交换机把这个组播数据报复制成许多份,每份提交到一个出接口,从而完成数据的交换。
需要注意的是,这个组播转发项不像单播那样,是通过学习的方式获得的(在处理单播数据的时候,交换机每转发一个数据帧,会进行相应的学习过程,学习到一个单播转发项),而是通过一些二层的组播协议获得的,这些流行的组播协议有IGMP(Internet Group Management Protocol, 因特网组管理协议),GMRP(GARP Multicast Register Protocol,GARP组播注册协议,GARP- General Attribute Registration Protocol)等。下面部分详细解释每一种二层组播协议。
二层组播协议
上面我们提到,用于转发组播数据帧的组播转发项不是通过学习获得的,而是通过一些其他的组播协议,比如IGMP窥探,GMRP等协议获得的。至于为什么不能通过学习获得,是因为学习过程是分析一个数据帧的源地址,但组播地址在任何情况下不可能出现在源地址的位置(因为组播MAC地址是一个逻辑的MAC地址,网络上没有一个终端的MAC地址是一个组播MAC地址)。
下面我们分析二层组播协议,这些协议在构建组播网络的时候,有不可替代的重要地位。尤其是将来的流媒体应用会很广泛,在这种情况下,二层的组播将是必然的要求。
IGMP协议
在讲述IGMP窥探之前,先看一下IGMP协议,理解了IGMP协议,IGMP窥探就很容易了。
一般情况下,组播数据是从路由器的上游流下来的,而组播的接收端一般位于路由器的下游,典型的情况是,路由器通过一个以太网口连接企业网,另外通过一个串口连接Internet。组播数据源,比如流媒体服务器一般位于Internet上,组播数据流通过路由器到达企业网上的数据接收端。这种情况下,流媒体服务器是长时间工作的,也就是说,一天24小时,一周7天都在不停的发送媒体流信息,相当于电视频道。但企业网络中的数据接收端却不是这样,只有有限的时间接收端才接收数据,其他时间都是不接收数据的。这样,在企业网上没有数据接收端的时候,如果路由器也把大量的媒体流信息发到企业网内部,必然会浪费大量的资源,理想的情况是,如果企业网上没有数据接收端,则路由器就不转发媒体流,但如果只要有一个接收端,路由器就必须把媒体流引入企业网。这样必须有一种机制来保证路由器对企网上的数据接收端有一个清楚的了解,什么时候网络上有数据接收端,什么时候没有数据接收端,这种机制就是IGMP协议。
从上面的分析中,我们可以看出IGMP协议是数据接收端和路由器之间的交互协议,数据接收端使用该协议来通知路由器,自己是否想接收组播数据流。如果想接收的话,接收哪个组播组的数据流。
一般情况下,存在两种类型的IGMP消息:组成员报告和组成员查询,其中组成员报告消息由计算机发出,用来告诉路由器,自己想加入某个组播组,而组成员查询消息则由路由器发出,用来查询网络上是否存在相应组的成员。一个指导性的原则是,只要网络上有组播组的数据接收端,不管该接收端的数量是多少,路由器必须把该组播组的数据转发到网络上。
IGMP消息也是通过组播地址发出的,在IGMP加入消息中,该组播地址(IGMP消息的目的地址)就是该计算机想加入的组播组的组地址。比如,计算机想加入224.0.0.2这个组播组,则该计算机发出的IGMP加入消息的目的IP地址就是224.0.0.2。这样当路由器接收到该组播消息后,就知道网络上有一个主机,该主机想接收到组播组224.0.0.2的数据,于是,每当从上游接收到目的地址是该组地址的数据的时候,就把该数据包向企业网上转发。
还有一类消息就是组播组成员查询消息。在IGMP协议第一版中,没有规定主机的离开消息,即如果一个主机不想接收某一组播组的数据了,它也不会通过某种消息通知路由器,而是静悄悄的离开该组播组。这样如果路由器不采取某种措施来掌握网络上组播组接收端的数目情况,就会产生问题,假设网络上所有的主机都不想接收组播数据了,根据IGMP协议第一版,这些主机不会通知路由器,而是不做任何处理。这样路由器不知道这些原来接收组播数据的主机现在已经不接收数据了,而且还以为网络上有一大批的接收端,于是仍源源不断的发送组播数据,这样必然浪费带宽。而引入了组成员查询消息后,路由器可以每隔一段时间发出该消息,用来查询网络上还有没有主机在接收组播数据。注意的是,该查询消息跟成员加入消息一样,采用的目的IP地址也是查询的组的组播IP地址。假设路由器想查询245.2.2.1这样一个组播组是否还有成员,发出的查询消息的目的IP地址就是245.2.2.1。当网络上接收该组播组数据的成员接收到这个查询消息后,就发出一个响应,该响应还是一个组播组成员加入消息。这样只要路由器接收到一个这样的响应,它就知道网络上必然还有终端在接收数据,于是不能停止转发。但路由器发出查询消息后,一段时间内没有接收到任何响应,它就判断网络上没有了数据接收端,于
在学习了模板系统的基础之后,现在让我们使用相关知识来创建视图。 重新打开我们在前一章在 mysite.views 中创建的 current_datetime 视图。 以下是其内容:
from django.http import HttpResponse import datetime def current_datetime(request): now = datetime.datetime.now() html = "<html><body>It is now %s.</body></html>" % now return HttpResponse(html)
让我们用 Django 模板系统来修改该视图。 第一步,你可能已经想到了要做下面这样的修改:
from django.template import Template, Context from django.http import HttpResponse import datetime def current_datetime(request): now = datetime.datetime.now() t = Template("<html><body>It is now {{ current_date }}.</body></html>") html = t.render(Context({'current_date': now})) return HttpResponse(html)
没错,它确实使用了模板系统,但是并没有解决我们在本章开头所指出的问题。 也就是说,模板仍然嵌入在Python代码里,并未真正的实现数据与表现的分离。 让我们将模板置于一个单独的文件 中,并且让视图加载该文件来解决此问题。
你可能首先考虑把模板保存在文件系统的某个位置并用 Python 内建的文件操作函数来读取文件内容。假设文件保存在/home/djangouser/templates/mytemplate.html 中的话,代码就会像下面这样:
from django.template import Template, Context from django.http import HttpResponse import datetime def current_datetime(request): now = datetime.datetime.now() # Simple way of using templates from the filesystem. # This is BAD because it doesn't account for missing files! fp = open('/home/djangouser/templates/mytemplate.html') t = Template(fp.read()) fp.close() html = t.render(Context({'current_date': now})) return HttpResponse(html)
然而,基于以下几个原因,该方法还算不上简洁:
-
它没有对文件丢失的情况做出处理。 如果文件 mytemplate.html 不存在或者不可读, open() 函数调用将会引发 IOError 异常。
-
这里对模板文件的位置进行了硬编码。 如果你在每个视图函数都用该技术,就要不断复制这些模板的位置。 更不用说还要带来大量的输入工作!
-
它包含了大量令人生厌的重复代码。 与其在每次加载模板时都调用 open() 、 fp.read() 和fp.close() ,还不如做出更佳选择。
为了解决这些问题,我们采用了 模板自加载 跟 模板目录 的技巧.
模板加载为了减少模板加载调用过程及模板本身的冗余代码,Django 提供了一种使用方便且功能强大的 API ,用于从磁盘中加载模板,
要使用此模板加载API,首先你必须将模板的保存位置告诉框架。 设置的保存文件就是我们前一章节讲述ROOT_URLCONF配置的时候提到的settings.py。
如果你是一步步跟随我们学习过来的,马上打开你的settings.py配置文件,找到TEMPLATE_DIRS这项设置吧。 它的默认设置是一个空元组(tuple),加上一些自动生成的注释。
TEMPLATE_DIRS = ( # Put strings here, like "/home/html/django_templates" or "C:/www/django/templates". # Always use forward slashes, even on Windows. # Don't forget to use absolute paths, not relative paths. )
该设置告诉 Django 的模板加载机制在哪里查找模板。 选择一个目录用于存放模板并将其添加到 TEMPLATE_DIRS 中:
TEMPLATE_DIRS = ( '/home/django/mysite/templates', )
下面是一些注意事项:
你可以任意指定想要的目录,只要运行 Web 服务器的用户可以读取该目录的子目录和模板文件。 如果实在想不出合适的位置来放置模板,我们建议在 Django 项目中创建一个templates 目录(也就是说,如果你一直都按本书的范例操作的话,在第二章创建的mysite 目录中)。
如果你的 TEMPLATE_DIRS只包含一个目录,别忘了在该目录后加上个逗号。
Bad:
# Missing comma! TEMPLATE_DIRS = ( '/home/django/mysite/templates' )
运输层
标志进程:因为运输层直接为上层的调用进程提供服务,它需要地址来唯一地标志进程间的通迅。或者说进程通过TSAP来调用运输层提供的服务;
端口号:大致有两种意思:一是物理意义上的端口,比如,ADSLModem,集线器,交换机,路由器等 用于连接其他网络设备的接口,如RJ-45端口,SC端口等等。二十逻辑意义上的端口,一般是指TCP/IP协议中的端口,端口号的范围从0到65535;
IP地址:就是给每个连接在Internet上的主机分配的一个32bit地址;
UDP用户数据报协议:是OSI参考模型中一种无连接的传输层协议,提供面向事务的简单不可靠信息传送服务;
UDP的主要特点:㈠UDP是无连接的;㈡UDP尽最大努力交付;㈢UDP面向报文的;㈣UDP没有拥塞控制;㈤UDP支持一对一,一对多,和多对一,多对多的交互通信;㈥UDP首部开销小;
TCP传输控制协议:是一种面向连接的,可靠的,基于字节流的运输层通信协议;
复用:应用层所有的进程都可以通过运输层再传送到IP层;
分用:运输层从IP层收到数据后必须交付给指明的应用进程;
TCP可靠传输的实现:以字节为单位的滑动窗口;超时重传时间的选择;选择确认SACK;
TCP的流量控制:让发送方的发送速率不要太快,要让接受方来得及接收。利用滑动窗口实现流量控制;必须考虑传输速率;
TCP的拥塞控制:防止过多的数据注入到网络中,这样可以使网络中的路由器或链路不致过载;
方法:慢开始;拥塞避免;快重传;快恢复;
TCP的运输连接管理:运输连接三个阶段:连接建立;数据传送和连接释放;