存档

‘协议’ 分类的存档

Early Retransmit for TCP原理以及实现

2013年5月11日 没有评论

原创文章,转载请注明: 转载自pagefault

本文链接地址: Early Retransmit for TCP原理以及实现

Early Retransmit for TCP(ER)是google为了解决快重传的一些局限,从而对快重传(fast retransmit)做出的一些改变,其中ER在linux kernel 3.5进入了内核,他的paper在这里:

http://tools.ietf.org/html/rfc5827

首先我们要知道快重传算法的弱点很多,比如如果发送端接收不到足够数量(一般来说是3个)的ack,那么快重传算法就无法起作用,这个时候就只能等待RTO超时。ER主要就是为了解决这个问题的。在下面的条件下,就会导致收不到足够的ack。

  • 拥塞窗口比较小
  • 窗口中一个很大的段丢失或者在传输的结尾处发生了丢包

阅读全文…

Share
分类: kernel, 协议 标签: , ,

linux 内核tcp拥塞处理(二)

2012年10月21日 4 条评论

原创文章,转载请注明: 转载自pagefault

本文链接地址: linux 内核tcp拥塞处理(二)

这篇接的是我最早在javaeye的那篇blog. http://simohayha.iteye.com/blog/614258

首先我们要知道在linux下分为5个拥塞状态,定义如下:

enum tcp_ca_state {
	TCP_CA_Open = 0,
#define TCPF_CA_Open	(1<<TCP_CA_Open)
	TCP_CA_Disorder = 1,
#define TCPF_CA_Disorder (1<<TCP_CA_Disorder)
	TCP_CA_CWR = 2,
#define TCPF_CA_CWR	(1<<TCP_CA_CWR)
	TCP_CA_Recovery = 3,
#define TCPF_CA_Recovery (1<<TCP_CA_Recovery)
	TCP_CA_Loss = 4
#define TCPF_CA_Loss	(1<<TCP_CA_Loss)
}

TCP_CA_OPEN这个就是初始状态,也就是没有检测到任何拥塞的情况.
TCP_CA_Disorder 顾名思义,这个状态就是当第一次由于收到SACK或者重复的ack而检测到拥塞时,就进入这个状态.
TCP_CA_CWR 由于一些拥塞通知事件而导致拥塞窗口减小,然后就会进入这个状态。比如ECN,ICMP,本地设备拥塞。
TCP_CA_Recovery 当CWND减小
TCP_CA_Loss 超时或者SACK被拒绝,此时表示数据包丢失,因此进入这个状态.
阅读全文…

Share
分类: kernel, 协议 标签: , , ,

tcp拥塞算法vegas分析

2012年8月18日 没有评论

原创文章,转载请注明: 转载自pagefault

本文链接地址: tcp拥塞算法vegas分析

tcp的vegas算法是基于delay的一个拥塞控制算法,所谓基于delay也就是说窗口的变化只和RTT的变化相关。而传统的基于丢包的算法是窗口的变化和丢包相关.

先来看原理,paper地址在这里(94年提出来的),基本上linux的实现就是按照paper来实现的,注意Vegas它是第一个基于delay的拥塞算法.

http://citeseerx.ist.psu.edu/viewdoc/download?doi=10.1.1.100.9587&rep=rep1&type=pdf

它的主要思想是估计一段时间能够发送的数据量,然后和最终发送的数据量比较。如果预测要发送的数据没有被发送,那么就会被认为可能出现拥塞状况,如果这个状态持久,那么就减慢发送速度,并且这个算法不仅作用于拥塞避免状态,而且还作用于slow start状态。

不过vegas的缺点也是很明显.那就是他会被欺骗,也就是说本身这个正向的延迟就是比它期待的高,比如在tcp中,有可能正向反向做的不是相同的路径,那么当反向有拥塞的时候,就有问题了。也就是数据包ack返回给发送端的就是延迟的。此时就会导致Vegas降低拥塞窗口。这个问题就是基于延迟的拥塞算法的一个陷阱。不过在linux下vegas算法被打开,只有是正常状态才会被打开,而只要遇到异常(丢包/快重传..)就会使用经典的newreno算法.

