码迷,mamicode.com
首页 > 其他好文 > 详细

第5章 运输层

时间:2016-06-06 01:21:09      阅读:295      评论:0      收藏:0      [点我收藏+]

标签:

第5章运输层


5.1 运输层协议概述

运输层的一个很重要的功能就是复用分用


5.1.1 进程之间的通信

运输层的主要功能

  • 运输层为应用进程之间提供端到端的逻辑通信(但网络层是为主机之间提供逻辑通信)。
  • 运输层还要对收到的报文进行差错检测。
  • 运输层需要有两种不同的运输协议,即面向连接的TCP和无连接的UDP。

IP数据报首部中的检验和字段,只检验收是否出现差错,而不检查数据部分。

TCP
一条全双工的可靠信道
UDP
仍然是一条不可靠信道

5.1.2 运输层的两个主要协议

运输协议数据单元TPDU
Transport Protocol Data Unit,来源自OSI,两个对等运输实体在通信时传送的数据单位
  • TCP 传送的数据单位协议是TCP 报文段(segment)
  • UDP 传送的数据单位协议是UDP 报文用户数据报

UDP在传送数据之前不需要先建立连接
TCP不提供广播或多播服务


5.1.3 运输层的端口

软件端口与硬件端口

  • 在协议栈层间的抽象的协议端口是软件端口。
  • 路由器或交换机上的端口是硬件端口。
    硬件端口是不同硬件设备进行交互的接口,而软件端口是应用层的各种协议进程与运输实体进行层间交互的一种地址。

16位端口号

1. 服务器端使用的端口号

熟知端口号
well-known port number,也称系统端口号,0~1023
登记端口号
1024~49151
客户端使用的端口号
动态选择,49151~65535

5.2 用户数据报协议UDP


5.2.1 UDP概述

在IP层上增加的功能

  • 复用,分用
  • 差错检测

UDP特点

  • 无连接
  • 尽最大努力交付,即不保证可靠交付
  • 面向报文,UDP不拆分或合并应用层报文
  • 没有拥塞控制,很适合多媒体通信的要求。
  • 支持一对多,多对多通信
  • 首部小,只有8字节!

面向报文的 UDP
发送方 UDP 对应用程序交下来的报文,在添加首部后就向下交付 IP 层。UDP 对应用层交下来的报文,既不合并,也不拆分,而是保留这些报文的边界。
应用程序必须选择合适大小的报文。


5.2.2 UDP的首部格式

技术分享

每个字段的长度都是2个字节

  1. 源端口2B
  2. 目的端口2B
  3. 长度2B
  4. 检验和2B

伪首部的第3个字段是全零,第4个字段是IP首部中的协议字段的值,(对UDP来说是17),第5个字段是UDP用户数据报的长度。

我的看法,伪首部简介的保证了IP传送的时候最基本的信息不会出错,如果这些信息都出错了,就赶快抛弃,根本没有传下去的必要了。


5.3 传输控制协议TCP概述


5.3.1 TCP最主要的特点

  • 面向连接
  • 端点,点对点
  • 可靠交付
  • 全双工
  • 面向字节流
stream,流入到进程或从进程流出的字节序列
面向字节流
应用程序和TCP每次交互式一次一个数据块【大小不等】,但TCP把这些数据仅看成是一连串的无结构的字节流,不关注内容

TCP不保证发送的数据块的接收的数据块具有大小对应关系,但是保证收发的字节流完全一样。


5.3.2 TCP的连接

每一条TCP连接唯一地被通信的两个端点(即两个套接字)所确定,UDP是不是这样呢?

TCP连接的端点就是套接字。

同一个名词 socket有多种不同的意思
应用编程接口 API 称为 socket API, 简称为 socket。
socket API 中使用的一个函数名也叫作 socket。
调用 socket 函数的端点称为 socket。
调用 socket 函数时其返回值称为 socket 描述符,可简称为 socket。
在操作系统内核中连网协议的 Berkeley 实现,称为 socket 实现。


5.4 可靠传输的工作原理

理想的传输条件有以下两个特点:
1. 传输信道不产生差错
2. 不管发送方以多快的速度发送数据,接收方总是来得及处理收到的数据
在这样的理想传输条件下,不需要采取任何措施就能够实现可靠传输

