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

牽著老婆滿街逛

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

libjingle源碼解析(5)-【PseudoTcp】建立UDP之上的TCP(3):對成塊數據流的處理

轉載自:http://blog.csdn.net/leehark/article/details/7671462

PseudoTcp對成塊數據流的處理

上一篇談論了TCPPTCP交互數據流的處理方法。這一篇談論另一個數據流--成塊數據流。成塊數據流主要采用滑動窗口協議和慢啟動算法來控制成塊數據的流量。

滑動窗口

    滑動窗口允許發送方在停止并等待確認前可以連續發送多個分組。因此發送方不必每發一個就停下來等待,這樣可以加速數據的傳輸。這個Nagle算法沖突么?不會,因為成塊數據流的分組都是滿載傳輸的,根據Nagle算法,當等待發送數據的大小和窗口大小都大于MSS時,會立即發送。

    如果發送方一直傳輸數據會出現經常丟包的現象,特別是快的發送方發給慢的接收方。當接收方還沒有處理數據,發送方就接連發來了數據會填滿接收方的緩沖區,從而后續的數據將被丟棄,為了減少網絡上丟包的次數,用一種機制來限制發送方傳輸數據。

因此出現了滑動窗口,如下圖:


    滑動窗口分為4個部分:

        上圖1~3為發送并確認的數據段

        上圖4~6為已經發送,但是沒有被確認的數據段

        上圖7~9為可用的窗口,即滑動窗口,發送方還可以發送的數據段空間

        上圖10以上為不能夠發送。


    當接收方確認數據后,滑動窗口兩邊不斷的向右移動。

        窗口合攏:當發送方發送數據并等待確認時,滑動窗口的左邊向右移動。

        窗口張開:當接收方收到數據并確認且釋放緩沖區數據時,右邊向右移動。 

        窗口收縮:當接收方的緩沖區大小變小時,右邊向左移動,但不建議使用這種方式。

    滑動窗口時通過窗口大小來更新。當接收方收到數據后,重新計算接收緩沖區的大小,并通告發送方。如果通告窗口大小為0,則發送方不能再發送數據,等到窗口大小為非0,這樣可以有效的避免因接收方緩沖區滿導致的分組的丟失。

    那么PTCP是怎么實現的呢?

    PTCP通過m_rbuf_len來標示接收緩沖區大小。如果緩沖區大小小于65536時,m_rwnd_scale0m_rcv_wnd標示窗口大小,而大于65535時,通過如下算法來調整m_rbuf_lenm_rwnd_scale。調整后根據緩沖區中可用空間來更新窗口大小m_rcv_wnd 。為什么選擇65535為界限呢?因為在PTCP的頭部中window字段的長度為16bit,只能支持窗口打小范圍0~65535(包含65535)。

  1. void  
  2. PseudoTcp::resizeReceiveBuffer(uint32 new_size) {  
  3.   uint8 scale_factor = 0;  
  4.   //處理大于65536字節的緩沖區,更新scale_factor  
  5.   while (new_size > 0xFFFF) {  
  6.     ++scale_factor;  
  7.     new_size >>= 1;  
  8.   }  
  9.   new_size <<= scale_factor;//當緩沖區大小大于65535時,大小會被調整  
  10.   bool result = m_rbuf.SetCapacity(new_size);//更新緩沖區  
  11.   m_rbuf_len = new_size;//更新緩沖區大小  
  12.   m_rwnd_scale = scale_factor;//更新窗口擴大因子  
  13.   m_ssthresh = new_size;  
  14.   size_t available_space = 0;  
  15.   m_rbuf.GetWriteRemaining(&available_space);  
  16.   m_rcv_wnd = available_space;//更新可用窗口大小  
  17. }  

     當PTCP三次握手時,通過PTCP選項TCP_OPT_WND_SCALE來通告對方m_rwnd_scale的大小。

  1. void  
  2. PseudoTcp::queueConnectMessage() {  
  3.   talk_base::ByteBuffer buf(talk_base::ByteBuffer::ORDER_NETWORK);  
  4.   buf.WriteUInt8(CTL_CONNECT);  
  5.   if (m_support_wnd_scale) {//判斷窗口擴大選項是否開啟  
  6.     buf.WriteUInt8(TCP_OPT_WND_SCALE);//增加窗口擴大選項  
  7.     buf.WriteUInt8(1);  
  8.     buf.WriteUInt8(m_rwnd_scale);//窗口擴大擴大因子  
  9.   }  
  10.   m_snd_wnd = buf.Length();  
  11.   queue(buf.Data(), buf.Length(), true);  
  12. }  

    PTCP接收窗口擴大因子對應的控制包之后,通過parseOptions方法來解析此包如下:

  1. void  
  2. PseudoTcp::parseOptions(const char* data, uint32 len) {  
  3.   std::set<uint8> options_specified;  
  4.   talk_base::ByteBuffer buf(data, len);  
  5.   while (buf.Length()) {  
  6.     uint8 kind = TCP_OPT_EOL;  
  7.     buf.ReadUInt8(&kind);  
  8.     if (kind == TCP_OPT_EOL) {//判斷是否到了緩沖區末  
  9.       break;  
  10.     } else if (kind == TCP_OPT_NOOP) {//空選項  
  11.       continue;  
  12.     }  
  13.     UNUSED(len);  
  14.     uint8 opt_len = 0;  
  15.     buf.ReadUInt8(&opt_len);  
  16.     if (opt_len <= buf.Length()) {  
  17.       applyOption(kind, buf.Data(), opt_len);//更新選項對應的值  
  18.       buf.Consume(opt_len);  
  19.     } else {  
  20.       return;  
  21.     }  
  22.     options_specified.insert(kind);  
  23.   }  
  24.   if (options_specified.find(TCP_OPT_WND_SCALE) == options_specified.end()) {  
  25.     if (m_rwnd_scale > 0) {  
  26.       resizeReceiveBuffer(DEFAULT_RCV_BUF_SIZE);//如果對端不支持窗口擴大因子,且本端的緩沖區大小超過了65535,則改為60K,因為必須兩端都支持窗口擴大因子才能使用m_swnd_scale。  
  27.       m_swnd_scale = 0;  
  28.     }  
  29.   }  
  30. }  

    接收方調整窗口大小,如下:

    窗口合攏:當接收方收到數據時,會從窗口大小里減去把接收緩沖區消耗的數據大小。

  1. bool PseudoTcp::process(Segment& seg) {  
  2.     ......  
  3.       uint32 nOffset = seg.seq - m_rcv_nxt;  
  4.       talk_base::StreamResult result = m_rbuf.WriteOffset(seg.data, seg.len,  
  5.                                                           nOffset, NULL);  
  6.       ASSERT(result == talk_base::SR_SUCCESS);  
  7.       UNUSED(result);  
  8.       if (seg.seq == m_rcv_nxt) {//如果當前收到的分組恰好是下一個需要的分組  
  9.         m_rbuf.ConsumeWriteBuffer(seg.len);//消耗接收緩沖區  
  10.         m_rcv_nxt += seg.len;//更新下一個需要的分組  
  11.         m_rcv_wnd -= seg.len;//更新窗口大小,減去剛才消耗的緩沖區  
  12.         bNewData = true;  
  13.         RList::iterator it = m_rlist.begin();  
  14.         while ((it != m_rlist.end()) && (it->seq <= m_rcv_nxt)) {  
  15.           if (it->seq + it->len > m_rcv_nxt) {  
  16.             sflags = sfImmediateAck; // (Fast Recovery)  
  17.             uint32 nAdjust = (it->seq + it->len) - m_rcv_nxt;  
  18.             m_rbuf.ConsumeWriteBuffer(nAdjust);  
  19.             m_rcv_nxt += nAdjust;//之前收到的分組包含了下一個需要的seq number,調整m_rcv_nxt   
  20.             m_rcv_wnd -= nAdjust;//m_rcv_nxt增加了,且接收緩沖區被填充了,窗口大小也隨之更新。  
  21.           }  
  22.           it = m_rlist.erase(it);  
  23.         }  
  24.       } else {//拿到的分組不是所需要的,但是有效的分組  
  25.         RSegment rseg;  
  26.         rseg.seq = seg.seq;  
  27.         rseg.len = seg.len;  
  28.         RList::iterator it = m_rlist.begin();  
  29.         while ((it != m_rlist.end()) && (it->seq < rseg.seq)) {  
  30.           ++it;  
  31.         }  
  32.         m_rlist.insert(it, rseg);//更新接收分組列表,當收到下一個所需要的分組時,重組恢復所用。  
  33.       }  
  34. ......  
  35. }  

    窗口張開當應用層調用Recv來獲取PTCP接收的數據時,PTCP會把此部分數據清除,騰空緩沖區并擴大窗口大小。

  1. int PseudoTcp::Recv(char* buffer, size_t len) {  
  2.  ......  
  3.   talk_base::StreamResult result = m_rbuf.Read(buffer, len, &read, NULL);  
  4. ......  
  5.   size_t available_space = 0;  
  6.   m_rbuf.GetWriteRemaining(&available_space);//獲取接收緩沖區可用空間  
  7.   if (uint32(available_space) - m_rcv_wnd >=  
  8.       talk_base::_min<uint32>(m_rbuf_len / 2, m_mss)) {  
  9.     bool bWasClosed = (m_rcv_wnd == 0); // !?! Not sure about this was closed business  
  10.     m_rcv_wnd = available_space;//更新窗口大小,此為窗口張開過程  
  11.     if (bWasClosed) {  
  12.       attemptSend(sfImmediateAck);//如果窗口大小從0變為有可用空間時,立即通告對方可以繼續發送數據。  
  13.     }  
  14.   }  
  15.   return read;  
  16. }  

    通告窗口大小給對方:

  1. IPseudoTcpNotify::WriteResult PseudoTcp::packet(uint32 seq, uint8 flags,  
  2.                                                 uint32 offset, uint32 len) {  
  3.   ASSERT(HEADER_SIZE + len <= MAX_PACKET);  
  4.   uint32 now = Now();  
  5.   uint8 buffer[MAX_PACKET];  
  6.   long_to_bytes(m_conv, buffer);  
  7.   long_to_bytes(seq, buffer + 4);  
  8.   long_to_bytes(m_rcv_nxt, buffer + 8);  
  9.   buffer[12] = 0;  
  10.   buffer[13] = flags;  
  11.   short_to_bytes(static_cast<uint16>(m_rcv_wnd >> m_rwnd_scale), buffer + 14);//這里會把窗口擴大因子也算進去  
  12. ......  
  13. }  

    當發送方收到接收方發送的窗口大小后,可發送大小計算為窗口大小減去已經發送但未被確認的數據大小。

  1. void PseudoTcp::attemptSend(SendFlags sflags) {  
  2. ......  
  3.     uint32 nWindow = talk_base::_min(m_snd_wnd, cwnd);//接收方窗口大小  
  4.     uint32 nInFlight = m_snd_nxt - m_snd_una;//已經發送但未被確認的數據大小  
  5. uint32 nUseable = (nInFlight < nWindow) ? (nWindow - nInFlight) : 0;//發送方可發送數據大小  
  6. ......  
  7. }  

