青青草原综合久久大伊人导航_色综合久久天天综合_日日噜噜夜夜狠狠久久丁香五月_热久久这里只有精品

牽著老婆滿街逛

嚴以律己,寬以待人. 三思而后行.
GMail/GTalk: yanglinbo#google.com;
MSN/Email: tx7do#yahoo.com.cn;
QQ: 3 0 3 3 9 6 9 2 0 .

UDP_CORK,TCP_CORK以及TCP_NODELAY

轉載自:http://soft-app.iteye.com/blog/919784

這三個選項十分有意思,并且困擾了很多人。特別是cork選項,它到底和nodelay有什么區別,到底怎樣影響了Nagle算法。在tcp的實現中(特指linux內核的協議棧實現),cork和nodelay非常讓人看不出區別,這一塊的實現非常復雜,看內核實現之前最好先明白它們大概在說什么,否則很容易迷失的。
所謂的cork就是塞子的意思,形象地理解就是用cork將連接塞住,使得數據先不發出去,等到拔去塞子后再發出去,而nodelay事實上是為了禁用Nagle算法,Nagle算法為了增加了網絡的吞吐量而犧牲了響應時間體驗,這在有些應用中是不合適的,比如交互式應用(終端登錄或者遠程X應用 etc.),因此有必要提供一個選項將它禁用掉,Nagle算法在RFC1122中有提及,它的實現實際上很簡單,利用了tcp本身的一些特性,在算法描述中,關鍵的一點是“什么時候真實的發送數據”,這個問題的解答也是很簡單,原則上只要發出的包都被對端ack了就可以發送了,這實際上也是一種權衡,Nagle算法最初的目的在于解決大量小包存在于網絡從而造成網絡擁塞的問題(一個小包可能只有幾個字節,比如ls,cat等等,然而為每個小包封裝幾個協議頭,其大小就不可忽視了,大量此類小包存在于網絡勢必會使得網絡帶寬的利用率大大下降),如果包被ack了,說明包已經離開了網絡進入了對端主機,這樣就可以發送數據了,此時無需再等,有多少數據發送多少(當然要考慮窗口大小和MTU),如果很極端地等待更多的數據,那么響應度會更低,換句話簡單的說Nagle算法只允許一個未被ack的包存在于網絡,它并不管包的大小,因此它事實上就是一個擴展的停-等協議,只不過它是基于包停-等的,而不是基于字節停-等的。
可以看出,Nagle算法完全由tcp協議的ack機制決定,這會帶來一些問題,比如如果對端ack回復很快的話,Nagle事實上不會拼接太多的數據包,雖然避免了網絡擁塞,網絡總體的利用率依然很低,Nagle真的做到了“只做好一件事”的原則,然而有沒有另外一種算法,可以提高整體網絡利用率呢?也就是說盡量以不能再多的數據發送,這里之所以說是盡量還是權衡導致的,某時可以發送數據的時候將會發送數據,即使當前數據再小也不再等待后續的可能拼接成更大包的數據的到來。
實際上,這樣的需求可以用TCP_CORK來實現,但是實現得可能并不像你想象的那么完美,cork并不會將連接完全塞住。內核其實并不知道應用層到底什么時候會發送第二批數據用于和第一批數據拼接以達到MTU的大小,因此內核會給出一個時間限制,在該時間內沒有拼接成一個大包(努力接近MTU)的話,內核就會無條件發送,這里給出的只是一個大致的思想,真實的情況還要受到窗口大小以及擁塞情況的影響,因此tcp“何時發送數據”這個問題非常復雜。
Nagle算法和CORK算法非常類似,但是它們的著眼點不一樣,Nagle算法主要避免網絡因為太多的小包(協議頭的比例非常之大)而擁塞,而CORK算法則是為了提高網絡的利用率,使得總體上協議頭占用的比例盡可能的小。如此看來這二者在避免發送小包上是一致的,在用戶控制的層面上,Nagle算法完全不受用戶socket的控制,你只能簡單的設置TCP_NODELAY而禁用它,CORK算法同樣也是通過設置或者清除TCP_cork使能或者禁用之,然而Nagle算法關心的是網絡擁塞問題,只要所有的ack回來則發包,而CORK算法卻可以關心內容,在前后數據包發送間隔很短的前提下(很重要,否則內核會幫你將分散的包發出),即使你是分散發送多個小數據包,你也可以通過使能CORK算法將這些內容拼接在一個包內,如果此時用Nagle算法的話,則可能做不到這一點。
接下來看一下內核代碼,然后給出一個測試程序來感性感受這些選項。tcp的發送函數是tcp_sendmsg,這個函數中存在一個大循環,用于將用戶數據置入skb中,它的形式如下:
int tcp_sendmsg(struct kiocb *iocb, struct sock *sk, struct msghdr *msg,
size_t size)
{

while (--iovlen >= 0) {
0.更新數據結構元數據;
while (seglen > 0) {
int copy;
skb = sk->sk_write_queue.prev;
1.如果既有skb的長度過大或者根本還沒有一個skb則分配一個skb;
2.將數據拷貝到既有的skb或者新的skb中;
3.更新skb和用戶數據的元數據;
//如果數據還沒有達到mss,則繼續,換句話就是如果數據已經達到mss了就接著往下走來權衡是否馬上發送。
if (skb->len != mss_now || (flags & MSG_OOB))
continue;
4.權衡發送與否
continue;
}
}
out:
//如果循環完成,所有數據都進入了skb,調用tcp_push來權衡是否發送
tcp_push(sk, tp, flags, mss_now, tp->nonagle);
}
tcp_push很短但是很復雜,
static inline void tcp_push(struct sock *sk, struct tcp_opt *tp, int flags,
int mss_now, int nonagle)
{
if (sk->sk_send_head) {
struct sk_buff *skb = sk->sk_write_queue.prev;
...
//如果有MSG_MORE,則當作cork來處理
__tcp_push_pending_frames(sk, tp, mss_now,
(flags & MSG_MORE) ? TCP_NAGLE_CORK : nonagle);
}
}
static __inline__ void __tcp_push_pending_frames(struct sock *sk,
struct tcp_opt *tp,
unsigned cur_mss,
int nonagle)
{
struct sk_buff *skb = sk->sk_send_head;
if (skb) {
if (!tcp_skb_is_last(sk, skb)) //如果已經有了很多的skb,則盡量馬上發送
nonagle = TCP_NAGLE_PUSH;
//只有tcp_snd_test返回1才會發送數據,該函數很復雜
if (!tcp_snd_test(tp, skb, cur_mss, nonagle) || 
tcp_write_xmit(sk, nonagle))
tcp_check_probe_timer(sk, tp); 
}
tcp_cwnd_validate(sk, tp);
}
static __inline__ int tcp_snd_test(struct tcp_opt *tp, struct sk_buff *skb,
unsigned cur_mss, int nonagle)
{
//如果有TCP_NAGLE_PUSH標志(或者tcp_nagle_check同意發送)且未ack的數據夠少且...則可以發送
return (((nonagle&TCP_NAGLE_PUSH) || tp->urg_mode
|| !tcp_nagle_check(tp, skb, cur_mss, nonagle)) &&
((tcp_packets_in_flight(tp) < tp->snd_cwnd) ||
(TCP_SKB_CB(skb)->flags & TCPCB_FLAG_FIN)) &&
!after(TCP_SKB_CB(skb)->end_seq, tp->snd_una + tp->snd_wnd));
}
tcp_nagle_check函數是一個很重要的函數,它基本決定了數據是否可以發送的80%,內核源碼中對該函數有一條注釋:
-3. Or TCP_NODELAY was set.
-4. Or TCP_CORK is not set, and all sent packets are ACKed.
就是說如果TCP_NODELAY值為1就可以直接發送,或者cork被禁用的情況下所有發出的包都被ack了也可以發送數據,這里體現的就是Nagle算法和CORK算法的區別了,Nagle算法只要求所有的出發包都ack就可以發送,而不管當前包是否足夠大(雖然它通過tcp_minshall_check保證了包不太小),而如果啟用cork的話,可能僅僅數據被ack就不夠了,這就是為何在代碼注釋中說cork要比Nagle更stronger的原因,同時這段代碼也說明了為何TCP_CORK和TCP_NODELAY不能一起使用的原因,它們有共同的東西,卻在做著不同的事情。看看tcp_nagle_check:
static __inline__ int
tcp_nagle_check(struct tcp_opt *tp, struct sk_buff *skb, unsigned mss_now, int nonagle)
{
return (skb->len < mss_now &&
!(TCP_SKB_CB(skb)->flags & TCPCB_FLAG_FIN) &&
((nonagle&TCP_NAGLE_CORK) ||
(!nonagle &&
tp->packets_out &&
tcp_minshall_check(tp))));
}
看看__tcp_push_pending_frames的最后,有一個tcp_check_probe_timer調用,就是說在沒有數據被發送的時候會調用這個函數。這個函數有兩個作用,第一個是防止0窗口導致的死鎖,另一個作用就是定時發送由于使能了CORK算法或者Nagle算法一直等待新數據拼接而沒有機會發送的數據包。這個timer內置在重傳timer之中,其時間間隔和rtt有關,一旦觸發則會發送數據包或者窗口探測包。反過來可以理解,如果沒有這個timer的話,啟用cork的連接將幾乎(可能根據實現的不同還會受別的因素影響,太復雜了)每次都發送mtu大小的數據包。該timer調用tcp_probe_timer函數:
static void tcp_probe_timer(struct sock *sk)
{
struct tcp_opt *tp = tcp_sk(sk);
int max_probes;
//1.如果有數據在網絡上,則期望馬上回來ack,ack中會通告對端窗口
//2.如果沒有數據要發送,則無需關注對端窗口,即使為0也無所謂
if (tp->packets_out || !sk->sk_send_head) {
tp->probes_out = 0;
return;
}
//這個sysctl_tcp_retries2是可以調整的
max_probes = sysctl_tcp_retries2;
if (tp->probes_out > max_probes) {
tcp_write_err(sk);
} else {
tcp_send_probe0(sk);
}
}
tcp_send_probe0會調用tcp_write_wakeup函數,該函數會要么發送可以發送的數據,如果由于發送隊列越過了發送窗口導致不能發送,則發送一個窗口探測包:
int tcp_write_wakeup(struct sock *sk)
{
if (sk->sk_state != TCP_CLOSE) {
struct tcp_opt *tp = tcp_sk(sk);
struct sk_buff *skb;
if ((skb = sk->sk_send_head) != NULL &&
before(TCP_SKB_CB(skb)->seq, tp->snd_una+tp->snd_wnd)) {
...//在sk_send_head隊列上取出一個發送出去,其ack會帶回對端通告窗口的大小
err = tcp_transmit_skb(sk, skb_clone(skb, GFP_ATOMIC));
...
return err;
} else {
...
return tcp_xmit_probe_skb(sk, 0);
}
}
return -1;
}
這個probe timer雖然一定程度阻礙了cork的滿載發送,然而它卻是必要的,這是由于tcp并不為純的ack包(不帶數據的ack包)提供確認,因此一旦這種ack包丟失,那么就有可能死鎖,發送端的窗口無法更新,接收端由于已經發送了ack而等待接收數據,兩端就這樣僵持起來,因此需要一個timer,定期發送一個探測包,一個ack丟失,不能所有的ack都丟失吧,在timer到期時,如果本來發送隊列上有數據要發送,則直接發送這些數據而不再發送探測包,因為發送了這些數據,所以它“破壞”了cork的承諾,不過也因此增強了響應度。
在示出應用程序之前,總結一下內核在哪里會發送tcp包,在解釋在哪里會發送tcp包之前,首先說明內核協議棧為了高效和低耦合設計,tcp_sendmsg并不一定真實發送數據,真實發送數據的地點在:
1.tcp_sendmsg內部(廢話!),如果權衡的結果需要發送則發送;
2.收到對端ack的時候會調用tcp_data_snd_check來發送,它同樣完全按照cork策略來的;
3.probe timer到期后作為窗口探測包發送一些數據,它“破壞”了cork,在塞子上捅破一個口子;
4.連接斷開或者進程退出時可能會將所有數據刷到對端;
5.當禁用cork或者啟用nodelay的時候會將pending的數據刷入對端。
下面看一下應用層的測試程序:
客戶端程序:client
#define BUFF_SIZE 500 
#define REMOTE_PORT 6800 
signed int len = 0; 
int main(int argc, char *argv[])
{
int sock;
struct sockaddr_in remote_addr;
int on = 1;
unsigned char buff[BUFF_SIZE];
int i;
if (argc != 5) {
printf("usage: client server_ip on|off cork|nodelay usec\n");
exit(-1);
}
int msd = atoi(argv[4]);
if (!strcmp(argv[2], "on"))
on = 1;
else if (!strcmp(argv[2], "off"))
on = 0;
for (i = 0; i < BUFF_SIZE; i++) {
buff[i] = 'q'; 
}
sock = socket(AF_INET, SOCK_STREAM, 0);
if (!strcmp(argv[3], "nodelay")) {
setsockopt(sock, SOL_TCP, TCP_NODELAY, &dontroute, sizeof(dontroute));
} else if (!strcmp(argv[3], "cork")) {
setsockopt(sock, SOL_TCP, TCP_CORK, &dontroute, sizeof(dontroute));
}
struct sockaddr_in sa;
memset (&sa, '\0', sizeof(sa));
sa.sin_family = AF_INET;
sa.sin_addr.s_addr = inet_addr (argv[1]); 
sa.sin_port = htons(REMOTE_PORT); 
connect(sock, (struct sockaddr*) &sa, sizeof(sa));
while(1) {
len = send(sock, buff, BUFF_SIZE, MSG_MORE);
if (len < 0) 
exit(-1);
usleep(msd);
}
return (0);
}
服務器程序:server
int main (int argc, char **argv)
{
int err;
int listen_sd;
int sd;
struct sockaddr_in sa_serv;
struct sockaddr_in sa_cli;
size_t client_len;
char* str;
char buf [500];
listen_sd = socket (AF_INET, SOCK_STREAM, 0); 
memset (&sa_serv, '\0', sizeof(sa_serv));
sa_serv.sin_family = AF_INET;
sa_serv.sin_addr.s_addr = INADDR_ANY;
sa_serv.sin_port = htons (6800); 
err = bind(listen_sd, (struct sockaddr*) &sa_serv, sizeof (sa_serv)); 
err = listen (listen_sd, 5); 
client_len = sizeof(sa_cli);
while (1) {
sd = accept (listen_sd, (struct sockaddr*) &sa_cli, &client_len);
while (1) {
err = read(sd, buf, sizeof(buf)); 
if (err <= 0)
break;
}
}
close (sd);
}
運行之:
client 192.168.x.y on cork 66000
在我的機器上,第四個參數最大到66000時cork會滿載發送,如果usleep的時間再長一些,probe timer就是“幫忙”發送數據了,給你的感覺是,啟用了cork為何看起來沒有什么用。這個時間在不同環境在有所不同,因為probe timer導致了cork的破壞,而probe timer和rtt有關,rtt又和網絡環境有關...再進行一個測試,執行下列命令:sysctl -w net.ipv4.tcp_retries2=-1
然后以比較高的時間間隔以及比較小的BUFF_SIZE在開啟cork情況下運行client程序,我們發現第一個包還沒發完進程就會退出,這是由于cork盡力在組包,間隔過大導致probe timer過期,然后tp->probes_out > max_probes判斷通過,導致超時退出,這個可以從/proc/net/netstat中的超時計數器中看出來,如果間隔比較短,每次新的數據pending到既有的skb上而不發送,重置probe timer,使得timer總是不過期,終于pending的數據到達了mtu的大小,cork的滿載發送起作用進而發送之。
還有一個概念是“糊涂窗口”,那就是接收端接收緩慢并不斷確認,導致窗口一直很小,而發送端收到ack就再次發送小包,這樣導致一直發送-確認很小的包...這個是可以通過應用層編程來避免的,另外也可以通過cork算法或者Nagle算法來減輕,但是無論怎樣都逃不過一些timer自動幫你發送數據。
最后,好像遺漏了UDP_CORK,很簡單,udp沒有連接,沒有確認,因此也就不需要什么timer之類的復雜機制,也因此,它是真正承諾的cork,除非你在應用層手工拔掉塞子,否則數據將不會發出。