运输层传送的协议数据单元叫做报文段,网络层传送的协议数据单元叫做IP数据报,二者都可以简称为分组。


5.4.1 停止等待协议

1. 无差错情况
2. 出现差错情况

超时重传
超时计时器

注意

  1. 必须暂时保留已发送的分组的副本
  2. 分组和确认分组都要编号
  3. 超时重传时间应当比数据在分组传输的平均往返时间更长一些

3. 确认丢失和确认迟到**

技术分享

确认丢失处理
1. 丢弃这个重复的分组
2. 向发送方再次发送确认,不能认为已经发送过确认就不再发送,因为发送方之所以重传分组就表示没有收到确认

确认迟到处理
对重复的确认处理很简单,收下后就丢弃
自动重传请求ARQ
Automatic Repeat reQuestARQ 表明重传的请求是自动进行的。接收方不需要请求发送方重传某个出错的分组 。

4. 信道利用率

技术分享

RTT
往返时间
TD
发送分组所需要的时间
TA
发送确认分组所需要的时间
总时间
TD+TA+RTT

信道利用率U=TDTD+RTT+TA

一般的TA?TD

流水线传输
连续ARQ协议和滑动窗口协议

5.4.2 连续ARQ协议

发送窗口
位于发送窗口内的分组都可以连续发送出去,而不需要等待对方的确认

连续ARQ协议规定发送方每收到一个确认,就把发送窗口向前滑动一个分组的位置

累计确认
接收方不必对收到的分组逐个发送确认,而是可以在收到几个分组后,对按序到达的最后一个分组发送确认

优点是:容易实现,即使确认丢失也不必重传。
缺点是:不能向发送方反映出接收方已经正确收到的所有分组的信息。

GBN
Go-back-N,表示需要再退回来重传已发送过的 N 个分组。可见当通信线路质量不好时,连续 ARQ 协议会带来负面的影响。

TCP 可靠通信的具体实现
TCP 连接的每一端都必须设有两个窗口——一个发送窗口和一个接收窗口。
TCP 的可靠传输机制用字节的序号进行控制。TCP所有的确认都是基于序号而不是基于报文段。
TCP 两端的四个窗口经常处于动态变化之中。
TCP连接的往返时间 RTT 也不是固定不变的。需要使用特定的算法估算较为合理的重传时间。


5.5 TCP报文段的首部格式

技术分享

编号 内容 大小 作用
1 源端口和目的端口 16位,2字节 实现运输层的复用和分用
2 序号*** 32位,4字节 每一个字节都按顺序编号*
3 确认号*** 32位,4字节 期望收到对方下一个报文段的第一个数据字节的序号**
5 数据偏移 4位,0.5个字节 指出TCP报文段的首部长度,以4字节为单位****
6 保留 6位
7 URG 1位
8 ACK 1位 ACKnowlegment,连接建立之后所有传送的报文段都必须把ACK置1
9 PSH 1位 PuSH
10 RST 1位 ReSet
11 SYN 1位 SYNchronization,链接请求报文段
12 FIN 1位
13 窗口 16位,2字节 窗口值作为接收方让发送方设置其发送窗口的依据,最大窗口大小为64KB
14 检验和 16位,2字节 计算是要加上12字节的伪首部,检验范围包括首部和数据两部分
15 紧急指针 16位,2字节
16 选项 最长可达40字节

*首部中的序号字段值则是本报文段所发送的数据的第一个字节序号,一报文段的许哈字段值是301,携带的数据共有100字节,第一个字节的序号是301,最后一个字节的序号是400
**若确认号为=N,则表明到序号N-1为止的所有数据都已正确收到,
***由于序号字段有32位,可对4GB的数据进行编号
****4位最大就是1111=15,故TCP报文段首部的最大长度为60字节

最大报文段长度MSS
Maximum Segment Size,MSS是每一个TCP报文段中数据字段的最大长度。数据字段加上TCP首部才等于整个TCP报文段。所以MSS并不是整个TCP报文段的最大长度,而是”TCP报文段长度减去TCP首部长度”。MSS默认值是536字节,加上20字节的固定首部长度等于556字节。