慢啟動

    當接收方和發送方之間存在多個路由器和速率較慢的鏈路時,一些中間的路由器必須緩存分組。一開始發送方向接收方發送多個分組,可能會把緩存填滿,這會嚴重降低TCP的吞吐量。

    TCP通過慢啟動算法解決上述問題:首先設置擁塞窗口cwnd1,當發送方每收到一個ACK擁塞窗口加1個報文段。發送方取擁塞窗口和通告窗口的最小值為發送上限。擁塞窗口是發送方使用的流量控制,而通告窗口時接收方使用的流量控制。

    發送方首先發送一個報文段,當收到ACK時,cwnd變為2,可以發送2個報文段,當收到2ACKcwnd變為4,發送方可以發送4個報文段,依次類推,慢啟動算法是指數增長的。

    PTCP實現慢啟動算法如下:

    Cwnd初始值為2MSS,當收到ACKcwnd增加一個MSS

  1. Bool PseudoTcp::process(Segment& seg) {  
  2. ......  
  3.   // Check if this is a valuable ack  
  4.   if ((seg.ack > m_snd_una) && (seg.ack <= m_snd_nxt)) {  
  5. if (m_dup_acks >= 3) {  
  6. ......  
  7. }else{  
  8.       m_dup_acks = 0;  
  9.       // Slow start, congestion avoidance  
  10.       if (m_cwnd < m_ssthresh) {  
  11.         m_cwnd += m_mss;//當收到有效的ACK時,cwnd增加一個MSS。  
  12.       } else {  
  13.         m_cwnd += talk_base::_max<uint32>(1, m_mss * m_mss / m_cwnd);  
  14.       }  
  15. }  
  16.   }  
  17. ......  
  18. }  


    當發送方發送數據時,取窗口大小為通告窗口(m_snd_wnd和擁塞窗口(cwnd)的最小值,然后減去已經發送的未被確認的大小為當前可發送數據大小(nUseable )。

  1. void PseudoTcp::attemptSend(SendFlags sflags) {  
  2. ......  
  3.   while (true) {  
  4.     uint32 cwnd = m_cwnd;  
  5.     if ((m_dup_acks == 1) || (m_dup_acks == 2)) { // Limited Transmit  
  6.       cwnd += m_dup_acks * m_mss;  
  7.     }  
  8.     uint32 nWindow = talk_base::_min(m_snd_wnd, cwnd);//取窗口大小為通告窗口和擁塞窗口的最小值  
  9.     uint32 nInFlight = m_snd_nxt - m_snd_una;  
  10.     uint32 nUseable = (nInFlight < nWindow) ? (nWindow - nInFlight) : 0;//減去已經發送的未被確認的大小為當前可發送數據大小  
  11.     size_t snd_buffered = 0;  
  12.     m_sbuf.GetBuffered(&snd_buffered);  
  13.     uint32 nAvailable =  
  14.         talk_base::_min(static_cast<uint32>(snd_buffered) - nInFlight, m_mss);//已經緩存的數據中可發送數據大小  
  15. ......  
  16. }  

posted on 2013-09-01 14:07 楊粼波 閱讀(512) 評論(0)  編輯 收藏 引用


只有注冊用戶登錄后才能發表評論。
網站導航: 博客園   IT新聞   BlogJava   博問   Chat2DB   管理


青青草原综合久久大伊人导航_色综合久久天天综合_日日噜噜夜夜狠狠久久丁香五月_热久久这里只有精品
  • <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>
            香蕉成人久久| 久久国产精品久久久| 亚洲国产欧美一区二区三区丁香婷 | 亚洲国产成人精品久久久国产成人一区 | 国产精品香蕉在线观看| 欧美激情无毛| 欧美福利网址| 久久一区二区精品| 亚洲欧美一区二区三区久久 | 亚洲日本无吗高清不卡| 亚洲成色999久久网站| 美日韩精品免费观看视频| 久久久一区二区三区| 久久久xxx| 久久精品视频99| 久久成人18免费观看| 欧美在线视频一区二区| 欧美在线三区| 亚洲嫩草精品久久| 亚洲女人天堂成人av在线| 午夜免费在线观看精品视频| 99精品99| 亚洲一区自拍| 性色一区二区| 久久狠狠婷婷| 久久夜色精品国产| 国产欧美日韩亚州综合| 国产精品国产一区二区 | 日韩视频精品在线| 亚洲精品综合精品自拍| 99视频在线观看一区三区| 99精品久久| 亚洲欧美国产不卡| 性欧美videos另类喷潮| 久久精品人人做人人爽电影蜜月| 久久久精品tv| 久久久久网址| 免费看亚洲片| 欧美视频中文一区二区三区在线观看 | 久久精品亚洲精品| 欧美大片免费久久精品三p| 亚洲精品视频免费在线观看| 午夜精品一区二区三区在线视 | 国产精品青草久久| 在线国产精品播放| 日韩一区二区精品| 久久国产欧美| 亚洲精品久久嫩草网站秘色| 亚洲性感美女99在线| 久久亚洲欧美| 国产精品色一区二区三区| 在线观看视频日韩| 亚洲欧美日韩精品久久久久| 免费看的黄色欧美网站| 亚洲午夜精品视频| 男女激情视频一区| 国产偷久久久精品专区| 99精品欧美一区二区三区综合在线| 久久国产成人| 99ri日韩精品视频| 久久野战av| 国产精品中文字幕欧美| 日韩一级在线| 免费久久99精品国产| 亚洲欧美另类综合偷拍| 欧美人与禽猛交乱配视频| 一区二区三区自拍| 久久精品国产亚洲5555| 一区二区欧美在线| 欧美大片在线观看| 在线免费观看日本一区| 欧美一区二区三区四区视频| 日韩午夜在线视频| 欧美大片一区| 亚洲国产精品高清久久久| 久久爱91午夜羞羞| 亚洲图片自拍偷拍| 欧美日韩一区二区三区| 亚洲日本欧美| 欧美成人精品一区二区| 欧美伊久线香蕉线新在线| 国产精品免费在线| 亚洲一区在线观看视频 | 欧美激情2020午夜免费观看| 黄色成人在线网站| 久久久久国产一区二区| 亚洲免费在线观看| 国产精品亚洲不卡a| 亚洲影院在线观看| 一区二区三区精品视频| 欧美日本三区| 一本色道久久精品| 亚洲二区三区四区| 欧美电影免费观看高清完整版| 在线视频成人| 欧美激情亚洲激情| 免费在线日韩av| 亚洲日本成人网| 亚洲国产专区校园欧美| 欧美激情一区二区三级高清视频| 亚洲日产国产精品| 最近中文字幕mv在线一区二区三区四区| 久久嫩草精品久久久精品| 亚洲国产va精品久久久不卡综合| 欧美不卡高清| 欧美成人精品一区| 9久草视频在线视频精品| 亚洲免费观看高清在线观看| 欧美视频亚洲视频| 香蕉久久夜色| 欧美在线视频一区二区| 黄色亚洲在线| 欧美激情1区2区| 欧美极品一区| 亚洲一区二区三区四区视频 | 国产精品v片在线观看不卡| 亚洲性视频网站| 亚洲一区二区高清视频| 国产欧美精品在线观看| 久久婷婷成人综合色| 久久综合婷婷| 日韩视频在线观看一区二区| 亚洲美女毛片| 国产精品电影观看| 久久精品一级爱片| 麻豆精品在线视频| 一区二区不卡在线视频 午夜欧美不卡'| 亚洲人成亚洲人成在线观看图片| 欧美体内谢she精2性欧美| 欧美在线日韩在线| 美女任你摸久久| 亚洲图片你懂的| 欧美一进一出视频| 亚洲日本一区二区三区| 亚洲一区二区三区四区在线观看| 国产伪娘ts一区| 亚洲国产日韩综合一区| 国产精品久久久久久久一区探花| 久久性色av| 欧美日韩国产精品专区| 久久精品人人| 欧美另类视频| 久久天堂成人| 欧美三级特黄| 老司机免费视频久久| 欧美日韩黄色一区二区| 久久深夜福利免费观看| 欧美精品一区二区高清在线观看| 欧美在线啊v一区| 欧美高清在线| 久久久久久穴| 欧美色另类天堂2015| 美女黄毛**国产精品啪啪| 欧美特黄a级高清免费大片a级| 久久久天天操| 国产精品二区在线观看| 欧美成人免费播放| 国产精品免费视频观看| 亚洲国产精品国自产拍av秋霞| 国产精品一区一区| 最新国产拍偷乱拍精品| 国内自拍一区| 中文久久精品| 亚洲六月丁香色婷婷综合久久| 销魂美女一区二区三区视频在线| 亚洲毛片在线免费观看| 久久电影一区| 销魂美女一区二区三区视频在线| 欧美xxx在线观看| 久久久噜噜噜| 国产精品免费看片| 日韩一级欧洲| 亚洲精品一二三区| 久久国产一区二区| 欧美一区二区日韩一区二区| 欧美日韩一区二区三区| 亚洲国产成人精品女人久久久| 国产婷婷成人久久av免费高清| 日韩一二三区视频| 日韩午夜高潮| 男女激情视频一区| 男人的天堂亚洲在线| 国产日韩亚洲| 亚洲免费视频网站| 亚洲欧美日韩网| 欧美日韩一区二区三区在线看| 欧美激情一区三区| 亚洲高清视频一区| 久久久成人网| 久久久午夜精品| 国产午夜精品一区理论片飘花| 亚洲一区二区3| 欧美亚洲免费电影| 国产精品jvid在线观看蜜臀 | 免费观看亚洲视频大全| 狠狠色狠色综合曰曰| 欧美在线视频播放| 久久精品在线免费观看| 国产一区二区三区日韩| 欧美一区二区三区免费在线看|