并且如果连接都是Vegas算法,那么这些连接就是公平的,而如果有些是,有些不是,那么就不是公平的了,因此经典的tcp发送者是会尝试填满网络中的队列,而Vegas是尝试着保持队列为空。因此最终就会导致使用经典tcp拥塞算法的,发送的数据包越来越多,而Vegas的就会越来越慢。
阅读全文…

Share
分类: kernel, 协议 标签: , ,

tcp中RTO的计算以及linux下的实现

2012年8月1日 评论已被关闭

原创文章,转载请注明: 转载自pagefault

本文链接地址: tcp中RTO的计算以及linux下的实现

计算RTT以及RTO的代码比较简单,我们先来看原理,首先相关的rfc有两篇分别是rfc793以及rfc6298,而相关的paper有一篇,那就是Van Jacobson和Michael J. Karels的 Congestion Avoidance and Control这篇paper,这篇1988年的paper中描述的RTT计算方法,就是我们当前所使用的计算方法,可能有的操作系统有一点修改,不过基本的东西都一样。

首先RTT是什么,RTT简单来说,就是我发送一个数据包,然后对端回一个ack,那么当我接到ack之后,就能计算出从我发送出包到接到过了多久,这个时间就是RTT。RTT的计算是很简单的,就是一个时间差。

而RTO呢,RTO也就是tcp在发送一个数据包之后,会启动一个重传定时器,而RTO就是这个定时器的重传时间,那么这个时候,就有问题了,由于RTO是指的这次发送当前数据包所预估超时时间,那么RTO就需要一个很好的统计方法,来更好的预测这次的超时时间。

我们所能想到的最简单的方法,那就是取平均数,比如第一次RTT是500毫秒,第二次是800毫秒,那么第三次发送的时候,RTO就应该是650毫秒。其实经典的RTO计算方法和取平均有点类似,只不过因子不太一样,取平均的话,也就是老的RTO和新的RTT都是占50%的权重,而在经典的RTO计算中就有些变化了。
阅读全文…

Share
分类: kernel, 协议 标签: , ,

SPDY协议介绍

2011年10月22日 没有评论

原创文章,转载请注明: 转载自pagefault

本文链接地址: SPDY协议介绍

SPDY的主页: http://www.chromium.org/spdy

我主要看的是SPDY Protocol Drafts 3,这个草稿现在还没完成,google的人将它放在github上面: http://mbelshe.github.com/SPDY-Specification/

按照我的理解,SPDY只是在性能上对http做了很大的优化(比如它的核心思想就是尽量减少连接个数),而对于http的语义并没有做太大的修改(删除了一些头),基本上还是兼容http.

SPDY相对于HTTP,主要是有下面4个增强:

1 Multiplexed requests

对于一条SPDY连接,并发的发起多少request没有任何限制,其实也就是可以拥有多条stream(接下来会介绍stream).

2 Prioritized requests

提供具有优先级的请求(同一个SPDY 连接)。这个主要是解决了HTTP中的pipeline请求是严格的FIFO的。比如有多个request,如果先到的一个request处理时间比较长,则后面的request会被阻塞住,而在SPDY中,就会优先处理高优先级的stream中的frame(后面会介绍这个)

3 Compressed headers

在SPDY中,头是可以被压缩的。

4 Server pushed streams

Server可以主动的push数据给client,而不需要客户端的request.

———————————————————————

然后来看SPDY中的一些基本的概念。

首先在SPDY中的第一个概念是session,这里的Session也就是代表一条tcp连接。

在SPDY中第二个概念就是frame,frame也就是SPDY中server和client之间交互的数据。 SPDY Framing Layer是在tcp之上的一层,而当client端和server端建立连接之后,他们之间的交互数据就是frame。 SPDY中分为2种类型的frame,分别是control frame 和data frame。(具体frame的结构,我这里就不介绍了,可以去看协议中的介绍).而每种frame都有对应的flag.

第三个是Stream的概念,一条tcp连接,可以有多条的Stream,每个stream都有一个stream id.在SPDY中有3种control frame来控制Stream的生命周期,分别是:

SYN_STREAM – Open a new stream
SYN_REPLY – Remote acknowledgement of a new, open stream
RST_STREAM – Close a stream