其他选项
1. 窗口扩大选项 ——占 3 字节,其中有一个字节表示移位值 S。新的窗口值等于TCP 首部中的窗口位数增大到(16 + S),相当于把窗口值向左移动 S 位后获得实际的窗口大小。获得更高的吞吐量
2. 时间戳选项——占10 字节,其中最主要的字段时间戳值字段(4 字节)和时间戳回送回答字段(4 字节)。计算RTT,防止序号绕回
3. 选择确认选项SACK


5.6 TCP可靠传输的实现

A发送数据,B给出确认


5.6.1 以字节为单位的滑动窗口

发送窗口
在没有收到B的确认情况下,A可以连续把窗口内的数据都发送出去【不动】【前移】

发送窗口也可能向后收缩,TCP标准强烈不赞成这样做。

发送窗口通知窗口 = 已发送单位收到确认的字节数 + 允许发送单尚未发送的字节数可用窗口

缓存空间和序号空间都是有限的,并且都是循环使用。

发送缓存与接收缓存的作用

发送缓存用来暂时存放:

  • 发送应用程序传送给发送方 TCP 准备发送的数据;
  • TCP 已发送出但尚未收到确认的数据。

接收缓存用来暂时存放:

  • 按序到达的、但尚未被接收应用程序读取的数据;
  • 不按序到达的数据。

5.6.2 超时重传时间的选择

往返时延的方差很大

加权平均往返时间
RTTS又称为平滑的往返时间,S–>Smoothed

newRTTS=(1?α)?oldRTTS+α?(RTT)

超时重传时间
RTO,Retransmission Time-Out

往返时间的测量相当复杂
TCP 报文段 1 没有收到确认。重传(即报文段 2)后,收到了确认报文段 ACK。如何判定此确认报文段是对原来的报文段 1 的确认,还是对重传的报文段 2 的确认?

Karn 算法

在计算平均往返时间 RTT 时,只要报文段重传了,就不采用其往返时间样本。
这样得出的加权平均平均往返时间 RTTS 和超时重传时间 RTO 就较准确。

5.6.3 选择确认SACK

若收到的报文段无差错,只是未按序号,中间还缺少一些序号的数据,那么能否设法只传送串烧的数据而不重传已经正确到达接收方的数据?

选择重传
Selective ACK

如果这些字节的序号都在接收窗口之内,那么接收方就先收下这些数据,但要把这些信息准确地告诉发送方,使发送方不要再重复发送这些已收到的数据。


5.7 TCP的流量控制


5.7.1 利用滑动窗口实现流量控制

流量控制
就是让发送方的发送速率不要太快,要让接收方来得及接收。
流量控制的原理
原理这就是运用TCP报文段中的窗口大小字段来控制,发送方的发送窗口不可以大于接收方发回的窗口大小。
接收窗口rwnd
receiver window

发送方的发送窗口不能超过接收方给出的接收窗口的大小

TCP的窗口单位是字节,而不是报文段

大写的ACK
表示首部中的确认位
小写的ack
表示确认字段的值
持续计时器
persistence timer,考虑情况B向A发送了rwnd=0后,又发送了rwnd=400【这个报文段丢失了】。只要TCP连接的一方收到零窗口通知,就启动持续计时器,到期就发送一个零窗口的探测报文段,对方给出现在的窗口值。若窗口仍然是零,则收到这个报文段的一方就重新设置持续计时器。若窗口不是零,则死锁的僵局就可以打破了

5.7.2 必须考虑传输效率

控制TCP报文段的发送时机

  1. 缓存中数据达到MSS字节时就发送
  2. 应用进程负责,即TCP的push操作
  3. 发送方计时器到期发送
Nagele算法
广泛使用,只有收到前一个报文段的确认后才发送下一个报文段,当到达的数据已达到发送窗口大小的一般或MSS时,立即发送一个报文段。
糊涂窗口综合症
silly window syndrome,接收缓存已满,接收方进程每次只读取很少的数据。每次空出来一点点地方就通知发送方,浪费网络。

5.8 TCP的拥塞控制


5.8.1 拥塞控制的一般原理

资源

  • 带宽
  • 交换结点的缓存和处理机
拥塞
congestion,若对网络中某一资源的需求超过了该资源所能提供的可用部分,网络的性能就要变坏

>

若网络中有许多资源同时产生拥塞,网络的性能就要明显变坏,整个网络的吞吐量将随输入负荷的增大而下降。