posted on 2012-09-25 02:59 楊粼波 閱讀(1822) 評論(0)  編輯 收藏 引用 所屬分類: 網絡編程C++

青青草原综合久久大伊人导航_色综合久久天天综合_日日噜噜夜夜狠狠久久丁香五月_热久久这里只有精品
  • <ins id="pjuwb"></ins>
    <blockquote id="pjuwb"><pre id="pjuwb"></pre></blockquote>
    <noscript id="pjuwb"></noscript>
          <sup id="pjuwb"><pre id="pjuwb"></pre></sup>
            <dd id="pjuwb"></dd>
            <abbr id="pjuwb"></abbr>
            久久久美女艺术照精彩视频福利播放 | 欧美77777| 午夜免费久久久久| 午夜久久久久久| 久久精品免费电影| 免费成人在线观看视频| 欧美成人激情视频| 亚洲精品久久久一区二区三区| 亚洲国产精品久久久久秋霞蜜臀 | 免费在线亚洲欧美| 亚洲国产另类精品专区| 中文亚洲欧美| 美女啪啪无遮挡免费久久网站| 欧美极品在线视频| 国产精品色网| 亚洲国产综合在线看不卡| 一区二区三区产品免费精品久久75 | 亚洲中字黄色| 麻豆成人在线播放| 夜夜精品视频| 久久久www| 欧美天堂在线观看| 国产最新精品精品你懂的| 亚洲精品久久久久久下一站| 午夜精品偷拍| 亚洲国产精品久久久久婷婷老年 | 91久久国产综合久久蜜月精品| 一区二区三欧美| 久久综合亚洲社区| 亚洲午夜一区二区| 欧美成人在线免费视频| 欧美调教视频| 欧美大片在线看| 久久精品国产69国产精品亚洲| 欧美成人精精品一区二区频| 亚洲午夜电影网| 欧美精品v日韩精品v韩国精品v| 国产午夜亚洲精品理论片色戒| 亚洲精品国精品久久99热| 久久精品水蜜桃av综合天堂| 99精品热视频只有精品10| 欧美成人国产一区二区| 狠狠综合久久av一区二区老牛| 亚洲一区二区在线观看视频| 亚洲国产精品女人久久久| 久久精品中文| 国产亚洲一区二区在线观看| 亚洲免费伊人电影在线观看av| 亚洲国产成人久久综合| 久久人91精品久久久久久不卡| 国产美女诱惑一区二区| 亚洲在线观看免费视频| 99re66热这里只有精品3直播| 欧美成人免费小视频| 亚洲第一黄色| 欧美激情aⅴ一区二区三区| 久久久人成影片一区二区三区| 国产一区二区三区在线观看免费 | 国产精品久久久久久久久久ktv| 亚洲另类在线视频| 亚洲黄色免费| 欧美日韩不卡一区| 亚洲天堂免费观看| 在线亚洲精品| 国产乱码精品| 久久久久久久网站| 久久精品国产999大香线蕉| 狠狠色综合一区二区| 欧美激情黄色片| 欧美精品七区| 亚洲欧美日韩在线| 欧美一区二区三区在线观看视频| 国产亚洲视频在线观看| 美女黄网久久| 欧美福利一区二区三区| 一区二区三区视频免费在线观看| 日韩视频三区| 国产伦理精品不卡| 欧美r片在线| 久久国产主播| 欧美人交a欧美精品| 伊人久久亚洲热| 久久精品成人一区二区三区| 欧美在线视频免费播放| 国产精品日韩在线观看| 国内精品久久久久久久97牛牛| 91久久国产综合久久蜜月精品| 亚洲精选91| 国产精品久久久久久妇女6080| 亚洲破处大片| 亚洲欧美久久久| 一区二区三区亚洲| 欧美精品在线视频观看| 一本一本久久a久久精品综合妖精 一本一本久久a久久精品综合麻豆 | 亚洲精品乱码久久久久久蜜桃麻豆| 欧美mv日韩mv国产网站app| 欧美xx视频| 亚洲综合不卡| 久久一区激情| 午夜欧美精品久久久久久久| 久热re这里精品视频在线6| 亚洲一区亚洲二区| 男人插女人欧美| 欧美在线一二三| 欧美区一区二区三区| 久久久夜夜夜| 国产精品人人做人人爽| 亚洲国产成人精品久久| 国产中文一区| 亚洲一区二区三区在线| 日韩午夜免费| 久久一二三国产| 欧美中文字幕在线视频| 欧美日韩亚洲激情| 亚洲国产天堂久久综合网| 国产一区二区在线免费观看 | 亚洲视频欧美在线| 最近中文字幕日韩精品| 欧美一区网站| 欧美一区二区三区在线| 欧美另类专区| 亚洲电影专区| 亚洲国产精品久久久久| 久久久99爱| 久久久精品午夜少妇| 国产精品免费一区二区三区观看| 亚洲精品免费在线| 亚洲人人精品| 欧美成人国产一区二区| 欧美二区在线播放| 亚洲国产日韩欧美在线图片| 久久天堂精品| 美女主播视频一区| 激情综合中文娱乐网| 欧美一区二区三区在线视频| 一本一本a久久| 久久精品国产一区二区三区免费看 | 中文一区在线| 欧美日本免费| 99re国产精品| 亚洲一区国产视频| 国产精品国产福利国产秒拍| 日韩天堂在线视频| 亚洲影视在线播放| 国产精品一二三视频| 亚洲欧美日韩一区二区| 久久久国产一区二区| 国内精品视频久久| 久久蜜桃精品| 亚洲国产精品va在线看黑人| 亚洲精品久久嫩草网站秘色| 欧美日韩高清在线播放| 国产精品99久久久久久宅男 | 亚洲人成免费| 欧美日韩精品免费观看视频完整| 99精品视频免费| 欧美一区二区三区视频免费| 国内精品久久久久国产盗摄免费观看完整版| 欧美一区二区三区在线观看视频 | 欧美一区二区三区免费大片| 国产日韩欧美三级| 久久综合五月天婷婷伊人| 亚洲欧洲日产国产综合网| 亚洲一二三区在线| 国内精品嫩模av私拍在线观看| 欧美大尺度在线观看| 亚洲网站在线观看| 免费久久99精品国产| 亚洲神马久久| 伊人色综合久久天天| 欧美日韩亚洲网| 久久青草福利网站| 亚洲图片自拍偷拍| 欧美黄免费看| 久久激情五月婷婷| 99国内精品| 激情综合视频| 国产精品视频| 欧美精品一区二| 久久久999精品| 亚洲午夜精品一区二区三区他趣| 欧美不卡在线视频| 性做久久久久久久免费看| 亚洲国产精彩中文乱码av在线播放| 欧美日韩网站| 欧美成人福利视频| 久久精品伊人| 午夜精品视频在线观看一区二区| 亚洲精品国产精品国自产在线| 久久亚洲欧美国产精品乐播| 亚洲自拍高清| 一本色道久久88综合亚洲精品ⅰ| 一色屋精品亚洲香蕉网站| 国产精品日韩欧美一区| 欧美日韩99| 欧美日韩成人免费| 欧美国产免费| 久久免费观看视频| 欧美一区二区在线观看| 国产欧美日韩亚洲精品|