可以看到,和tcp建立连接很像。这里要注意Stream也是有优先级的。如果一端发送一个设置了FLAG_FIN标记的frame,则这个stream将会成为半关闭的.(也就是不会再发送数据,而只能够接收收据,这个和tcp的半关闭很类似).如果一个发送端发送了带有FLAG_FIN标记的flag,如果它再次发送数据,则将会收到一个RST_STREAM的frame.

要注意,只有synstreamframe(建立一个stream的控制frame)才拥有priority,也就是说在SPDY中优先级只到stream这个级别,只有某个stream中的request会被优先处理,而同一个stream中的frame则类似于http的行为。

从stream我们可以看到相比较于http,SPDY可以很打程度上减少建立的连接的数目,因为每个stream其实就类似于一个虚拟的连接。

假设现在需要做一个http->spdy的代理,当一个http request过来的时候,如果这个request没有body,则将会发送一个设置了FLAG_FIN标记的frame,而对应的http头都将会放入到这个frame中。可以看下go 中对于SYN frame的结构定义:

type SynStreamFrame struct {
	CFHeader             ControlFrameHeader
	StreamId             uint32
	AssociatedToStreamId uint32
	// Note, only 2 highest bits currently used
	// Rest of Priority is unused.
	Priority uint16
	Headers  http.Header
}

这里可以看到http的头就是紧挨着SPDY的frame,不过这里注意由于http是文本协议,所以最终SPDY还是会将Headers转成二进制的。

reponse类似,下面就是go中的synReplay frame,可以看到和syn类似:

// SynReplyFrame is the unpacked, in-memory representation of a SYN_REPLY frame.
type SynReplyFrame struct {
	CFHeader ControlFrameHeader
	StreamId uint32
	Headers  http.Header
}

最后来看下pushed stream,在SPDY中,server能够发送多个replay给一个request,就是说有时候server能够知道需要发送多个资源给client,此时就需要server push资源给client,而如果没有这个特性,则需要client不断的发多个request来请求多个资源。这样子就极大的RT延迟。

在SPDY中,如果server要push数据给client,则它必须选择一个已经存在的stream id,并且server端必须得设置一个比较高的优先级,以便于客户端能够迅速的发现pushed stream,然后响应它。server端能push的内容必须是客户端能够通过一个get请求所得到的资源(也就是push过来的syn frame必须包含一些头信息)。

Share
分类: SPDY, 协议 标签: , ,

linux下tcp选项TCP_DEFER_ACCEPT详解

2011年8月28日 没有评论

原创文章,转载请注明: 转载自pagefault

本文链接地址: linux下tcp选项TCP_DEFER_ACCEPT详解

TCP_DEFER_ACCEPT这个选项可能大家都知道,不过我这里会从源码和数据包来详细的分析这个选项。要注意,这里我所使用的内核版本是3.0.

首先看man手册中的介绍(man 7 tcp):

TCP_DEFER_ACCEPT (since Linux 2.4)
Allow a listener to be awakened only when data arrives on the socket. Takes an integer value (seconds), this can bound the maximum number of attempts TCP will make to complete the connection. This option should not be used in code intended to be portable.

我先来简单介绍下,这个选项主要是针对server端的服务器,一般来说我们三次握手,当客户端发送syn,然后server端接收到,然后发送syn + ack,然后client接收到syn+ack之后,再次发送ack(client进入establish状态),最终server端收到最后一个ack,进入establish状态。

而当正确的设置了TCP_DEFER_ACCEPT选项之后,server端会在接收到最后一个ack之后,并不进入establish状态,而只是将这个socket标记为acked,然后丢掉这个ack。此时server端这个socket还是处于syn_recved,然后接下来就是等待client发送数据, 而由于这个socket还是处于syn_recved,因此此时就会被syn_ack定时器所控制,对syn ack进行重传,而重传次数是由我们设置TCP_DEFER_ACCEPT传进去的值以及TCP_SYNCNT选项,proc文件系统的tcp_synack_retries一起来决定的(后面分析源码会看到如何来计算这个值).而我们知道我们传递给TCP_DEFER_ACCEPT的是秒,而在内核里面会将这个东西转换为重传次数.

这里要注意,当重传次数超过限制之后,并且当最后一个ack到达时,下一次导致超时的synack定时器还没启动,那么这个defer的连接将会被加入到establish队列,然后通知上层用户。这个也是符合man里面所说的(Takes an integer value (seconds), this can bound the maximum number of attempts TCP will make to complete the connection.) 也就是最终会完成这个连接.