拥塞常常趋于恶化。拥塞引起的重传并不会缓解网络的拥塞,反而会加剧网络的拥塞。

拥塞控制
防止过多的数据注入到网络中,这样可以使网络中的路由器或链路不致过载。
前提
网络能够承受现有的负荷

拥塞控制是个全局性的过程。流量控制往往指点对点通信量的控制,是个端到端的问题。流量控制所要做的就是抑制发送端发送数据,以便接收端来得及接收。

进行拥塞控制需要付出代价!

对比

拥塞控制 流量控制
带宽1Mb/s,1000台大型机连在网络上,发送速率100kb/s 带宽1000Gb/s,巨型机向PC发送数据速率1Gb/s


拥塞控制和流量控制之所以常常被弄混,是因为某些拥塞算法是向发送端发送控制报文,并告诉发送端,网络已经出现麻烦,必须放慢发送速率。这点又和流量控制室很相似的。

图5-23 拥塞控制所起的作用

提供的负载
offered load,代表单位时间内输入给忘了的分株数目
吞吐量
throughput,代表单位时间内从网络输出的分组数目

拥塞控制的一般原理
拥塞控制是很难设计的,因为它是一个动态的(而不是静态的)问题。
当前网络正朝着高速化的方向发展,这很容易出现缓存不够大而造成分组的丢失。但分组的丢失是网络发生拥塞的征兆而不是原因
在许多情况下,甚至正是拥塞控制本身成为引起网络性能恶化甚至发生死锁的原因。这点应特别引起重视。


开环控制和闭环控制
开环控制方法就是在设计网络时事先将有关发生拥塞的因素考虑周到,力求网络在工作时不产生拥塞。

闭环控制是基于反馈环路的概念。属于闭环控制的有以下几种措施:
1. 监测网络系统以便检测到拥塞在何时、何处发生。
2. 将拥塞发生的信息传送到可采取行动的地方。
3. 调整网络系统的运行以解决出现的问题。


5.8.2 几种拥塞控制方法

  • 慢开始,slow-start
  • 拥塞避免,congestion advoidance
  • 快重传,fast retransmit
  • 快恢复, fast recover

1. 慢开始和拥塞避免

拥塞窗口 cwnd
发送方维持一个叫做拥塞窗口 cwnd (congestion window)的状态变量。拥塞窗口的大小取决于网络的拥塞程度,并且动态地在变化。发送方让自己的发送窗口等于拥塞窗口。如再考虑到接收方的接收能力,则发送窗口还可能小于拥塞窗口
发送方控制拥塞窗口的原则
只要网络没有出现拥塞,拥塞窗口就再增大一些,以便把更多的分组发送出去。但只要网络出现拥塞,拥塞窗口就减小一些,以减少注入到网络中的分组数。

慢开始算法的原理
发送之前先探测一下,由大到小逐渐增大发送窗口即拥塞窗口数值

在主机刚刚开始发送报文段时可先设置拥塞窗口 cwnd = 1,即设置为一个最大报文段 MSS 的数值。
在每收到一个对新的报文段的确认后,将拥塞窗口加 1,即增加一个 MSS 的数值。
用这样的方法逐步增大发送端的拥塞窗口 cwnd,可以使分组注入到网络的速率更加合理。

图1.png

传输轮次(transmission round)
使用慢开始算法后,每经过一个传输轮次,拥塞窗口 cwnd 就加倍。一个传输轮次所经历的时间其实就是往返时间 RTT。“传输轮次”更加强调:把拥塞窗口 cwnd 所允许发送的报文段都连续发送出去,并收到了对已发送的最后一个字节的确认。例如,拥塞窗口 cwnd = 4,这时的往返时间 RTT 就是发送方连续发送 4 个报文段,并收到这 4 个报文段的确认,总共经历的时间。

慢开始的慢并不是指cwnd的增长速率慢,而是指在TCP开始发送报文段时先设置 cwnd=1 ,使得发送方在开始是只发送一个报文段,然后再逐渐增大cwnd。


就像一群小鸟慢慢起飞

设置慢开始门限状态变量ssthresh
慢开始门限 ssthresh 的用法如下:
当 cwnd < ssthresh 时,使用慢开始算法。
当 cwnd > ssthresh 时,停止使用慢开始算法而改用拥塞避免算法。
当 cwnd = ssthresh 时,既可使用慢开始算法,也可使用拥塞避免算法。

