• <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>

            牽著老婆滿街逛

            嚴(yán)以律己,寬以待人. 三思而后行.
            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):對成塊數(shù)據(jù)流的處理

            轉(zhuǎn)載自:http://blog.csdn.net/leehark/article/details/7671462

            PseudoTcp對成塊數(shù)據(jù)流的處理

            上一篇談?wù)摿薚CPPTCP交互數(shù)據(jù)流的處理方法。這一篇談?wù)摿硪粋€(gè)數(shù)據(jù)流--成塊數(shù)據(jù)流。成塊數(shù)據(jù)流主要采用滑動(dòng)窗口協(xié)議和慢啟動(dòng)算法來控制成塊數(shù)據(jù)的流量。

            滑動(dòng)窗口

                滑動(dòng)窗口允許發(fā)送方在停止并等待確認(rèn)前可以連續(xù)發(fā)送多個(gè)分組。因此發(fā)送方不必每發(fā)一個(gè)就停下來等待,這樣可以加速數(shù)據(jù)的傳輸。這個(gè)Nagle算法沖突么?不會(huì),因?yàn)槌蓧K數(shù)據(jù)流的分組都是滿載傳輸?shù)?,根?jù)Nagle算法,當(dāng)?shù)却l(fā)送數(shù)據(jù)的大小和窗口大小都大于MSS時(shí),會(huì)立即發(fā)送。

                如果發(fā)送方一直傳輸數(shù)據(jù)會(huì)出現(xiàn)經(jīng)常丟包的現(xiàn)象,特別是快的發(fā)送方發(fā)給慢的接收方。當(dāng)接收方還沒有處理數(shù)據(jù),發(fā)送方就接連發(fā)來了數(shù)據(jù)會(huì)填滿接收方的緩沖區(qū),從而后續(xù)的數(shù)據(jù)將被丟棄,為了減少網(wǎng)絡(luò)上丟包的次數(shù),用一種機(jī)制來限制發(fā)送方傳輸數(shù)據(jù)。

            因此出現(xiàn)了滑動(dòng)窗口,如下圖:


                滑動(dòng)窗口分為4個(gè)部分:

                    上圖1~3為發(fā)送并確認(rèn)的數(shù)據(jù)段

                    上圖4~6為已經(jīng)發(fā)送,但是沒有被確認(rèn)的數(shù)據(jù)段

                    上圖7~9為可用的窗口,即滑動(dòng)窗口,發(fā)送方還可以發(fā)送的數(shù)據(jù)段空間

                    上圖10以上為不能夠發(fā)送。


                當(dāng)接收方確認(rèn)數(shù)據(jù)后,滑動(dòng)窗口兩邊不斷的向右移動(dòng)。

                    窗口合攏:當(dāng)發(fā)送方發(fā)送數(shù)據(jù)并等待確認(rèn)時(shí),滑動(dòng)窗口的左邊向右移動(dòng)。

                    窗口張開:當(dāng)接收方收到數(shù)據(jù)并確認(rèn)且釋放緩沖區(qū)數(shù)據(jù)時(shí),右邊向右移動(dòng)。 

                    窗口收縮:當(dāng)接收方的緩沖區(qū)大小變小時(shí),右邊向左移動(dòng),但不建議使用這種方式。

                滑動(dòng)窗口時(shí)通過窗口大小來更新。當(dāng)接收方收到數(shù)據(jù)后,重新計(jì)算接收緩沖區(qū)的大小,并通告發(fā)送方。如果通告窗口大小為0,則發(fā)送方不能再發(fā)送數(shù)據(jù),等到窗口大小為非0,這樣可以有效的避免因接收方緩沖區(qū)滿導(dǎo)致的分組的丟失。

                那么PTCP是怎么實(shí)現(xiàn)的呢?

                PTCP通過m_rbuf_len來標(biāo)示接收緩沖區(qū)大小。如果緩沖區(qū)大小小于65536時(shí),m_rwnd_scale0m_rcv_wnd標(biāo)示窗口大小,而大于65535時(shí),通過如下算法來調(diào)整m_rbuf_lenm_rwnd_scale。調(diào)整后根據(jù)緩沖區(qū)中可用空間來更新窗口大小m_rcv_wnd 。為什么選擇65535為界限呢?因?yàn)樵?/span>PTCP的頭部中window字段的長度為16個(gè)bit,只能支持窗口打小范圍0~65535(包含65535)。

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

                 當(dāng)PTCP三次握手時(shí),通過PTCP選項(xiàng)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) {//判斷窗口擴(kuò)大選項(xiàng)是否開啟  
            6.     buf.WriteUInt8(TCP_OPT_WND_SCALE);//增加窗口擴(kuò)大選項(xiàng)  
            7.     buf.WriteUInt8(1);  
            8.     buf.WriteUInt8(m_rwnd_scale);//窗口擴(kuò)大擴(kuò)大因子  
            9.   }  
            10.   m_snd_wnd = buf.Length();  
            11.   queue(buf.Data(), buf.Length(), true);  
            12. }  

                PTCP接收窗口擴(kuò)大因子對應(yīng)的控制包之后,通過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) {//判斷是否到了緩沖區(qū)末  
            9.       break;  
            10.     } else if (kind == TCP_OPT_NOOP) {//空選項(xiàng)  
            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);//更新選項(xiàng)對應(yīng)的值  
            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);//如果對端不支持窗口擴(kuò)大因子,且本端的緩沖區(qū)大小超過了65535,則改為60K,因?yàn)楸仨殐啥硕贾С执翱跀U(kuò)大因子才能使用m_swnd_scale。  
            27.       m_swnd_scale = 0;  
            28.     }  
            29.   }  
            30. }  

                接收方調(diào)整窗口大小,如下:

                窗口合攏:當(dāng)接收方收到數(shù)據(jù)時(shí),會(huì)從窗口大小里減去把接收緩沖區(qū)消耗的數(shù)據(jù)大小。

            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) {//如果當(dāng)前收到的分組恰好是下一個(gè)需要的分組  
            9.         m_rbuf.ConsumeWriteBuffer(seg.len);//消耗接收緩沖區(qū)  
            10.         m_rcv_nxt += seg.len;//更新下一個(gè)需要的分組  
            11.         m_rcv_wnd -= seg.len;//更新窗口大小,減去剛才消耗的緩沖區(qū)  
            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;//之前收到的分組包含了下一個(gè)需要的seq number,調(diào)整m_rcv_nxt   
            20.             m_rcv_wnd -= nAdjust;//m_rcv_nxt增加了,且接收緩沖區(qū)被填充了,窗口大小也隨之更新。  
            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);//更新接收分組列表,當(dāng)收到下一個(gè)所需要的分組時(shí),重組恢復(fù)所用。  
            33.       }  
            34. ......  
            35. }  

                窗口張開當(dāng)應(yīng)用層調(diào)用Recv來獲取PTCP接收的數(shù)據(jù)時(shí),PTCP會(huì)把此部分?jǐn)?shù)據(jù)清除,騰空緩沖區(qū)并擴(kuò)大窗口大小。

            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);//獲取接收緩沖區(qū)可用空間  
            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變?yōu)橛锌捎每臻g時(shí),立即通告對方可以繼續(xù)發(fā)送數(shù)據(jù)。  
            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);//這里會(huì)把窗口擴(kuò)大因子也算進(jìn)去  
            12. ......  
            13. }  

                當(dāng)發(fā)送方收到接收方發(fā)送的窗口大小后,可發(fā)送大小計(jì)算為窗口大小減去已經(jīng)發(fā)送但未被確認(rèn)的數(shù)據(jù)大小。

            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;//已經(jīng)發(fā)送但未被確認(rèn)的數(shù)據(jù)大小  
            5. uint32 nUseable = (nInFlight < nWindow) ? (nWindow - nInFlight) : 0;//發(fā)送方可發(fā)送數(shù)據(jù)大小  
            6. ......  
            7. }  

            慢啟動(dòng)

                當(dāng)接收方和發(fā)送方之間存在多個(gè)路由器和速率較慢的鏈路時(shí),一些中間的路由器必須緩存分組。一開始發(fā)送方向接收方發(fā)送多個(gè)分組,可能會(huì)把緩存填滿,這會(huì)嚴(yán)重降低TCP的吞吐量。

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

                發(fā)送方首先發(fā)送一個(gè)報(bào)文段,當(dāng)收到ACK時(shí),cwnd變?yōu)?/span>2,可以發(fā)送2個(gè)報(bào)文段,當(dāng)收到2個(gè)ACK時(shí)cwnd變?yōu)?/span>4,發(fā)送方可以發(fā)送4個(gè)報(bào)文段,依次類推,慢啟動(dòng)算法是指數(shù)增長的。

                PTCP實(shí)現(xiàn)慢啟動(dòng)算法如下:

                Cwnd初始值為2個(gè)MSS,當(dāng)收到ACK時(shí)cwnd增加一個(gè)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;//當(dāng)收到有效的ACK時(shí),cwnd增加一個(gè)MSS。  
            12.       } else {  
            13.         m_cwnd += talk_base::_max<uint32>(1, m_mss * m_mss / m_cwnd);  
            14.       }  
            15. }  
            16.   }  
            17. ......  
            18. }  


                當(dāng)發(fā)送方發(fā)送數(shù)據(jù)時(shí),取窗口大小為通告窗口(m_snd_wnd和擁塞窗口(cwnd)的最小值,然后減去已經(jīng)發(fā)送的未被確認(rèn)的大小為當(dāng)前可發(fā)送數(shù)據(jù)大?。?/span>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;//減去已經(jīng)發(fā)送的未被確認(rèn)的大小為當(dāng)前可發(fā)送數(shù)據(jù)大小  
            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);//已經(jīng)緩存的數(shù)據(jù)中可發(fā)送數(shù)據(jù)大小  
            15. ......  
            16. }  

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

            久久久女人与动物群交毛片| 国产精品久久久久久久久久免费| 久久成人影院精品777| 久久国产亚洲精品无码| 精品久久久久久国产三级| 久久亚洲2019中文字幕| 久久香综合精品久久伊人| 国产精品成人99久久久久91gav| 亚洲精品tv久久久久久久久| 久久99精品国产麻豆宅宅| 国产亚洲精久久久久久无码| 久久九九久精品国产免费直播| 国产激情久久久久久熟女老人| 国产精品一久久香蕉国产线看观看 | 国产产无码乱码精品久久鸭 | 国内精品伊人久久久久777| 久久综合给合久久狠狠狠97色69| 青草影院天堂男人久久| 久久91精品国产91| 激情综合色综合久久综合| 国产精品美女久久久久久2018| 久久久久一本毛久久久| 亚洲中文字幕久久精品无码喷水| 国产AⅤ精品一区二区三区久久| 狠狠色婷婷久久一区二区| 国产精品嫩草影院久久| AV无码久久久久不卡蜜桃| 亚洲AV无码久久| 欧美成人免费观看久久| 国产精品99久久精品爆乳| 久久99精品国产99久久6男男| 99re久久精品国产首页2020| 久久亚洲精精品中文字幕| 亚洲∧v久久久无码精品| 久久久久波多野结衣高潮| 中文字幕精品无码久久久久久3D日动漫 | 美女久久久久久| 品成人欧美大片久久国产欧美...| 99热成人精品热久久669| 久久国产精品99精品国产| 99久久成人国产精品免费|