阅读全文…

Share
分类: kernel, 协议 标签: , ,

rfc 2988(Computing TCP’s Retransmission Timer)简介

2011年6月11日 没有评论

原创文章,转载请注明: 转载自pagefault

本文链接地址: rfc 2988(Computing TCP’s Retransmission Timer)简介

rfc 2988是描述tcp如何计算定时器的一个rfc,是2000年发布的,而最近2988 已经被更新:
http://tools.ietf.org/html/draft-paxson-tcpm-rfc2988bis-02

并且google的jerry chu(2988bis的作者之一)最近在内核提交一个patch,主要是用来修改3次握手时的初始化rto的值(以及rfc 2988bis的一些改变),当前的内核默认是3,而patch修改这个值为1,主要的修改依据是rfc2988.
阅读全文…

Share
分类: 协议 标签: , , ,

TFO(tcp fast open)简介

2011年5月9日 没有评论

原创文章,转载请注明: 转载自pagefault

本文链接地址: TFO(tcp fast open)简介

这个是google的几个人提交的一个rfc,是对tcp的一个增强,简而言之就是在3次握手的时候也用来交换数据。这个东西google内部已经在使用了,不过内核的相关patch还没有开源出来,chrome也支持这个了(client的内核必须支持). 要注意,TFO默认是关闭的,因为它有一些特定的适用场景,下面我会介绍到。

相关的rfc:
http://www.ietf.org/id/draft-cheng-tcpm-fastopen-00.txt
相关的ppt:
http://www.ietf.org/proceedings/80/slides/tcpm-3.pdf

我来简单的介绍下这个东西.想了解详细的设计和实现还是要去看上面的rfc。

1 http的keepalive受限于idle时间,据google的统计(chrome浏览器),尽管chrome开启了http的keepalive(chrome是4分钟),可是依然有35%的请求是重新发起一条连接。而三次握手会造成一个RTT的延迟,因此TFO的目标就是去除这个延迟,在三次握手期间也能交换数据。

2 RFC793允许在syn数据包中带数据,可是它要求这些数据必须当3次握手之后才能给应用程序,这样子做主要是两个原因,syn带数据可能会引起2个问题。第一个是有可能会有前一个连接的重复或者老的数据的连接(syn+data的数据),这个其实就是三次握手的必要性所决定的。第二个就是最重要的,也就是安全方面的,为了防止攻击。

3 而在TFO中是这样解决上面两个问题的,第一个问题,TFO选择接受重复的syn,它的观点就是有些应用是能够容忍重复的syn+data的(幂等的操作),也就是交给应用程序自己去判断需不需要打开TFO。比如http的query操作(它是幂等的).可是比如post这种类型的,就不能使用TFO,因为它有可能会改变server的内容. 因此TFO默认是关闭的,内核会提供一个接口为当前的tcp连接打开TFO。为了解决第二个问题,TFO会有一个Fast Open Cookie(这个是TFO最核心的一个东西),其实也就是一个tag。

4 启用TFO的tcp连接也很简单,就是首先client会在一个请求中(非tfo的),请求一个Fast Open Cookie(放到tcp option中),然后在下次的三次握手中使用这个cookie(这个请求就会在3次握手的时候交换数据).

下面的张图就能很好的表示出启用了TFO的tcp连接:
6d8bf08d-6cb0-3ce2-8905-53ea1a49daa6

Share
分类: 协议 标签: , ,

路由的基本概念介绍

2011年3月27日 没有评论

原创文章,转载请注明: 转载自pagefault

本文链接地址: 路由的基本概念介绍

这里主要是针对linux下的路由一些基本概念.

1 路由是位于L3(ip层)。

2 路由表(routing table)也叫做Forwarding Information Base(FIB).

3 路由器之间通过路由协议(routing protocols)进行信息的交换.

4 一个路由表包含了一大堆的路由,一个路由就是存储了一些传输数据包到给定地址的必须信息。在linux里面一个路由主要包括了这三个参数,分别是目的网络地址,需要使用的设备以及下一跳网关(next hop gateway)。
阅读全文…

Share
分类: kernel, 协议 标签: , , ,