拥塞避免算法的思路
是让拥塞窗口 cwnd 缓慢地增大,即每经过一个往返时间 RTT 就把发送方的拥塞窗口 cwnd 加 1,而不是加倍,使拥塞窗口 cwnd 按线性规律缓慢增长

当网络出现拥塞时

无论在慢开始阶段还是在拥塞避免阶段,只要发送方判断网络出现拥塞(其根据就是没有按时收到确认),就要把慢开始门限 ssthresh 设置为出现拥塞时的发送方窗口值的一半(但不能小于2)。
然后把拥塞窗口 cwnd 重新设置为 1,执行慢开始算法。
这样做的目的就是要迅速减少主机发送到网络中的分组数,使得发生拥塞的路由器有足够时间把队列中积压的分组处理完毕。


乘法减小(multiplicative decrease)
是指不论在慢开始阶段还是拥塞避免阶段,只要出现一次超时(即出现一次网络拥塞),就把慢开始门限值 ssthresh 设置为当前的拥塞窗口值乘以 0.5。
当网络频繁出现拥塞时,ssthresh 值就下降得很快,以大大减少注入到网络中的分组数。

加法增大(additive increase)
是指执行拥塞避免算法后,在收到对所有报文段的确认后(即经过一个往返时间),就把拥塞窗口 cwnd增加一个 MSS 大小,使拥塞窗口缓慢增大,以防止网络过早出现拥塞。

必须强调指出

  • “拥塞避免”并非指完全能够避免了拥塞。利用以上的措施要完全避免网络拥塞还是不可能的。
  • “拥塞避免”是说在拥塞避免阶段把拥塞窗口控制为按线性规律增长,使网络比较不容易出现拥塞。

    1. 快重传和快恢复
快重传
快重传算法首先要求接收方每收到一个失序的报文段后就立即发出重复确认。这样做可以让发送方及早知道有报文段没有到达接收方。发送方只要一连收到三个重复确认就应当立即重传对方尚未收到的报文段。

不难看出,快重传并非取消重传计时器,而是在某些情况下可更早地重传丢失的报文段。

快恢复算法
1. 当发送端收到连续三个重复的确认时,就执行“乘法减小”算法,把慢开始门限 ssthresh 减半。但接下去不执行慢开始算法。
2. 由于发送方现在认为网络很可能没有发生拥塞,因此现在不执行慢开始算法,即拥塞窗口 cwnd 现在不设置为 1,而是设置为慢开始门限 ssthresh 减半后的数值,然后开始执行拥塞避免算法(“加法增大”),使拥塞窗口缓慢地线性增大。

从连续收到三个重复的确认转入拥塞避免

发送窗口的上限值

发送方的发送窗口的上限值应当取为接收方窗口 rwnd 和拥塞窗口 cwnd 这两个变量中较小的一个,即应按以下公式确定:
发送窗口的上限值 =? Min [rwnd, cwnd]
当 rwnd < cwnd 时,是接收方的接收能力限制发送窗口的最大值。
当 cwnd < rwnd 时,则是网络的拥塞限制发送窗口的最大值。

5.8.3 随机早期检测RED


5.9 TCP的运输连接管理

1. 运输连接的三个阶段

运输连接就有三个阶段,即:连接建立、数据传送和连接释放。运输连接的管理就是使运输连接的建立和释放都能正常地进行。

连接建立过程中要解决以下三个问题:
要使每一方能够确知对方的存在。
要允许双方协商一些参数(如最大报文段长度,最大窗口大小,服务质量等)。
能够对运输实体资源(如缓存大小,连接表中的项目等)进行分配。

客户-服务器方式

TCP 连接的建立都是采用客户服务器方式。
主动发起连接建立的应用进程叫做客户(client)。
被动等待连接建立的应用进程叫做服务器(server)。


5.9.1 TCP的连接建立

图5-31画出了TCP的建立连接的过程。假定主机A运行的是TCP客户程序,而B运行TCP服务器程序。最初两端的TCP进程都处于CLOSED(关闭)状态。图中在主机下面的方框分别是TCP进程所处的状态。请注意,A主动打开连接,而B被动打开连接。

图5-31 用三次握手建立TCP连接

