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。

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

阅读全文

linux 内核tcp拥塞处理(二)

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

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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
  
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被拒绝,此时表示数据包丢失,因此进入这个状态.

阅读全文

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的就会越来越慢。

阅读全文

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计算中就有些变化了。

阅读全文

nginx least_conn 模块源码剖析

nginx在1.3.1添加了一个新模块 least_conn,也就是我们常说的最少连接负载均衡算法,简单来说就是每次选择的都是当前最少连接的一个server(这个最少连接不是全局的,是每个进程都有自己的一个统计列表)。

在看最少连接模块之前需要对round robin模块有一定的了解,这里我就不对round robin模块进行分析了,想要看这块代码,可以去我们组 卫岳的blog的这篇文章

http://blog.sina.com.cn/s/blog_7303a1dc01014i0j.html

ok,接下来就来看这个模块,首先来看如何打开least_conn模块:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
  
static ngx_command_t ngx_http_upstream_least_conn_commands[] = {

{ ngx_string("least_conn"),

NGX_HTTP_UPS_CONF|NGX_CONF_NOARGS,

ngx_http_upstream_least_conn,

0,

0,

NULL },

ngx_null_command

};

阅读全文

打开tcp_tw_recycle引起的一个问题

今天普空说了一个问题就是如果设置了tcp_tw_recycle ,那么如果客户端是NAT出来的,那么就可能会出现连接被直接rst的情况。然后我google了下,在内核列表也有人说了这个问题 https://lkml.org/lkml/2008/11/15/67

The big problem is that both are incompatible with NAT. So if you

ever talk to any NATed clients don’t use it.

阅读全文

一个out of socket memory的问题

今天同事遇到一个问题,就是server(read hat 5, kernel 2.6.18)的dmesg打出了下面两个信息

TCP: too many of orphaned sockets

Out of socket memory

一般我们看到这个信息,第一反应肯定是需要调节tcp_mem(/proc/sys/net/ipv4)了,可是根据当时的内存使用情况,使用的内存并没有超过 tcp_mem。然后我先去看了最新的内核代码,3.4.4,其中涉及到socket 内存报警在这里

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
  
bool tcp_check_oom(struct sock *sk, int shift)

{

bool too_many_orphans, out_of_socket_memory;

too_many_orphans = tcp_too_many_orphans(sk, shift);

out_of_socket_memory = tcp_out_of_memory(sk);

if (too_many_orphans && net_ratelimit())

pr_info("too many orphaned sockets\n");

if (out_of_socket_memory && net_ratelimit())

pr_info("out of memory &#8212; consider tuning tcp_mem\n");

return too_many_orphans || out_of_socket_memory;

}

上面的代码很简单,就是如果孤儿socket太多,则打印警告,然后如果socket memory超过限制,也打印出警告。

阅读全文

intel万兆网卡驱动简要分析

这里分析的驱动代码是给予linux kernel 3.4.4

对应的文件在drivers/net/ethernet/intel 目录下,这个分析不涉及到很细节的地方,主要目的是理解下数据在协议栈和驱动之间是如何交互的。

首先我们知道网卡都是pci设备,因此这里每个网卡驱动其实就是一个pci驱动。并且intel这里是把好几个万兆网卡(82599/82598/x540)的驱动做在一起的。

首先我们来看对应的pci_driver的结构体,这里每个pci驱动都是一个pci_driver的结构体,而这里是多个万兆网卡共用这个结构体ixgbe_driver.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
  
static struct pci_driver ixgbe_driver = {

.name = ixgbe_driver_name,

.id_table = ixgbe_pci_tbl,

.probe = ixgbe_probe,

.remove = __devexit_p(ixgbe_remove),

#ifdef CONFIG_PM

.suspend = ixgbe_suspend,

.resume = ixgbe_resume,

#endif

.shutdown = ixgbe_shutdown,

.err_handler = &ixgbe_err_handler

};

阅读全文

mochiweb源码分析(二)

这次主要来看mochiweb如何处理http协议以及如何将外部模块加载到mochiweb框架中。

首先在上一篇的分析最后,我们知道当accept句柄之后,mochiweb最终会调用call_loop方法,那么我们就从call_loop开始

[erlang]

call_loop({M, F}, Socket) ->

M:F(Socket);

call_loop({M, F, [A1]}, Socket) ->

M:F(Socket, A1);

call_loop({M, F, A}, Socket) ->

erlang:apply(M, F, [Socket | A]);

call_loop(Loop, Socket) ->

Loop(Socket).

[/erlang]

阅读全文

mochiweb源码分析(一)

这篇主要分析下mochiweb的整体结构。

我这里看的代码是github上的最新代码( https://github.com/mochi/mochiweb )。

由于mochiweb是一个框架,因此我们就从他自带的简单例子入手,来看他是如何工作的。我们就从keepalive.erl开始。

首先来看这个模块的start函数,也就是它的启动函数:

[erlang]

-define(LOOP, {?MODULE, loop}).

start(Options = [{port, _Port}]) ->

mochiweb_http:start([{name, ?MODULE}, {loop, ?LOOP} | Options]).

[/erlang]

可以看到启动函数非常简单,那就是直接调用mochiweb_http模块的start函数。那么我们整个分析流程,就从这个模块的start函数开始。这里要注意,我们可以看到是将当前回调模块的loop函数传递给了mochiweb_http,这就给后续调用回调,提供了接口。

[erlang]

%% @spec start(Options) -> ServerRet

%% Options = [option()]

%% Option = {name, atom()} | {ip, string() | tuple()} | {backlog, integer()}

%% | {nodelay, boolean()} | {acceptor_pool_size, integer()}

%% | {ssl, boolean()} | {profile_fun, undefined | (Props) -> ok}

%% | {link, false}

%% @doc Start a mochiweb server.

%% profile_fun is used to profile accept timing.

%% After each accept, if defined, profile_fun is called with a proplist of a subset of the mochiweb_socket_server state and timing information.

%% The proplist is as follows: [{name, Name}, {port, Port}, {active_sockets, ActiveSockets}, {timing, Timing}].

%% @end

start(Options) ->

mochiweb_socket_server:start(parse_options(Options)).

[/erlang]

阅读全文