linux重置网络协议,Linux 内核网络协议栈 ------ tcp_ack 函数处理接收到的ACK包之后 ....
注意 tcp_ack 是來處理接收到的ACK的,那么到底怎么去做呢?看下面:
先還上把tcp_sock的結(jié)構(gòu)放在這里,下面一些數(shù)據(jù)的分析需要用到:
structtcp_sock?{
/*?inet_connection_sock?has?to?be?the?first?member?of?tcp_sock?*/
structinet_connection_sock?????inet_conn;
u16?????tcp_header_len;/*?Bytes?of?tcp?header?to?send??????????*///?tcp頭部長度
u16?????xmit_size_goal_segs;/*?Goal?for?segmenting?output?packets?*///?分段數(shù)據(jù)包的數(shù)量
/*
*??????Header?prediction?flags
*??????0x5?10?<
*/
__be32??pred_flags;//?頭部預(yù)置位(用于檢測頭部標(biāo)識位處理ACK和PUSH之外還有沒有其他位,從而判斷是不是可以使用快速路徑處理數(shù)據(jù))
/*
*??????RFC793?variables?by?their?proper?names.?This?means?you?can
*??????read?the?code?and?the?spec?side?by?side?(and?laugh?...)
*??????See?RFC793?and?RFC1122.?The?RFC?writes?these?in?capitals.
*/
u32?????rcv_nxt;/*?What?we?want?to?receive?next?????????*///?下一個想要收到的第一個數(shù)據(jù)的字節(jié)編號
u32?????copied_seq;/*?Head?of?yet?unread?data??????????????*///?沒還有讀出的數(shù)據(jù)的頭
u32?????rcv_wup;/*?rcv_nxt?on?last?window?update?sent???*///?rcv_nxt在最后一個窗口更新時的值
u32?????snd_nxt;/*?Next?sequence?we?send????????????????*///?下一個發(fā)送的第一個字節(jié)編號
u32?????snd_una;/*?First?byte?we?want?an?ack?for????????*///?對于發(fā)出的數(shù)據(jù),都需要對方的ACK,這里標(biāo)示當(dāng)前需要被確認(rèn)的第一個字節(jié)
u32?????snd_sml;/*?Last?byte?of?the?most?recently?transmitted?small?packet?*///?最近發(fā)送的小數(shù)據(jù)包的最后一個字節(jié)
u32?????rcv_tstamp;/*?timestamp?of?last?received?ACK?(for?keepalives)?*///?最后一次接收到ACK的時間戳
u32?????lsndtime;/*?timestamp?of?last?sent?data?packet?(for?restart?window)?*///?最后一次發(fā)送數(shù)據(jù)包時間戳
u32?????tsoffset;/*?timestamp?offset?*///?時間戳偏移
structlist_head?tsq_node;/*?anchor?in?tsq_tasklet.head?list?*///
unsignedlongtsq_flags;
//?注意下面這個ucopy:就是將用戶數(shù)據(jù)從skb中拿出來放進去,然后傳給應(yīng)用進程!!!
/*?Data?for?direct?copy?to?user?*/
struct{
structsk_buff_head?????prequeue;//?預(yù)處理隊列
structtask_struct??????*task;//?預(yù)處理進程
structiovec????????????*iov;//?用戶程序(應(yīng)用程序)接收數(shù)據(jù)的緩沖區(qū)
intmemory;//?用于預(yù)處理計數(shù)
intlen;//?預(yù)處理長度
#ifdef?CONFIG_NET_DMA
/*?members?for?async?copy?*/
structdma_chan?????????*dma_chan;
intwakeup;
structdma_pinned_list??*pinned_list;
dma_cookie_t????????????dma_cookie;
#endif
}?ucopy;
//??snd_wl1:記錄發(fā)送窗口更新時,造成窗口更新的那個數(shù)據(jù)報的第一個序號。?它主要用于在下一次判斷是否需要更新發(fā)送窗口。
u32?????snd_wl1;/*?Sequence?for?window?update???????????*///?窗口更新序列號(?每一次收到確認(rèn)之后都會改變?)
u32?????snd_wnd;/*?The?window?we?expect?to?receive??????*///?我們期望收到的窗口
u32?????max_window;/*?Maximal?window?ever?seen?from?peer???*///?從對方接收到的最大窗口
u32?????mss_cache;/*?Cached?effective?mss,?not?including?SACKS?*///?有效的MSS,SACKS不算
u32?????window_clamp;/*?Maximal?window?to?advertise??????????*///?對外公布的最大的窗口
u32?????rcv_ssthresh;/*?Current?window?clamp?????????????????*///?當(dāng)前窗口值
u16?????advmss;/*?Advertised?MSS???????????????????????*///?對外公布的MSS
u8??????unused;
u8??????nonagle?????:?4,/*?Disable?Nagle?algorithm??????????????*///?Nagle算法是否有效
thin_lto????:?1,/*?Use?linear?timeouts?for?thin?streams?*///?使用線性超時處理
thin_dupack?:?1,/*?Fast?retransmit?on?first?dupack??????*///?收到第一個重復(fù)的ACK的時候是否快速重傳
repair??????:?1,
frto????????:?1;/*?F-RTO?(RFC5682)?activated?in?CA_Loss?*/
u8??????repair_queue;
u8??????do_early_retrans:1,/*?Enable?RFC5827?early-retransmit??*///?是否可以使用之前的重傳
syn_data:1,/*?SYN?includes?data?*///?data中是否包含SYN
syn_fastopen:1,/*?SYN?includes?Fast?Open?option?*///?SYN選項
syn_data_acked:1;/*?data?in?SYN?is?acked?by?SYN-ACK?*///?SYN回復(fù)
u32?????tlp_high_seq;/*?snd_nxt?at?the?time?of?TLP?retransmit.?*///?tlp重傳時候snd_nxt的值
/*?RTT?measurement?*/
u32?????srtt;/*?smoothed?round?trip?time?<
u32?????mdev;/*?medium?deviation?????????????????????*///
u32?????mdev_max;/*?maximal?mdev?for?the?last?rtt?period?*///?最大mdev
u32?????rttvar;/*?smoothed?mdev_max????????????????????*/
u32?????rtt_seq;/*?sequence?number?to?update?rttvar?????*/
u32?????packets_out;/*?Packets?which?are?"in?flight"????????*///?已經(jīng)發(fā)出去的尚未收到確認(rèn)的包
u32?????retrans_out;/*?Retransmitted?packets?out????????????*///?重傳的包
u16?????urg_data;/*?Saved?octet?of?OOB?data?and?control?flags?*///?OOB數(shù)據(jù)和控制位
u8??????ecn_flags;/*?ECN?status?bits.?????????????????????*///?ECN狀態(tài)位
u8??????reordering;/*?Packet?reordering?metric.????????????*///?包重排度量
u32?????snd_up;/*?Urgent?pointer???????????????*///?緊急指針
u8??????keepalive_probes;/*?num?of?allowed?keep?alive?probes???*/
/*
*??????Options?received?(usually?on?last?packet,?some?only?on?SYN?packets).
*/
structtcp_options_received?rx_opt;//?tcp接收選項
/*
*??????Slow?start?and?congestion?control?(see?also?Nagle,?and?Karn?&?Partridge)
*/
u32?????snd_ssthresh;/*?Slow?start?size?threshold????????????*///?慢啟動的開始大小
u32?????snd_cwnd;/*?Sending?congestion?window????????????*///?發(fā)送阻塞窗口
u32?????snd_cwnd_cnt;/*?Linear?increase?counter??????????????*///?線性增長計數(shù)器(為了窗口的擴大)
u32?????snd_cwnd_clamp;/*?Do?not?allow?snd_cwnd?to?grow?above?this?*///?snd_cwnd值不可以超過這個門限
u32?????snd_cwnd_used;
u32?????snd_cwnd_stamp;
u32?????prior_cwnd;/*?Congestion?window?at?start?of?Recovery.?*///?在剛剛恢復(fù)時候的阻塞窗口大小
u32?????prr_delivered;/*?Number?of?newly?delivered?packets?to?//?在恢復(fù)期間接收方收到的包
*?receiver?in?Recovery.?*/
u32?????prr_out;/*?Total?number?of?pkts?sent?during?Recovery.?*///?在恢復(fù)期間發(fā)出去的包
u32?????rcv_wnd;/*?Current?receiver?window??????????????*///?當(dāng)前接收窗口
u32?????write_seq;/*?Tail(+1)?of?data?held?in?tcp?send?buffer?*///?tcp發(fā)送buf中數(shù)據(jù)的尾部
u32?????notsent_lowat;/*?TCP_NOTSENT_LOWAT?*/
u32?????pushed_seq;/*?Last?pushed?seq,?required?to?talk?to?windows?*///?push序列
u32?????lost_out;/*?Lost?packets?????????????????*///?丟失的包
u32?????sacked_out;/*?SACK'd?packets???????????????*///?SACK包
u32?????fackets_out;/*?FACK'd?packets???????????????*///?FACK包
u32?????tso_deferred;
/*?from?STCP,?retrans?queue?hinting?*/
structsk_buff*?lost_skb_hint;//?用于丟失的包
structsk_buff?*retransmit_skb_hint;//?用于重傳的包
structsk_buff_head?????out_of_order_queue;/*?Out?of?order?segments?go?here?*///?接收到的無序的包保存
/*?SACKs?data,?these?2?need?to?be?together?(see?tcp_options_write)?*/
structtcp_sack_block?duplicate_sack[1];/*?D-SACK?block?*/
structtcp_sack_block?selective_acks[4];/*?The?SACKS?themselves*/
structtcp_sack_block?recv_sack_cache[4];
structsk_buff?*highest_sack;/*?skb?just?after?the?highest
*?skb?with?SACKed?bit?set
*?(validity?guaranteed?only?if
*?sacked_out?>?0)
*/
intlost_cnt_hint;
u32?????retransmit_high;/*?L-bits?may?be?on?up?to?this?seqno?*/
u32?????lost_retrans_low;/*?Sent?seq?after?any?rxmit?(lowest)?*/
u32?????prior_ssthresh;/*?ssthresh?saved?at?recovery?start?????*/
u32?????high_seq;/*?snd_nxt?at?onset?of?congestion???????*/
u32?????retrans_stamp;/*?Timestamp?of?the?last?retransmit,
*?also?used?in?SYN-SENT?to?remember?stamp?of
*?the?first?SYN.?*/
u32?????undo_marker;/*?tracking?retrans?started?here.?*/
intundo_retrans;/*?number?of?undoable?retransmissions.?*/
u32?????total_retrans;/*?Total?retransmits?for?entire?connection?*/
u32?????urg_seq;/*?Seq?of?received?urgent?pointer?*/
unsignedintkeepalive_time;/*?time?before?keep?alive?takes?place?*/
unsignedintkeepalive_intvl;/*?time?interval?between?keep?alive?probes?*/
intlinger2;
/*?Receiver?side?RTT?estimation?*/
struct{
u32?????rtt;
u32?????seq;
u32?????time;
}?rcv_rtt_est;
/*?Receiver?queue?space?*/
struct{
intspace;
u32?????seq;
u32?????time;
}?rcvq_space;
/*?TCP-specific?MTU?probe?information.?*/
struct{
u32???????????????probe_seq_start;
u32???????????????probe_seq_end;
}?mtu_probe;
u32?????mtu_info;/*?We?received?an?ICMP_FRAG_NEEDED?/?ICMPV6_PKT_TOOBIG
*?while?socket?was?owned?by?user.
*/
#ifdef?CONFIG_TCP_MD5SIG
/*?TCP?AF-Specific?parts;?only?used?by?MD5?Signature?support?so?far?*/
conststructtcp_sock_af_ops????*af_specific;
/*?TCP?MD5?Signature?Option?information?*/
structtcp_md5sig_info??__rcu?*md5sig_info;
#endif
/*?TCP?fastopen?related?information?*/
structtcp_fastopen_request?*fastopen_req;
/*?fastopen_rsk?points?to?request_sock?that?resulted?in?this?big
*?socket.?Used?to?retransmit?SYNACKs?etc.
*/
structrequest_sock?*fastopen_rsk;
};
struct tcp_sock {
/* inet_connection_sock has to be the first member of tcp_sock */
struct inet_connection_sock inet_conn;
u16 tcp_header_len; /* Bytes of tcp header to send */ // tcp頭部長度
u16 xmit_size_goal_segs; /* Goal for segmenting output packets */// 分段數(shù)據(jù)包的數(shù)量
/*
* Header prediction flags
* 0x5?10 << 16 + snd_wnd in net byte order
*/
__be32 pred_flags; // 頭部預(yù)置位(用于檢測頭部標(biāo)識位處理ACK和PUSH之外還有沒有其他位,從而判斷是不是可以使用快速路徑處理數(shù)據(jù))
/*
* RFC793 variables by their proper names. This means you can
* read the code and the spec side by side (and laugh ...)
* See RFC793 and RFC1122. The RFC writes these in capitals.
*/
u32 rcv_nxt; /* What we want to receive next */ // 下一個想要收到的第一個數(shù)據(jù)的字節(jié)編號
u32 copied_seq; /* Head of yet unread data */ // 沒還有讀出的數(shù)據(jù)的頭
u32 rcv_wup; /* rcv_nxt on last window update sent */ // rcv_nxt在最后一個窗口更新時的值
u32 snd_nxt; /* Next sequence we send */ // 下一個發(fā)送的第一個字節(jié)編號
u32 snd_una; /* First byte we want an ack for */ // 對于發(fā)出的數(shù)據(jù),都需要對方的ACK,這里標(biāo)示當(dāng)前需要被確認(rèn)的第一個字節(jié)
u32 snd_sml; /* Last byte of the most recently transmitted small packet */ // 最近發(fā)送的小數(shù)據(jù)包的最后一個字節(jié)
u32 rcv_tstamp; /* timestamp of last received ACK (for keepalives) */ // 最后一次接收到ACK的時間戳
u32 lsndtime; /* timestamp of last sent data packet (for restart window) */ // 最后一次發(fā)送數(shù)據(jù)包時間戳
u32 tsoffset; /* timestamp offset */// 時間戳偏移
struct list_head tsq_node; /* anchor in tsq_tasklet.head list */ //
unsigned long tsq_flags;
// 注意下面這個ucopy:就是將用戶數(shù)據(jù)從skb中拿出來放進去,然后傳給應(yīng)用進程!!!
/* Data for direct copy to user */
struct {
struct sk_buff_head prequeue; // 預(yù)處理隊列
struct task_struct *task; // 預(yù)處理進程
struct iovec *iov; // 用戶程序(應(yīng)用程序)接收數(shù)據(jù)的緩沖區(qū)
int memory; // 用于預(yù)處理計數(shù)
int len; // 預(yù)處理長度
#ifdef CONFIG_NET_DMA
/* members for async copy */
struct dma_chan *dma_chan;
int wakeup;
struct dma_pinned_list *pinned_list;
dma_cookie_t dma_cookie;
#endif
} ucopy;
// ?snd_wl1:記錄發(fā)送窗口更新時,造成窗口更新的那個數(shù)據(jù)報的第一個序號。 它主要用于在下一次判斷是否需要更新發(fā)送窗口。
u32 snd_wl1; /* Sequence for window update */ // 窗口更新序列號( 每一次收到確認(rèn)之后都會改變 )
u32 snd_wnd; /* The window we expect to receive */ // 我們期望收到的窗口
u32 max_window; /* Maximal window ever seen from peer */ // 從對方接收到的最大窗口
u32 mss_cache; /* Cached effective mss, not including SACKS */ // 有效的MSS,SACKS不算
u32 window_clamp; /* Maximal window to advertise */ // 對外公布的最大的窗口
u32 rcv_ssthresh; /* Current window clamp */ // 當(dāng)前窗口值
u16 advmss; /* Advertised MSS */ // 對外公布的MSS
u8 unused;
u8 nonagle : 4,/* Disable Nagle algorithm? */ // Nagle算法是否有效
thin_lto : 1,/* Use linear timeouts for thin streams */ // 使用線性超時處理
thin_dupack : 1,/* Fast retransmit on first dupack */ // 收到第一個重復(fù)的ACK的時候是否快速重傳
repair : 1,
frto : 1;/* F-RTO (RFC5682) activated in CA_Loss */
u8 repair_queue;
u8 do_early_retrans:1,/* Enable RFC5827 early-retransmit */ // 是否可以使用之前的重傳
syn_data:1, /* SYN includes data */ // data中是否包含SYN
syn_fastopen:1, /* SYN includes Fast Open option */ // SYN選項
syn_data_acked:1;/* data in SYN is acked by SYN-ACK */ // SYN回復(fù)
u32 tlp_high_seq; /* snd_nxt at the time of TLP retransmit. */ // tlp重傳時候snd_nxt的值
/* RTT measurement */
u32 srtt; /* smoothed round trip time << 3 */ // 往返時間
u32 mdev; /* medium deviation */ //
u32 mdev_max; /* maximal mdev for the last rtt period */ // 最大mdev
u32 rttvar; /* smoothed mdev_max */
u32 rtt_seq; /* sequence number to update rttvar */
u32 packets_out; /* Packets which are "in flight" */ // 已經(jīng)發(fā)出去的尚未收到確認(rèn)的包
u32 retrans_out; /* Retransmitted packets out */ // 重傳的包
u16 urg_data; /* Saved octet of OOB data and control flags */ // OOB數(shù)據(jù)和控制位
u8 ecn_flags; /* ECN status bits. */ // ECN狀態(tài)位
u8 reordering; /* Packet reordering metric. */ // 包重排度量
u32 snd_up; /* Urgent pointer */ // 緊急指針
u8 keepalive_probes; /* num of allowed keep alive probes */
/*
* Options received (usually on last packet, some only on SYN packets).
*/
struct tcp_options_received rx_opt; // tcp接收選項
/*
* Slow start and congestion control (see also Nagle, and Karn & Partridge)
*/
u32 snd_ssthresh; /* Slow start size threshold */ // 慢啟動的開始大小
u32 snd_cwnd; /* Sending congestion window */ // 發(fā)送阻塞窗口
u32 snd_cwnd_cnt; /* Linear increase counter */ // 線性增長計數(shù)器(為了窗口的擴大)
u32 snd_cwnd_clamp; /* Do not allow snd_cwnd to grow above this */ // snd_cwnd值不可以超過這個門限
u32 snd_cwnd_used;
u32 snd_cwnd_stamp;
u32 prior_cwnd; /* Congestion window at start of Recovery. */ // 在剛剛恢復(fù)時候的阻塞窗口大小
u32 prr_delivered; /* Number of newly delivered packets to // 在恢復(fù)期間接收方收到的包
* receiver in Recovery. */
u32 prr_out; /* Total number of pkts sent during Recovery. */ // 在恢復(fù)期間發(fā)出去的包
u32 rcv_wnd; /* Current receiver window */ // 當(dāng)前接收窗口
u32 write_seq; /* Tail(+1) of data held in tcp send buffer */ // tcp發(fā)送buf中數(shù)據(jù)的尾部
u32 notsent_lowat; /* TCP_NOTSENT_LOWAT */
u32 pushed_seq; /* Last pushed seq, required to talk to windows */ // push序列
u32 lost_out; /* Lost packets */ // 丟失的包
u32 sacked_out; /* SACK'd packets */ // SACK包
u32 fackets_out; /* FACK'd packets */ // FACK包
u32 tso_deferred;
/* from STCP, retrans queue hinting */
struct sk_buff* lost_skb_hint; // 用于丟失的包
struct sk_buff *retransmit_skb_hint; // 用于重傳的包
struct sk_buff_head out_of_order_queue; /* Out of order segments go here */ // 接收到的無序的包保存
/* SACKs data, these 2 need to be together (see tcp_options_write) */
struct tcp_sack_block duplicate_sack[1]; /* D-SACK block */
struct tcp_sack_block selective_acks[4]; /* The SACKS themselves*/
struct tcp_sack_block recv_sack_cache[4];
struct sk_buff *highest_sack; /* skb just after the highest
* skb with SACKed bit set
* (validity guaranteed only if
* sacked_out > 0)
*/
int lost_cnt_hint;
u32 retransmit_high; /* L-bits may be on up to this seqno */
u32 lost_retrans_low; /* Sent seq after any rxmit (lowest) */
u32 prior_ssthresh; /* ssthresh saved at recovery start */
u32 high_seq; /* snd_nxt at onset of congestion */
u32 retrans_stamp; /* Timestamp of the last retransmit,
* also used in SYN-SENT to remember stamp of
* the first SYN. */
u32 undo_marker; /* tracking retrans started here. */
int undo_retrans; /* number of undoable retransmissions. */
u32 total_retrans; /* Total retransmits for entire connection */
u32 urg_seq; /* Seq of received urgent pointer */
unsigned int keepalive_time; /* time before keep alive takes place */
unsigned int keepalive_intvl; /* time interval between keep alive probes */
int linger2;
/* Receiver side RTT estimation */
struct {
u32 rtt;
u32 seq;
u32 time;
} rcv_rtt_est;
/* Receiver queue space */
struct {
int space;
u32 seq;
u32 time;
} rcvq_space;
/* TCP-specific MTU probe information. */
struct {
u32 probe_seq_start;
u32 probe_seq_end;
} mtu_probe;
u32 mtu_info; /* We received an ICMP_FRAG_NEEDED / ICMPV6_PKT_TOOBIG
* while socket was owned by user.
*/
#ifdef CONFIG_TCP_MD5SIG
/* TCP AF-Specific parts; only used by MD5 Signature support so far */
const struct tcp_sock_af_ops *af_specific;
/* TCP MD5 Signature Option information */
struct tcp_md5sig_info __rcu *md5sig_info;
#endif
/* TCP fastopen related information */
struct tcp_fastopen_request *fastopen_req;
/* fastopen_rsk points to request_sock that resulted in this big
* socket. Used to retransmit SYNACKs etc.
*/
struct request_sock *fastopen_rsk;
};
關(guān)于窗口的操作值snd_una,snd_wnd等可以看下面的圖形:
先看發(fā)送窗口:
再看接收窗口:
還有tcp_skb_cb結(jié)構(gòu):
structtcp_skb_cb?{
union{
structinet_skb_parm????h4;
#if?defined(CONFIG_IPV6)?||?defined?(CONFIG_IPV6_MODULE)
structinet6_skb_parm???h6;
#endif
}?header;/*?For?incoming?frames??????*/
__u32???????seq;//?當(dāng)前tcp包的第一個序列號
__u32???????end_seq;//?表示結(jié)束的序列號:seq?+?FIN?+?SYN?+?數(shù)據(jù)長度len
__u32???????when;//?用于計算RTT
__u8????????flags;//?tcp頭的flag
__u8????????sacked;//?SACK/FACK的狀態(tài)flag或者是sack?option的偏移
__u32???????ack_seq;//?ack(確認(rèn))的序列號
};
struct tcp_skb_cb {
union {
struct inet_skb_parmh4;
#if defined(CONFIG_IPV6) || defined (CONFIG_IPV6_MODULE)
struct inet6_skb_parmh6;
#endif
} header;/* For incoming frames*/
__u32seq; // 當(dāng)前tcp包的第一個序列號
__u32end_seq; // 表示結(jié)束的序列號:seq + FIN + SYN + 數(shù)據(jù)長度len
__u32when; // 用于計算RTT
__u8flags; // tcp頭的flag
__u8sacked; // SACK/FACK的狀態(tài)flag或者是sack option的偏移
__u32ack_seq; // ack(確認(rèn))的序列號
};
關(guān)于tcp頭的標(biāo)識(flags)可以取:
#define?TCPCB_FLAG_FIN??????0x01??//?FIN?結(jié)束符
#define?TCPCB_FLAG_SYN??????0x02??//?SYN?握手
#define?TCPCB_FLAG_RST??????0x04??//?RST?重置
#define?TCPCB_FLAG_PSH??????0x08??//?PSH?接收方要立即處理
#define?TCPCB_FLAG_ACK??????0x10??//?ACK?ack段有效
#define?TCPCB_FLAG_URG??????0x20??//?URG?緊急指針
#define?TCPCB_FLAG_ECE??????0x40??//?ECE?有擁塞情況(可能是傳播線路上的擁塞,例如路由器提供的信息)
#define?TCPCB_FLAG_CWR??????0x80??//?CWR?(發(fā)生某種擁塞,例如ICMP源抑制、本地設(shè)備擁塞)
#define TCPCB_FLAG_FIN0x01 // FIN 結(jié)束符
#define TCPCB_FLAG_SYN0x02 // SYN 握手
#define TCPCB_FLAG_RST0x04 // RST 重置
#define TCPCB_FLAG_PSH0x08 // PSH 接收方要立即處理
#define TCPCB_FLAG_ACK0x10 // ACK ack段有效
#define TCPCB_FLAG_URG0x20 // URG 緊急指針
#define TCPCB_FLAG_ECE0x40 // ECE 有擁塞情況(可能是傳播線路上的擁塞,例如路由器提供的信息)
#define TCPCB_FLAG_CWR0x80 // CWR (發(fā)生某種擁塞,例如ICMP源抑制、本地設(shè)備擁塞)
sack的標(biāo)識:
#define?TCPCB_SACKED_ACKED??0x01?//?tcp的cb結(jié)構(gòu)上是被sack確認(rèn)的
#define?TCPCB_SACKED_RETRANS????0x02?//?重傳幀
#define?TCPCB_LOST??????0x04?//?丟失
#define?TCPCB_TAGBITS???????0x07?//?tag?bits??
#define?TCPCB_EVER_RETRANS??0x80
#define?TCPCB_RETRANS???????(TCPCB_SACKED_RETRANS|TCPCB_EVER_RETRANS)
#define TCPCB_SACKED_ACKED0x01 // tcp的cb結(jié)構(gòu)上是被sack確認(rèn)的
#define TCPCB_SACKED_RETRANS0x02 // 重傳幀
#define TCPCB_LOST0x04 // 丟失
#define TCPCB_TAGBITS0x07 // tag bits ?
#define TCPCB_EVER_RETRANS0x80
#define TCPCB_RETRANS(TCPCB_SACKED_RETRANS|TCPCB_EVER_RETRANS)
OK,回到tcp_ack函數(shù),函數(shù)的主要功能是:
1 更新重傳隊列。
2 更新發(fā)送窗口。
3 從sack的信息或者重復(fù)ack來決定是否進入擁塞模式。
/*?This?routine?deals?with?incoming?acks,?but?not?outgoing?ones.?*/
staticinttcp_ack(structsock?*sk,structsk_buff?*skb,intflag)
{
structinet_connection_sock?*icsk?=?inet_csk(sk);//?獲得連接sock
structtcp_sock?*tp?=?tcp_sk(sk);//?獲得tcp_sock
u32?prior_snd_una?=?tp->snd_una;//?獲得未發(fā)送確認(rèn)的序號
u32?ack_seq?=?TCP_SKB_CB(skb)->seq;//?獲得數(shù)據(jù)序號
u32?ack?=?TCP_SKB_CB(skb)->ack_seq;//?獲得ack序號(用于確認(rèn)的序號)
u32?prior_in_flight;
u32?prior_fackets;
intprior_packets;
intfrto_cwnd?=?0;
/*?If?the?ack?is?newer?than?sent?or?older?than?previous?acks
*?then?we?can?probably?ignore?it.
*/
if(after(ack,?tp->snd_nxt))//?如果確認(rèn)序號比我還下一個準(zhǔn)備發(fā)送的序號還要大,即確認(rèn)了我們尚未發(fā)送的數(shù)據(jù),那么顯然不合理
gotouninteresting_ack;//?沒有意義的ACK
if(before(ack,?prior_snd_una))//?如果ack確認(rèn)比我期望的確認(rèn)序號小,那么可能是以前老的ack,丟棄!!!
gotoold_ack;//?老的ack
if(after(ack,?prior_snd_una))//?如果ack確認(rèn)比我期望的第一個ack要大,但是經(jīng)過上面我們還知道沒有超過我沒有發(fā)送的數(shù)據(jù)序號,范圍
flag?|=?FLAG_SND_UNA_ADVANCED;//?那么設(shè)置標(biāo)識~
if(sysctl_tcp_abc)?{//?是否設(shè)置了tcp_abc,若有則我們不需要對每個ack確認(rèn)都要擁塞避免,所以我們需要計算已經(jīng)ack(確認(rèn))的字節(jié)數(shù)。
if(icsk->icsk_ca_state?
tp->bytes_acked?+=?ack?-?prior_snd_una;//?已經(jīng)(確定)ack的字節(jié)數(shù)增大了(?ack?-?prior_snd_una?)大小
elseif(icsk->icsk_ca_state?==?TCP_CA_Loss)
/*?we?assume?just?one?segment?left?network?*/
tp->bytes_acked?+=?min(ack?-?prior_snd_una,
tp->mss_cache);
}
prior_fackets?=?tp->fackets_out;//?得到fack的數(shù)據(jù)包的字節(jié)數(shù)
prior_in_flight?=?tcp_packets_in_flight(tp);//?計算還在傳輸?shù)臄?shù)據(jù)段的字節(jié)數(shù),下面會說手這個函數(shù)!(?1?)
if(!(flag?&?FLAG_SLOWPATH)?&&?after(ack,?prior_snd_una))?{//?如果不是“慢路徑”?&&?ack確認(rèn)比其需要的第一個大(正確的確認(rèn)序號)
/*?Window?is?constant,?pure?forward?advance.
*?No?more?checks?are?required.
*?Note,?we?use?the?fact?that?SND.UNA>=SND.WL2.
*/
tcp_update_wl(tp,?ack,?ack_seq);//?需要更新sock中的snd_wl1字段:tp->snd_wl1?=?ack_seq;(?記錄造成發(fā)送窗口更新的第一個數(shù)據(jù)?)
tp->snd_una?=?ack;//?snd_una更新為已經(jīng)確認(rèn)的序列號!下一次期待從這里開始的確認(rèn)!!!
flag?|=?FLAG_WIN_UPDATE;//?窗口更新標(biāo)識
tcp_ca_event(sk,?CA_EVENT_FAST_ACK);//?重要函數(shù)!!!進入擁塞操作!這個函數(shù)最后看,這里處理的是“正常的ACK”事件(999999)
NET_INC_STATS_BH(LINUX_MIB_TCPHPACKS);
}else{
if(ack_seq?!=?TCP_SKB_CB(skb)->end_seq)//?如果不相等,那么說明還是帶有數(shù)據(jù)一起的~不僅僅是一個ACK的包
flag?|=?FLAG_DATA;//?說明還是有數(shù)據(jù)的~
else
NET_INC_STATS_BH(LINUX_MIB_TCPPUREACKS);//?否則僅僅是ACK的包
flag?|=?tcp_ack_update_window(sk,?skb,?ack,?ack_seq);//?下面需要更新發(fā)送窗口~(2)
if(TCP_SKB_CB(skb)->sacked)//?然后判斷是否有sack段,有的話,我們進入sack段的處理。
flag?|=?tcp_sacktag_write_queue(sk,?skb,?prior_snd_una);//?~~~~~處理SACK(選擇確認(rèn)),以后單獨解釋
if(TCP_ECN_rcv_ecn_echo(tp,?tcp_hdr(skb)))//?判斷是否有ecn標(biāo)記,如果有的話,設(shè)置ecn標(biāo)記。
flag?|=?FLAG_ECE;//?ECE?也是用于判斷是否阻塞情況
tcp_ca_event(sk,?CA_EVENT_SLOW_ACK);//?重要函數(shù)!!!進入擁塞操作!這個函數(shù)最后看,這里處理“其他ACK”事件(999999)
}
/*?We?passed?data?and?got?it?acked,?remove?any?soft?error
*?log.?Something?worked...
*/
sk->sk_err_soft?=?0;
tp->rcv_tstamp?=?tcp_time_stamp;
prior_packets?=?tp->packets_out;//?獲得發(fā)出去沒有收到確認(rèn)的包數(shù)量
if(!prior_packets)//?如果為0,則可能是0窗口探測包
gotono_queue;
/*?See?if?we?can?take?anything?off?of?the?retransmit?queue.?*/
flag?|=?tcp_clean_rtx_queue(sk,?prior_fackets);//?清理重傳隊列中的已經(jīng)確認(rèn)的數(shù)據(jù)段。(3)
if(tp->frto_counter)//?處理F-RTO?(4)
frto_cwnd?=?tcp_process_frto(sk,?flag);//?處理超時重傳,暫時先不多說
/*?Guarantee?sacktag?reordering?detection?against?wrap-arounds?*/
if(before(tp->frto_highmark,?tp->snd_una))
tp->frto_highmark?=?0;
if(tcp_ack_is_dubious(sk,?flag))?{//?判斷ack是否可疑,其實本質(zhì)就是判斷可不可以增大擁塞窗口,下面會有詳細(xì)解釋(5)
/*?Advance?CWND,?if?state?allows?this.?*/
if((flag?&?FLAG_DATA_ACKED)?&&?!frto_cwnd?&&//?如果是數(shù)據(jù)確認(rèn)包
tcp_may_raise_cwnd(sk,?flag))//?檢測flag以及是否需要update擁塞窗口的大小!!!(6)--->被懷疑也有可能增大窗口哦~~~
tcp_cong_avoid(sk,?ack,?prior_in_flight);//?為真則更新?lián)砣翱?擁塞避免算法(7)--->如果允許增大窗口,那么擁塞算法處理窗口
tcp_fastretrans_alert(sk,?prior_packets?-?tp->packets_out,//?這里進入擁塞狀態(tài)的處理,非常重要的函數(shù)(對于擁塞處理來說)!!!(8)
flag);//?處理完窗口變化之后,進入快速重傳處理!!!
}else{//?沒有被懷疑(說明是正常的確認(rèn)ACK)
if((flag?&?FLAG_DATA_ACKED)?&&?!frto_cwnd)
tcp_cong_avoid(sk,?ack,?prior_in_flight);//?如果沒有被懷疑,直接擁塞算法處理窗口變化
}
if((flag?&?FLAG_FORWARD_PROGRESS)?||?!(flag?&?FLAG_NOT_DUP))
dst_confirm(sk->sk_dst_cache);
return1;
no_queue:
icsk->icsk_probes_out?=?0;
/*?If?this?ack?opens?up?a?zero?window,?clear?backoff.??It?was
*?being?used?to?time?the?probes,?and?is?probably?far?higher?than
*?it?needs?to?be?for?normal?retransmission.
*/
if(tcp_send_head(sk))//?這里判斷發(fā)送緩沖區(qū)是否為空,如果不為空,則進入判斷需要重啟keepalive定時器還是關(guān)閉定時器
tcp_ack_probe(sk);//?處理定時器函數(shù)~(暫時不看)
return1;
old_ack:
if(TCP_SKB_CB(skb)->sacked)//?如果有sack標(biāo)識
tcp_sacktag_write_queue(sk,?skb,?prior_snd_una);//?進入sack段處理(暫時不看)
uninteresting_ack:
SOCK_DEBUG(sk,"Ack?%u?out?of?%u:%u\n",?ack,?tp->snd_una,?tp->snd_nxt);//?沒有意義的包~
return0;
}
/* This routine deals with incoming acks, but not outgoing ones. */
static int tcp_ack(struct sock *sk, struct sk_buff *skb, int flag)
{
struct inet_connection_sock *icsk = inet_csk(sk); // 獲得連接sock
struct tcp_sock *tp = tcp_sk(sk); // 獲得tcp_sock
u32 prior_snd_una = tp->snd_una; // 獲得未發(fā)送確認(rèn)的序號
u32 ack_seq = TCP_SKB_CB(skb)->seq; // 獲得數(shù)據(jù)序號
u32 ack = TCP_SKB_CB(skb)->ack_seq; // 獲得ack序號(用于確認(rèn)的序號)
u32 prior_in_flight;
u32 prior_fackets;
int prior_packets;
int frto_cwnd = 0;
/* If the ack is newer than sent or older than previous acks
* then we can probably ignore it.
*/
if (after(ack, tp->snd_nxt)) // 如果確認(rèn)序號比我還下一個準(zhǔn)備發(fā)送的序號還要大,即確認(rèn)了我們尚未發(fā)送的數(shù)據(jù),那么顯然不合理
goto uninteresting_ack; // 沒有意義的ACK
if (before(ack, prior_snd_una)) // 如果ack確認(rèn)比我期望的確認(rèn)序號小,那么可能是以前老的ack,丟棄!!!
goto old_ack; // 老的ack
if (after(ack, prior_snd_una)) // 如果ack確認(rèn)比我期望的第一個ack要大,但是經(jīng)過上面我們還知道沒有超過我沒有發(fā)送的數(shù)據(jù)序號,范圍
flag |= FLAG_SND_UNA_ADVANCED; // 那么設(shè)置標(biāo)識~
if (sysctl_tcp_abc) { // 是否設(shè)置了tcp_abc,若有則我們不需要對每個ack確認(rèn)都要擁塞避免,所以我們需要計算已經(jīng)ack(確認(rèn))的字節(jié)數(shù)。
if (icsk->icsk_ca_state < TCP_CA_CWR)
tp->bytes_acked += ack - prior_snd_una; // 已經(jīng)(確定)ack的字節(jié)數(shù)增大了( ack - prior_snd_una )大小
else if (icsk->icsk_ca_state == TCP_CA_Loss)
/* we assume just one segment left network */
tp->bytes_acked += min(ack - prior_snd_una,
tp->mss_cache);
}
prior_fackets = tp->fackets_out; // 得到fack的數(shù)據(jù)包的字節(jié)數(shù)
prior_in_flight = tcp_packets_in_flight(tp); // 計算還在傳輸?shù)臄?shù)據(jù)段的字節(jié)數(shù),下面會說手這個函數(shù)!( 1 )
if (!(flag & FLAG_SLOWPATH) && after(ack, prior_snd_una)) { // 如果不是“慢路徑” && ack確認(rèn)比其需要的第一個大(正確的確認(rèn)序號)
/* Window is constant, pure forward advance.
* No more checks are required.
* Note, we use the fact that SND.UNA>=SND.WL2.
*/
tcp_update_wl(tp, ack, ack_seq); // 需要更新sock中的snd_wl1字段:tp->snd_wl1 = ack_seq;( 記錄造成發(fā)送窗口更新的第一個數(shù)據(jù) )
tp->snd_una = ack; // snd_una更新為已經(jīng)確認(rèn)的序列號!下一次期待從這里開始的確認(rèn)!!!
flag |= FLAG_WIN_UPDATE; // 窗口更新標(biāo)識
tcp_ca_event(sk, CA_EVENT_FAST_ACK); // 重要函數(shù)!!!進入擁塞操作!這個函數(shù)最后看,這里處理的是“正常的ACK”事件(999999)
NET_INC_STATS_BH(LINUX_MIB_TCPHPACKS);
} else {
if (ack_seq != TCP_SKB_CB(skb)->end_seq) // 如果不相等,那么說明還是帶有數(shù)據(jù)一起的~不僅僅是一個ACK的包
flag |= FLAG_DATA; // 說明還是有數(shù)據(jù)的~
else
NET_INC_STATS_BH(LINUX_MIB_TCPPUREACKS); // 否則僅僅是ACK的包
flag |= tcp_ack_update_window(sk, skb, ack, ack_seq); // 下面需要更新發(fā)送窗口~(2)
if (TCP_SKB_CB(skb)->sacked) // 然后判斷是否有sack段,有的話,我們進入sack段的處理。
flag |= tcp_sacktag_write_queue(sk, skb, prior_snd_una); // ~~~~~處理SACK(選擇確認(rèn)),以后單獨解釋
if (TCP_ECN_rcv_ecn_echo(tp, tcp_hdr(skb))) // 判斷是否有ecn標(biāo)記,如果有的話,設(shè)置ecn標(biāo)記。
flag |= FLAG_ECE; // ECE 也是用于判斷是否阻塞情況
tcp_ca_event(sk, CA_EVENT_SLOW_ACK); // 重要函數(shù)!!!進入擁塞操作!這個函數(shù)最后看,這里處理“其他ACK”事件(999999)
}
/* We passed data and got it acked, remove any soft error
* log. Something worked...
*/
sk->sk_err_soft = 0;
tp->rcv_tstamp = tcp_time_stamp;
prior_packets = tp->packets_out; // 獲得發(fā)出去沒有收到確認(rèn)的包數(shù)量
if (!prior_packets) // 如果為0,則可能是0窗口探測包
goto no_queue;
/* See if we can take anything off of the retransmit queue. */
flag |= tcp_clean_rtx_queue(sk, prior_fackets); // 清理重傳隊列中的已經(jīng)確認(rèn)的數(shù)據(jù)段。(3)
if (tp->frto_counter) // 處理F-RTO (4)
frto_cwnd = tcp_process_frto(sk, flag); // 處理超時重傳,暫時先不多說
/* Guarantee sacktag reordering detection against wrap-arounds */
if (before(tp->frto_highmark, tp->snd_una))
tp->frto_highmark = 0;
if (tcp_ack_is_dubious(sk, flag)) { // 判斷ack是否可疑,其實本質(zhì)就是判斷可不可以增大擁塞窗口,下面會有詳細(xì)解釋(5)
/* Advance CWND, if state allows this. */
if ((flag & FLAG_DATA_ACKED) && !frto_cwnd && // 如果是數(shù)據(jù)確認(rèn)包
tcp_may_raise_cwnd(sk, flag)) // 檢測flag以及是否需要update擁塞窗口的大小!!!(6)--->被懷疑也有可能增大窗口哦~~~
tcp_cong_avoid(sk, ack, prior_in_flight); // 為真則更新?lián)砣翱?擁塞避免算法(7)--->如果允許增大窗口,那么擁塞算法處理窗口
tcp_fastretrans_alert(sk, prior_packets - tp->packets_out, // 這里進入擁塞狀態(tài)的處理,非常重要的函數(shù)(對于擁塞處理來說)!!!(8)
flag); // 處理完窗口變化之后,進入快速重傳處理!!!
} else { // 沒有被懷疑(說明是正常的確認(rèn)ACK)
if ((flag & FLAG_DATA_ACKED) && !frto_cwnd)
tcp_cong_avoid(sk, ack, prior_in_flight); // 如果沒有被懷疑,直接擁塞算法處理窗口變化
}
if ((flag & FLAG_FORWARD_PROGRESS) || !(flag & FLAG_NOT_DUP))
dst_confirm(sk->sk_dst_cache);
return 1;
no_queue:
icsk->icsk_probes_out = 0;
/* If this ack opens up a zero window, clear backoff. It was
* being used to time the probes, and is probably far higher than
* it needs to be for normal retransmission.
*/
if (tcp_send_head(sk)) // 這里判斷發(fā)送緩沖區(qū)是否為空,如果不為空,則進入判斷需要重啟keepalive定時器還是關(guān)閉定時器
tcp_ack_probe(sk); // 處理定時器函數(shù)~(暫時不看)
return 1;
old_ack:
if (TCP_SKB_CB(skb)->sacked) // 如果有sack標(biāo)識
tcp_sacktag_write_queue(sk, skb, prior_snd_una); // 進入sack段處理(暫時不看)
uninteresting_ack:
SOCK_DEBUG(sk, "Ack %u out of %u:%u\n", ack, tp->snd_una, tp->snd_nxt);// 沒有意義的包~
return 0;
}
-----------------------------------------------------------------------------------------------------------------------------------------
看看這個函數(shù):tcp_packets_in_flight,就是計算還在傳輸中的字節(jié)數(shù):
staticinlineunsignedinttcp_packets_in_flight(conststructtcp_sock?*tp)
{
returntp->packets_out?-?tcp_left_out(tp)?+?tp->retrans_out;
}
static inline unsigned int tcp_packets_in_flight(const struct tcp_sock *tp)
{
return tp->packets_out - tcp_left_out(tp) + tp->retrans_out;
}
staticinlineunsignedinttcp_left_out(conststructtcp_sock?*tp)
{
returntp->sacked_out?+?tp->lost_out;
}
static inline unsigned int tcp_left_out(const struct tcp_sock *tp)
{
return tp->sacked_out + tp->lost_out;
}
我們從tcp_sock可以知道:其實就是返回 packets_out - sacked_out - lost_out + retrans_out,就是還沒有到達對方的數(shù)據(jù)段的字節(jié)數(shù)
-----------------------------------------------------------------------------------------------------------------------------------------
下面我們看一下“發(fā)送窗口”的更新tcp_ack_update_window:
staticinttcp_ack_update_window(structsock?*sk,structsk_buff?*skb,?u32?ack,
u32?ack_seq)
{
structtcp_sock?*tp?=?tcp_sk(sk);//?獲得tcp_sock
intflag?=?0;
u32?nwin?=?ntohs(tcp_hdr(skb)->window);//?獲得skb發(fā)送方的可以接收的窗口值
if(likely(!tcp_hdr(skb)->syn))//?如果不是建立連接時候,即是普通傳遞數(shù)據(jù)時候,窗口縮放
nwin?<<=?tp->rx_opt.snd_wscale;//?接收方要求對窗口進行縮放
//?下面正式更新窗口
if(tcp_may_update_window(tp,?ack,?ack_seq,?nwin))?{//?可能需要更新窗口大小,在函數(shù)tcp_may_update_window中實際處理(1)
flag?|=?FLAG_WIN_UPDATE;//?窗口更新成功
tcp_update_wl(tp,?ack,?ack_seq);//?更新snd_wl1
if(tp->snd_wnd?!=?nwin)?{//?如果發(fā)送窗口!=縮放后的新窗口(注意skb發(fā)送方的接收窗口和本tp的發(fā)送窗口應(yīng)該一致)
tp->snd_wnd?=?nwin;//?改變窗口值
/*?Note,?it?is?the?only?place,?where
*?fast?path?is?recovered?for?sending?TCP.
*/
tp->pred_flags?=?0;
tcp_fast_path_check(sk);//?檢驗是否能夠開啟“快速路徑”(2)看下面
if(nwin?>?tp->max_window)?{//?如果調(diào)整之后的窗口大于從對方接收到的最大的窗口值
tp->max_window?=?nwin;//?調(diào)整為小的
tcp_sync_mss(sk,?inet_csk(sk)->icsk_pmtu_cookie);//?改變mss_cache
}
}
}
tp->snd_una?=?ack;//?下一個第一個需要確認(rèn)就是所有當(dāng)前已經(jīng)確認(rèn)序號之后~~~~
returnflag;
}
static int tcp_ack_update_window(struct sock *sk, struct sk_buff *skb, u32 ack,
u32 ack_seq)
{
struct tcp_sock *tp = tcp_sk(sk); // 獲得tcp_sock
int flag = 0;
u32 nwin = ntohs(tcp_hdr(skb)->window); // 獲得skb發(fā)送方的可以接收的窗口值
if (likely(!tcp_hdr(skb)->syn)) // 如果不是建立連接時候,即是普通傳遞數(shù)據(jù)時候,窗口縮放
nwin <<= tp->rx_opt.snd_wscale; // 接收方要求對窗口進行縮放
// 下面正式更新窗口
if (tcp_may_update_window(tp, ack, ack_seq, nwin)) { // 可能需要更新窗口大小,在函數(shù)tcp_may_update_window中實際處理(1)
flag |= FLAG_WIN_UPDATE; // 窗口更新成功
tcp_update_wl(tp, ack, ack_seq); // 更新snd_wl1
if (tp->snd_wnd != nwin) { // 如果發(fā)送窗口!=縮放后的新窗口(注意skb發(fā)送方的接收窗口和本tp的發(fā)送窗口應(yīng)該一致)
tp->snd_wnd = nwin; // 改變窗口值
/* Note, it is the only place, where
* fast path is recovered for sending TCP.
*/
tp->pred_flags = 0;
tcp_fast_path_check(sk); // 檢驗是否能夠開啟“快速路徑”(2)看下面
if (nwin > tp->max_window) { // 如果調(diào)整之后的窗口大于從對方接收到的最大的窗口值
tp->max_window = nwin; // 調(diào)整為小的
tcp_sync_mss(sk, inet_csk(sk)->icsk_pmtu_cookie); // 改變mss_cache
}
}
}
tp->snd_una = ack; // 下一個第一個需要確認(rèn)就是所有當(dāng)前已經(jīng)確認(rèn)序號之后~~~~
return flag;
}
下面看這個函數(shù):tcp_may_update_window
/*?Check?that?window?update?is?acceptable.
*?The?function?assumes?that?snd_una<=ack<=snd_next.
*///?這個函數(shù)是檢查窗口是否可變,只要確認(rèn)ack在snd_una~下一個需要發(fā)送的數(shù)據(jù)之間,就是需要改變窗口的!
staticinlineinttcp_may_update_window(conststructtcp_sock?*tp,
constu32?ack,constu32?ack_seq,
constu32?nwin)
{
return(after(ack,?tp->snd_una)?||//?snd_una<=ack
after(ack_seq,?tp->snd_wl1)?||//?看上面的窗口圖可以知道
(ack_seq?==?tp->snd_wl1?&&?nwin?>?tp->snd_wnd));//?調(diào)整的新窗口大于原始發(fā)送窗口
}
/* Check that window update is acceptable.
* The function assumes that snd_una<=ack<=snd_next.
*/ // 這個函數(shù)是檢查窗口是否可變,只要確認(rèn)ack在snd_una~下一個需要發(fā)送的數(shù)據(jù)之間,就是需要改變窗口的!
static inline int tcp_may_update_window(const struct tcp_sock *tp,
const u32 ack, const u32 ack_seq,
const u32 nwin)
{
return (after(ack, tp->snd_una) || // snd_una<=ack
after(ack_seq, tp->snd_wl1) || // 看上面的窗口圖可以知道
(ack_seq == tp->snd_wl1 && nwin > tp->snd_wnd)); // 調(diào)整的新窗口大于原始發(fā)送窗口
}
看一下檢測是否可以進入“快速路徑”處理函數(shù):tcp_fast_path_check
staticinlinevoidtcp_fast_path_check(structsock?*sk)
{
structtcp_sock?*tp?=?tcp_sk(sk);
if(skb_queue_empty(&tp->out_of_order_queue)?&&
tp->rcv_wnd?&&
atomic_read(&sk->sk_rmem_alloc)?sk_rcvbuf?&&
!tp->urg_data)
tcp_fast_path_on(tp);
}
static inline void tcp_fast_path_check(struct sock *sk)
{
struct tcp_sock *tp = tcp_sk(sk);
if (skb_queue_empty(&tp->out_of_order_queue) &&
tp->rcv_wnd &&
atomic_read(&sk->sk_rmem_alloc) < sk->sk_rcvbuf &&
!tp->urg_data)
tcp_fast_path_on(tp);
}
能夠進入“快速路徑”處理的基本條件是:
1 :是否ofo(亂序包隊列)隊列為空,如果不為空也就是說有亂序數(shù)據(jù)不可以進入快速路徑。
2: 當(dāng)前的接收窗口是否大于0.,如果不是,不可以進入。
3 :當(dāng)前的已經(jīng)提交的數(shù)據(jù)包大小是否小于接收緩沖區(qū)的大小,能夠放的下才可以進入快速路徑。
4: 是否含有urgent 數(shù)據(jù),不含有才可以進入快速路徑。
再看看 為tp開啟快速路徑函數(shù)tcp_fast_path_on:
staticinlinevoidtcp_fast_path_on(structtcp_sock?*tp)
{
__tcp_fast_path_on(tp,?tp->snd_wnd?>>?tp->rx_opt.snd_wscale);
}
static inline void tcp_fast_path_on(struct tcp_sock *tp)
{
__tcp_fast_path_on(tp, tp->snd_wnd >> tp->rx_opt.snd_wscale);
}
staticinlinevoid__tcp_fast_path_on(structtcp_sock?*tp,?u32?snd_wnd)
{
tp->pred_flags?=?htonl((tp->tcp_header_len?<
ntohl(TCP_FLAG_ACK)?|
snd_wnd);
}
static inline void __tcp_fast_path_on(struct tcp_sock *tp, u32 snd_wnd)
{
tp->pred_flags = htonl((tp->tcp_header_len << 26) | // 看見沒有。本質(zhì)就是設(shè)置pred_flags變量
ntohl(TCP_FLAG_ACK) |
snd_wnd);
}
其實就是就是說標(biāo)志位除了ACK和PSH外,如果其他的存在的話,就不能用快速路徑!
-----------------------------------------------------------------------------------------------------------------------------------------
下面看進入擁塞操作函數(shù)tcp_ca_event:
有必要先看看擁塞控制結(jié)構(gòu)體:
structtcp_congestion_ops?{
structlist_head????????list;
unsignedlongflags;
/*?initialize?private?data?(optional)?*/
void(*init)(structsock?*sk);//?初始化
/*?cleanup?private?data??(optional)?*/
void(*release)(structsock?*sk);//?清除數(shù)據(jù)
/*?return?slow?start?threshold?(required)?*/
u32?(*ssthresh)(structsock?*sk);//?返回慢開始門限
/*?lower?bound?for?congestion?window?(optional)?*/
u32?(*min_cwnd)(conststructsock?*sk);//?擁塞窗口最小值
/*?do?new?cwnd?calculation?(required)?*/
void(*cong_avoid)(structsock?*sk,?u32?ack,?u32?in_flight);//?計算新的擁塞窗口
/*?call?before?changing?ca_state?(optional)?*/
void(*set_state)(structsock?*sk,?u8?new_state);//?設(shè)置擁塞狀態(tài)
/*?call?when?cwnd?event?occurs?(optional)?*/
void(*cwnd_event)(structsock?*sk,enumtcp_ca_event?ev);//?擁塞事件發(fā)生時候處理
/*?new?value?of?cwnd?after?loss?(optional)?*/
u32??(*undo_cwnd)(structsock?*sk);//?丟包之后,擁塞窗口新的值
/*?hook?for?packet?ack?accounting?(optional)?*/
void(*pkts_acked)(structsock?*sk,?u32?num_acked,?s32?rtt_us);//?包的ack計數(shù)器
/*?get?info?for?inet_diag?(optional)?*/
void(*get_info)(structsock?*sk,?u32?ext,structsk_buff?*skb);//
charname[TCP_CA_NAME_MAX];
structmodule???*owner;
};
struct tcp_congestion_ops {
struct list_head list;
unsigned long flags;
/* initialize private data (optional) */
void (*init)(struct sock *sk); // 初始化
/* cleanup private data (optional) */
void (*release)(struct sock *sk); // 清除數(shù)據(jù)
/* return slow start threshold (required) */
u32 (*ssthresh)(struct sock *sk); // 返回慢開始門限
/* lower bound for congestion window (optional) */
u32 (*min_cwnd)(const struct sock *sk); // 擁塞窗口最小值
/* do new cwnd calculation (required) */
void (*cong_avoid)(struct sock *sk, u32 ack, u32 in_flight); // 計算新的擁塞窗口
/* call before changing ca_state (optional) */
void (*set_state)(struct sock *sk, u8 new_state); // 設(shè)置擁塞狀態(tài)
/* call when cwnd event occurs (optional) */
void (*cwnd_event)(struct sock *sk, enum tcp_ca_event ev); // 擁塞事件發(fā)生時候處理
/* new value of cwnd after loss (optional) */
u32 (*undo_cwnd)(struct sock *sk); // 丟包之后,擁塞窗口新的值
/* hook for packet ack accounting (optional) */
void (*pkts_acked)(struct sock *sk, u32 num_acked, s32 rtt_us); // 包的ack計數(shù)器
/* get info for inet_diag (optional) */
void (*get_info)(struct sock *sk, u32 ext, struct sk_buff *skb); //
char name[TCP_CA_NAME_MAX];
struct module *owner;
};
看一下TCP擁塞事件:
/*?Events?passed?to?congestion?control?interface?*/
enumtcp_ca_event?{
CA_EVENT_TX_START,/*?first?transmit?when?no?packets?in?flight?*/
CA_EVENT_CWND_RESTART,/*?congestion?window?restart?*/
CA_EVENT_COMPLETE_CWR,/*?end?of?congestion?recovery?*/
CA_EVENT_FRTO,/*?fast?recovery?timeout?*/
CA_EVENT_LOSS,/*?loss?timeout?*/
CA_EVENT_FAST_ACK,/*?in?sequence?ack?*/
CA_EVENT_SLOW_ACK,/*?other?ack?*/
};
/* Events passed to congestion control interface */
enum tcp_ca_event {
CA_EVENT_TX_START, /* first transmit when no packets in flight */
CA_EVENT_CWND_RESTART, /* congestion window restart */
CA_EVENT_COMPLETE_CWR, /* end of congestion recovery */
CA_EVENT_FRTO, /* fast recovery timeout */
CA_EVENT_LOSS, /* loss timeout */
CA_EVENT_FAST_ACK, /* in sequence ack */
CA_EVENT_SLOW_ACK, /* other ack */
};
staticinlinevoidtcp_ca_event(structsock?*sk,constenumtcp_ca_event?event)//?第三個參數(shù)在上面的來說分別是:CA_EVENT_SLOW_ACK和CA_EVENT_FAST_ACK
{
conststructinet_connection_sock?*icsk?=?inet_csk(sk);//?獲得連接sock
if(icsk->icsk_ca_ops->cwnd_event)//?注意這是一個函數(shù)指針:結(jié)構(gòu)體struct?tcp_congestion_ops中的!當(dāng)擁塞事件發(fā)生時候執(zhí)行
icsk->icsk_ca_ops->cwnd_event(sk,?event);//?執(zhí)行這個事件
}
static inline void tcp_ca_event(struct sock *sk, const enum tcp_ca_event event) // 第三個參數(shù)在上面的來說分別是:CA_EVENT_SLOW_ACK和CA_EVENT_FAST_ACK
{
const struct inet_connection_sock *icsk = inet_csk(sk); // 獲得連接sock
if (icsk->icsk_ca_ops->cwnd_event) // 注意這是一個函數(shù)指針:結(jié)構(gòu)體struct tcp_congestion_ops中的!當(dāng)擁塞事件發(fā)生時候執(zhí)行
icsk->icsk_ca_ops->cwnd_event(sk, event); // 執(zhí)行這個事件
}
擁塞窗口事件初始化在這:
if(icsk->icsk_ca_ops?==?&tcp_init_congestion_ops)?{?初始化結(jié)構(gòu)體
rcu_read_lock();
list_for_each_entry_rcu(ca,?&tcp_cong_list,?list)?{
if(try_module_get(ca->owner))?{
icsk->icsk_ca_ops?=?ca;
break;
}
/*?fallback?to?next?available?*/
}
rcu_read_unlock();
}
if (icsk->icsk_ca_ops == &tcp_init_congestion_ops) { 初始化結(jié)構(gòu)體
rcu_read_lock();
list_for_each_entry_rcu(ca, &tcp_cong_list, list) {
if (try_module_get(ca->owner)) {
icsk->icsk_ca_ops = ca;
break;
}
/* fallback to next available */
}
rcu_read_unlock();
}
structtcp_congestion_ops?tcp_init_congestion_ops??=?{
.name???????????="",
.owner??????????=?THIS_MODULE,
.ssthresh???????=?tcp_reno_ssthresh,
.cong_avoid?????=?tcp_reno_cong_avoid,
.min_cwnd???????=?tcp_reno_min_cwnd,
};
struct tcp_congestion_ops tcp_init_congestion_ops = {
.name = "",
.owner = THIS_MODULE,
.ssthresh = tcp_reno_ssthresh,
.cong_avoid = tcp_reno_cong_avoid,
.min_cwnd = tcp_reno_min_cwnd,
};
這有點問題!對于這個函數(shù)沒有進行實現(xiàn)?還是我沒有找到處理賦值的地方?無語了~~~~~~~~~~~~~~~~~~~~~~~~
或許這里面初始化:下面是關(guān)于cwnd_event所有的初始化
staticstructtcp_congestion_ops?tcp_veno?=?{
.flags??????????=?TCP_CONG_RTT_STAMP,
.init???????????=?tcp_veno_init,
.ssthresh???????=?tcp_veno_ssthresh,
.cong_avoid?????=?tcp_veno_cong_avoid,
.pkts_acked?????=?tcp_veno_pkts_acked,
.set_state??????=?tcp_veno_state,
.cwnd_event?????=?tcp_veno_cwnd_event,///
.owner??????????=?THIS_MODULE,
.name???????????="veno",
};
static struct tcp_congestion_ops tcp_veno = {
.flags = TCP_CONG_RTT_STAMP,
.init = tcp_veno_init,
.ssthresh = tcp_veno_ssthresh,
.cong_avoid = tcp_veno_cong_avoid,
.pkts_acked = tcp_veno_pkts_acked,
.set_state = tcp_veno_state,
.cwnd_event = tcp_veno_cwnd_event, ///
.owner = THIS_MODULE,
.name = "veno",
};
staticstructtcp_congestion_ops?tcp_vegas?=?{
.flags??????????=?TCP_CONG_RTT_STAMP,
.init???????????=?tcp_vegas_init,
.ssthresh???????=?tcp_reno_ssthresh,
.cong_avoid?????=?tcp_vegas_cong_avoid,
.min_cwnd???????=?tcp_reno_min_cwnd,
.pkts_acked?????=?tcp_vegas_pkts_acked,
.set_state??????=?tcp_vegas_state,
.cwnd_event?????=?tcp_vegas_cwnd_event,///
.get_info???????=?tcp_vegas_get_info,
.owner??????????=?THIS_MODULE,
.name???????????="vegas",
};
static struct tcp_congestion_ops tcp_vegas = {
.flags = TCP_CONG_RTT_STAMP,
.init = tcp_vegas_init,
.ssthresh = tcp_reno_ssthresh,
.cong_avoid = tcp_vegas_cong_avoid,
.min_cwnd = tcp_reno_min_cwnd,
.pkts_acked = tcp_vegas_pkts_acked,
.set_state = tcp_vegas_state,
.cwnd_event = tcp_vegas_cwnd_event, ///
.get_info = tcp_vegas_get_info,
.owner = THIS_MODULE,
.name = "vegas",
};
staticstructtcp_congestion_ops?tcp_yeah?=?{
.flags??????????=?TCP_CONG_RTT_STAMP,
.init???????????=?tcp_yeah_init,
.ssthresh???????=?tcp_yeah_ssthresh,
.cong_avoid?????=?tcp_yeah_cong_avoid,
.min_cwnd???????=?tcp_reno_min_cwnd,
.set_state??????=?tcp_vegas_state,
.cwnd_event?????=?tcp_vegas_cwnd_event,
.get_info???????=?tcp_vegas_get_info,
.pkts_acked?????=?tcp_yeah_pkts_acked,
.owner??????????=?THIS_MODULE,
.name???????????="yeah",
};
static struct tcp_congestion_ops tcp_yeah = {
.flags = TCP_CONG_RTT_STAMP,
.init = tcp_yeah_init,
.ssthresh = tcp_yeah_ssthresh,
.cong_avoid = tcp_yeah_cong_avoid,
.min_cwnd = tcp_reno_min_cwnd,
.set_state = tcp_vegas_state,
.cwnd_event = tcp_vegas_cwnd_event,
.get_info = tcp_vegas_get_info,
.pkts_acked = tcp_yeah_pkts_acked,
.owner = THIS_MODULE,
.name = "yeah",
};
-------------------------------------------------------------------------------------------------------------------------------------------
下面清理重傳隊列中已經(jīng)確認(rèn)的數(shù)據(jù),看函數(shù)tcp_clean_rtx_queue:
---------------------------------------------------------------------------------------------------------------------------------
現(xiàn)在可以看一下tcp_ack_is_dubious函數(shù),來判斷是不是進入了擁塞狀態(tài):
先可以看一下狀態(tài)的定義:
#define?FLAG_DATA???????????????0x01?/*?Incoming?frame?contained?data.??????????*/?????//?來了一個包含數(shù)據(jù)的包
#define?FLAG_WIN_UPDATE?????????0x02?/*?Incoming?ACK?was?a?window?update.???????*/?????//?來了一個ACK用于更新窗口
#define?FLAG_DATA_ACKED?????????0x04?/*?This?ACK?acknowledged?new?data.?????????*/?????//?對于數(shù)據(jù)的確認(rèn)
#define?FLAG_RETRANS_DATA_ACKED?0x08?/*?""?""?some?of?which?was?retransmitted.??*/?????//?對于重傳數(shù)據(jù)的確認(rèn)
#define?FLAG_SYN_ACKED??????????0x10?/*?This?ACK?acknowledged?SYN.??????????????*/?????//?對于SYN的確認(rèn)
#define?FLAG_DATA_SACKED????????0x20?/*?New?SACK.???????????????????????????????*/?????//?這是對數(shù)據(jù)的一個選擇確認(rèn)
#define?FLAG_ECE????????????????0x40?/*?ECE?in?this?ACK?????????????????????????*/?????//?確認(rèn)中旅帶有ECE信息
#define?FLAG_DATA_LOST??????????0x80?/*?SACK?detected?data?lossage.?????????????*/?????//?SACK檢測到數(shù)據(jù)丟失
#define?FLAG_SLOWPATH???????????0x100?/*?Do?not?skip?RFC?checks?for?window?update.*/???//?slowpath,需要做一些檢查
#define?FLAG_ONLY_ORIG_SACKED???0x200?/*?SACKs?only?non-rexmit?sent?before?RTO?*/
#define?FLAG_SND_UNA_ADVANCED???0x400?/*?Snd_una?was?changed?(!=?FLAG_DATA_ACKED)?*/???//?snd-una改變
#define?FLAG_DSACKING_ACK???????0x800?/*?SACK?blocks?contained?D-SACK?info?*/??????????//?包含DSACK信息
#define?FLAG_NONHEAD_RETRANS_ACKED??????0x1000?/*?Non-head?rexmitted?data?was?ACKed?*/
#define?FLAG_SACK_RENEGING??????0x2000?/*?snd_una?advanced?to?a?sacked?seq?*/??????????//?snd_una移動到一個sack中的一個位置
#define?FLAG_ACKED??????????????(FLAG_DATA_ACKED|FLAG_SYN_ACKED)?????????//?表示數(shù)據(jù)確認(rèn)或者SYN確認(rèn)
#define?FLAG_NOT_DUP????????????(FLAG_DATA|FLAG_WIN_UPDATE|FLAG_ACKED)???//?表示ACK是不重復(fù)的
#define?FLAG_CA_ALERT???????????(FLAG_DATA_SACKED|FLAG_ECE)??????????????//?表示是否在進入擁塞狀態(tài)的時候被alert(原因可能是SACK丟包或者路由器提示擁塞)
#define?FLAG_FORWARD_PROGRESS???(FLAG_ACKED|FLAG_DATA_SACKED)????????????//?選擇確認(rèn)
#define FLAG_DATA 0x01 /* Incoming frame contained data. */ // 來了一個包含數(shù)據(jù)的包
#define FLAG_WIN_UPDATE 0x02 /* Incoming ACK was a window update. */ // 來了一個ACK用于更新窗口
#define FLAG_DATA_ACKED 0x04 /* This ACK acknowledged new data. */ // 對于數(shù)據(jù)的確認(rèn)
#define FLAG_RETRANS_DATA_ACKED 0x08 /* "" "" some of which was retransmitted. */ // 對于重傳數(shù)據(jù)的確認(rèn)
#define FLAG_SYN_ACKED 0x10 /* This ACK acknowledged SYN. */ // 對于SYN的確認(rèn)
#define FLAG_DATA_SACKED 0x20 /* New SACK. */ // 這是對數(shù)據(jù)的一個選擇確認(rèn)
#define FLAG_ECE 0x40 /* ECE in this ACK */ // 確認(rèn)中旅帶有ECE信息
#define FLAG_DATA_LOST 0x80 /* SACK detected data lossage. */ // SACK檢測到數(shù)據(jù)丟失
#define FLAG_SLOWPATH 0x100 /* Do not skip RFC checks for window update.*/ // slowpath,需要做一些檢查
#define FLAG_ONLY_ORIG_SACKED 0x200 /* SACKs only non-rexmit sent before RTO */
#define FLAG_SND_UNA_ADVANCED 0x400 /* Snd_una was changed (!= FLAG_DATA_ACKED) */ // snd-una改變
#define FLAG_DSACKING_ACK 0x800 /* SACK blocks contained D-SACK info */ // 包含DSACK信息
#define FLAG_NONHEAD_RETRANS_ACKED 0x1000 /* Non-head rexmitted data was ACKed */
#define FLAG_SACK_RENEGING 0x2000 /* snd_una advanced to a sacked seq */ // snd_una移動到一個sack中的一個位置
#define FLAG_ACKED (FLAG_DATA_ACKED|FLAG_SYN_ACKED) // 表示數(shù)據(jù)確認(rèn)或者SYN確認(rèn)
#define FLAG_NOT_DUP (FLAG_DATA|FLAG_WIN_UPDATE|FLAG_ACKED) // 表示ACK是不重復(fù)的
#define FLAG_CA_ALERT (FLAG_DATA_SACKED|FLAG_ECE) // 表示是否在進入擁塞狀態(tài)的時候被alert(原因可能是SACK丟包或者路由器提示擁塞)
#define FLAG_FORWARD_PROGRESS (FLAG_ACKED|FLAG_DATA_SACKED) // 選擇確認(rèn)
再看一下:
TCP_CA_Open:TCP連接的初始化的狀態(tài)。TCP連接會在慢啟動和擁塞避免階段(調(diào)用tcp_cong_avoid)增加擁塞窗口。每個接收到的ACK都要調(diào)用tcp_ack_is_dubious,檢查它是否可疑。如果是ACK可疑,就調(diào)用 tcp_fastretrans_alert()就切換到其他CA擁塞狀態(tài)。但是對于可疑的ACK,若窗口也允許增大(tcp_may_raise_cwnd),那么(tcp_fastretrans_alert)仍然可能增大擁塞窗口。
TCP_CA_Disorder:注意如果收到重復(fù)的ACK或者SACK,那么可能出現(xiàn)亂序情況,進入這個狀態(tài)處理。
TCP_CA_CWR:表示發(fā)生某些道路擁塞,需要減慢發(fā)送速度。
TCP_CA_Recovery:正在進行快速重傳丟失的數(shù)據(jù)包。
TCP_CA_Loss:超時重傳情況下,如果接收到的ACK與SACK信息不一樣,則阻塞丟包狀態(tài)。
staticinlineinttcp_ack_is_dubious(conststructsock?*sk,constintflag)
{
return(!(flag?&?FLAG_NOT_DUP)?||?(flag?&?FLAG_CA_ALERT)?||//?是重復(fù)的ACK???或者???在進入擁塞狀態(tài)的時候出現(xiàn)警告
inet_csk(sk)->icsk_ca_state?!=?TCP_CA_Open);//?或者擁塞狀態(tài)不是“增大擁塞窗口”狀態(tài)
}//?則這個ACK是可疑的,其實意思就是,不是一個正常的ACK,不能隨便增大擁塞窗口
static inline int tcp_ack_is_dubious(const struct sock *sk, const int flag)
{
return (!(flag & FLAG_NOT_DUP) || (flag & FLAG_CA_ALERT) || // 是重復(fù)的ACK 或者 在進入擁塞狀態(tài)的時候出現(xiàn)警告
inet_csk(sk)->icsk_ca_state != TCP_CA_Open); // 或者擁塞狀態(tài)不是“增大擁塞窗口”狀態(tài)
} // 則這個ACK是可疑的,其實意思就是,不是一個正常的ACK,不能隨便增大擁塞窗口
下面就兩條路:
1:如果被懷疑
2:如果沒有被懷疑
先看如果被懷疑了,那么:
先看函數(shù):tcp_may_raise_cwnd
staticinlineinttcp_may_raise_cwnd(conststructsock?*sk,constintflag)
{
conststructtcp_sock?*tp?=?tcp_sk(sk);
return(!(flag?&?FLAG_ECE)?||?tp->snd_cwnd?snd_ssthresh)?&&//?沒有其他阻塞??或者??(發(fā)送窗口小于門限&&不是Recovery?,也不是CWR)
!((1?<icsk_ca_state)?&?(TCPF_CA_Recovery?|?TCPF_CA_CWR));//?那么這樣還是可以增大窗口的嘛~~~~~?^_^
}
static inline int tcp_may_raise_cwnd(const struct sock *sk, const int flag)
{
const struct tcp_sock *tp = tcp_sk(sk);
return (!(flag & FLAG_ECE) || tp->snd_cwnd < tp->snd_ssthresh) && // 沒有其他阻塞 或者 (發(fā)送窗口小于門限&&不是Recovery ,也不是CWR)
!((1 << inet_csk(sk)->icsk_ca_state) & (TCPF_CA_Recovery | TCPF_CA_CWR)); // 那么這樣還是可以增大窗口的嘛~~~~~ ^_^
}
如果可以增大窗口,那么就需要使用tcp_cong_avoid執(zhí)行這個函數(shù)用來實現(xiàn)慢啟動和快速重傳擁塞避免算法:
這個函數(shù)也是在“沒有被懷疑”的情況下執(zhí)行的函數(shù),所以
如果沒有被懷疑,執(zhí)行的也是tcp_cong_avoid,一起解釋:
staticvoidtcp_cong_avoid(structsock?*sk,?u32?ack,?u32?in_flight)
{
conststructinet_connection_sock?*icsk?=?inet_csk(sk);
icsk->icsk_ca_ops->cong_avoid(sk,?ack,?in_flight);//?這才是重要處理函數(shù)
tcp_sk(sk)->snd_cwnd_stamp?=?tcp_time_stamp;//?發(fā)送窗口改變時間戳
}
static void tcp_cong_avoid(struct sock *sk, u32 ack, u32 in_flight)
{
const struct inet_connection_sock *icsk = inet_csk(sk);
icsk->icsk_ca_ops->cong_avoid(sk, ack, in_flight); // 這才是重要處理函數(shù)
tcp_sk(sk)->snd_cwnd_stamp = tcp_time_stamp; // 發(fā)送窗口改變時間戳
}
我們看到上面說的擁塞結(jié)構(gòu)體的初始化:
structtcp_congestion_ops?tcp_init_congestion_ops??=?{
.name???????????="",
.owner??????????=?THIS_MODULE,
.ssthresh???????=?tcp_reno_ssthresh,
.cong_avoid?????=?tcp_reno_cong_avoid,//這個函數(shù)
.min_cwnd???????=?tcp_reno_min_cwnd,
};
struct tcp_congestion_ops tcp_init_congestion_ops = {
.name = "",
.owner = THIS_MODULE,
.ssthresh = tcp_reno_ssthresh,
.cong_avoid = tcp_reno_cong_avoid, //這個函數(shù)
.min_cwnd = tcp_reno_min_cwnd,
};
那么實際執(zhí)行的就是tcp_reno_cong_avoid函數(shù)!!!
----------------------------------------------------------------------------------------------------------------------------------
OK,下面再看看tcp_fastretrans_alert函數(shù):TCP擁塞狀態(tài)機主要是在tcp_fastretrans_alert()中實現(xiàn)的,只有在ACK被懷疑的時候才會執(zhí)行這個提醒函數(shù)
此函數(shù)被調(diào)用的條件也就是懷疑的條件:
1:進來一個ACK,但是狀態(tài)不是 Open
2:收到的是? ?SACK 、Duplicate ACK、ECN、ECE 等警告信息
到此為止,處理接收到的ACK基本結(jié)束。。。。
總結(jié)
以上是生活随笔為你收集整理的linux重置网络协议,Linux 内核网络协议栈 ------ tcp_ack 函数处理接收到的ACK包之后 ....的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: linux字符驱动头文件路径,Linux
- 下一篇: Linux程序内存跟踪,分享一款Linu