B的TCP服务器进程先创建传输控制块TCB,准备结合搜客户进程的连接请求。然后服务器进程就处于LISTEN(收听)状态,等待客户的连接请求。如有,即作出响应)。
A的TCP客户进程也是首先创建传输控制模块TCB,然后向B发出连接请求报文段,这时首部中的同部位SYN=1,同时选择一个初始序列seq=x。TCP规定SYN报文段(即SYN=1的报文段)不能携带数据,但要消耗掉一个序号。这时,TCP客户进程进入SYN—SENT(同步已发送)状态。
B收到连接请求报文段后,如同意建立连接,则向A发送确认。在确认报文段中应把SYN位和ACK位都置为1,确认号是ack=x+1,同时也为自己选择一个初始序号seq=y。
TCP客户进程收到B的确认后,还要向B给出确认。确认报文段的ACK置1,确认号ack=y+1,而自己的序号seq=x+1。TCP的标准规定,ACK报文段可以携带数据。但如果不携带数据则不消耗序号,在这种情况下,下一个数据报文段的序号仍是seq=x+1。这时,TCP连接已经建立,A进入ESTABLISHED(已建立连接)状态。
当B收到A的确认后,也进入ESTABLISHED状态。

上面给出的连接建立过程叫做三次握手(three-way handshake),或三次联络。


5.9.2 TCP的连接释放

TCP连接释放过程比较复杂,我们仍结合双方状态的改变来阐明连接释放的过程。

数据传输结束后,通信的双方都可释放连接。现在A和B都处于ESTABLISHED状态(图5-32)。

图5-32 TCP连接释放的过程

A的应用进程先向其TCP发出连接释放报文段,并停止再发送数据,主动关闭TCP连接。A把连接释放报文段首部的FIN置1,其序号seq=u, 它等于前面已传送过的数据的最后一个字节的序号加1.这时A进入FIN-WAIT-1(终止等待1)状态,等待B的确认。请注意,TCP规定,FIN报文段即使不携带数据,它也消耗掉一个序号。
B收到连接释放报文段后即发出确认,确认号是ack=u+1,而这个报文段自己的序号是v,等于B面前已传送过的数据的最后一个字节的序号加1。然后B就进入CLOSE-WAIT(关闭等待)状态。TCP服务器进程这时应通知高层应用进程,因而从A到B这个方向的连接就释放了,这时的TCP连接处于半关闭(half-close)状态,即A已经没有数据要发生送了,但B若发送数据,A仍要接受。也就是说,从B到A这个方向的连接并未关闭。这个状态可能会次序一些时间。
A收到来自B的确认后,就进入FIN-WAIT-2(终止等待2)状态,等待B发出的连接释放报文段。
若B已经没有要向A发送的数据,其应用进程就通知TCP释放连接。这时B发出的连接释放报文段必须使FIN=1。现假定B的序号为w(在半关闭状态B可能又发送了一些数据)。B还必须重复上次已发送过的确认号ack=u+1。这时B就进入LAST-ACK(最后确认)状态,等待A的确认。
A在收到B的连接释放报文段后,必须对此发出确认。在确认报文段中把ACK置1,确认号ack=w+1,而自己的序号是seq=u+1(根据TCP标准,前面发送过的FIN报文段要消耗一个序号)。然后进入到TIME-WAIT(时间等待)状态。请注意,现在TCP连接还没有释放掉。必须经过时间等待计时器(TIME-WAIT timer)设置的时间2MSL后,A才进入到CLOSED状态。时间MSL叫做最长报文段寿命(Msximum Segment Lifetime),RFC793建议设为2分钟。但这完全是从工程上来考虑,对于现在的网络,MSL=2分钟可能太长了一些。因此TCP允许不同的实现可根据具体情况使用更小的MSL值。因此,从A进入到TIME-WAIT状态后,要经过4分钟才能进入到CLOSED状态,才能开始建立下一个新的连接。当A撤销相应的传输控制块TCB后,就结束了这次的TCP连接。


5.9.3 TCP的有限状态机

粗实线箭头表示对客户进程的正常变迁。
粗虚线箭头表示对服务器进程的正常变迁。
另一种细线箭头表示异常变迁。

图5-33 TCP的有限状态机



第5章 运输层

标签:

原文地址:http://blog.csdn.net/guan__hua/article/details/51591900

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!