naobe @ ウィキ
UDT
最終更新:
Bot(ページ名リンク)
-
view
ネットワークに戻る
目的
- 排他は無視して概略を追う。
- UDPに対して拡張した機能を理解する。
- フローコントロールの仕組み(TCPからどう改善されているか)を理解する。
UDTの特徴(www.mcs.anl.gov/~kettimut/pfldnet04.pptから抜粋)
- パケットのほとんどがMSSにパックされる 済み
- パケットベースのシーケンス番号作成 済み
- ACKは副シーケンス?
- パケット損失情報のフィードバック(NACK)
- タイマ 転送率制御、ACK,NACK, 再送タイマ
- 転送率制御、ACKは、定期的にトリガ 済み
- NAKタイマは、再送が増加した時間間隔で届かなかったときに損失情報を再送するために用いる 済み
- 輻輳制御
- 転送率ベースの輻輳制御
- パケット送信間隔を調節する 済み
- 定期的に起動される 済み
- 転送率制御の間隔は、10msecで一定(ACKの受信で起動する) 済み
- ウィンドウベースのフロー制御
- ackされていないパケット数を制限する 済み
- ACK受信ごとに起動される。 済み
- フロー制御がスロースタートを開始する
- TCPに似ているが、セッションの開始でのみ発生する 済み
- 転送率ベースの輻輳制御
- 転送率制御
- AIMD
- 増加パラメータは、リンクキャパシティと現在の転送率に関連する。減少要因は1/9だが全ての損失イベントで減少するわけではない。
- リンクキャパシティは、UDTの送信データからサンプリングしたパケットペアを用いて調査する。 済み
- 16パケットごとのパケットが送信されて戻される??
- 受信者は、パケットが到着したときの間隔からメディアンフィルタ(中間値)を用いてリンクキャパシティを求める 済み
- AIMD
クライアント
UDT::socket
api.cpp
UDT::socket
CUDT::socket
CUDTUnited s_UDTUnited.m_bGCStatusが0かどうかの判定
api.cpp
CUDTUnited::startup() GCスレッド起動。m_bGCStatusをtrue
CUDTUnited::s_UDTUnited.newSocket(af, type); socketを作成して返す。
CUDTSocketをnew --> ns // コネクションごとのインスタンス
CUDTをnew --> ns->m_pUDT // コネクションごとのインスタンス
m_pCCFactory = new CCCFactory<CUDTCC>;
ns->m_Status = CUDTSocket::INIT;//初期状態
ns->m_ListenSocket = 0;
ns->m_pUDT->m_SocketID = ns->m_SocketID;
ns->m_pUDT->m_iSockType = (SOCK_STREAM == type) ? UDT_STREAM : UDT_DGRAM;
ns->m_pUDT->m_iIPversion = ns->m_iIPversion = af;
ns->m_pUDT->m_pCache = m_pCache;//UDT network information cache
common.h,common.cpp
m_Sockets[ns->m_SocketID] = ns // CUDTUnitedの中のマップに、作成したCUDTSocketを格納
return ns->m_SocketID
UDT::connect
api.cpp
connect(UDTSOCKET u, const struct sockaddr* name, int namelen)// UDTSOCKETはint
CUDT::connect(u, name, namelen);
CUDTUnited::s_UDTUnited.connect(u, name, namelen);
CUDTSocket* s = locate(u);//内部で保管しているsocketを取得
core.cpp
((CUDTSocket*)s)->m_pUDT->open(); // CUDT* m_pUDT
フィールド設定(CUDTのメンバ初期化。m_iPktSize, m_iPayloadSizeなど)
m_pSNode = new CSNode;
m_pRNode = new CRNode;
common.cpp
CTimer::rdtsc(m_ullNextACKTime); // m_ullNextACKTimeに現在時刻(μsec)を設定
m_ullNextACKTime += m_ullSYNInt; // 次のack時刻を決定 +10msec
同様に、m_ullNextNAKTime、m_ullNextEXPTimeを決める。
updateMux(s->m_pUDT);
CMultiplexer m;//マルチプレクサ
m.m_pChannel = new CChannel(u->m_iIPversion);//チャネル設定
m.m_pChannel->open(addr);
channel.cpp
CChannel::open(const sockaddr* addr)
DGRAMソケット作成
getaddrinfo(NULL, "0", &hints, &res)// システムコール 自分自身のアドレスを求める
bind(m_iSocket, res->ai_addr, res->ai_addrlen)// システムコール ソケットとアドレスをbind
CChannel::setUDPSockOpt() //受信バッファサイズ、送信バッファサイズ、タイムアウト、非ブロッキングモード設定
m.m_pChannel->getSockAddr(sa);//saにソケットのローカルアドレスを設定する
CChannel::getSockAddr(sockaddr* addr)
m.m_iPortにローカルのポート番号を設定する
m.m_pTimer = new CTimer;
m.m_pSndQueue->init(m.m_pChannel, m.m_pTimer);
queue.cpp
CSndQueue::init(const CChannel* c, const CTimer* t)
CSndQueueにチャネル、タイマ、送信リストを設定
[[pthread]]_create(&m_WorkerThread, NULL, CSndQueue::worker, this)// 送信スレッド起動
CSndQueue::worker(void* param) // スレッドとして起動
クローズするまで以下を永久ループ
uint64_t ts = self->m_pSndUList->getNextProcTime();//次回実行時刻
uint64_t CSndUList::getNextProcTime()//m_pHeap[0]->m_llTimeStampを返す。(初期値は0 CUDT::sendでは1を設定 輻輳制御で値が変わる(pCC->onAck()参照)。)
ts <= 0 のとき
self->m_WindowCondの条件が整うまで待ち
ts > 0 のとき
現在時刻 < 次回実行時刻ならば次回実行時刻まで待つ(probe OFFのときは送信間隔まで待つ)
self->m_pSndUListからパケットを取得する。
self->m_pSndUList->pop(addr, pkt)
最初のノードからCUDT(u)を取得
最初のノードを削除
u->packData(pkt, ts)
int CUDT::packData(CPacket& packet, uint64_t& ts)
common.h
CSeqNo::seqlen(const_cast<int32_t&>(m_iSndLastAck),CSeqNo::incseq(m_iSndCurrSeqNo))
CSeqNo::incseq(m_iSndCurrSeqNo)
m_iSndCurrSeqNoは乱数で決めたシーケンス番号-1。1追加。
m_iSndLastAck:
ウィンドウサイズ(m_iFlowWindowSizeとm_dCongestionWindowの小さい方) >= m_iSndCurrSeqNo + 1 - m_iSndLastAckのとき(m_dCongestionWindowは、スロースタートでないときは16+RTT間の送信データパケット数に設定(pCC->onAck())。)
つまり、前回のACK受信がRTTより前ならデータ送信しない(ACK送信間隔:10ms, RTT:100ms)-->9個のACK損失
buffer.cpp
m_pSndBuffer->readData(&(packet.m_pcData),packet.m_iMsgNo)
int CSndBuffer::readData(char** data, int32_t& msgno)
現在のブロック(先頭)のデータをdataにセット。メッセージ番号をmsgnoにセット。ブロック長を返す。
現在のブロックを次のブロックに設定
m_iSndCurrSeqNoを+1
packet.m_iSeqNo = m_iSndCurrSeqNo;//m_iSeqNo 初期値:0
16パケットごとにprobeをON
m_pSndTimeWindow->onPktSent(packet.m_iTimeStamp);// packet.m_iTimeStamp: 現在時刻 - 接続時刻(μsec)
void CPktTimeWindow::onPktSent(const int& currtime)
最小間隔(m_iMinPktSndInt 初期値1sec)を更新(currtimeと前回送信時刻の差分と最小間隔の小さい方)
前回送信時刻にcurrtimeをセット
tsにprobeのときは現在時刻を、probeでないときは現在時刻+送信間隔(m_ullInterval, 輻輳状態によって変わる)をセット
m_ullIntervalはm_dPktSndPeriodに周波数補正したもの。m_dPktSndPeriodは、ACK受信、パケット損失で再計算する。
m_ullTargetTime = ts
ts > 0のとき
insert_(ts, u);
m_pHeap[m_iLastEntry] = n;//リストにCUDTを登録
m_pHeap[m_iLastEntry]とm_pHeap[m_iLastEntry/2]のm_llTimeStampを比較して時刻が逆順なら交換(ソート??)
u->m_pSNode->n->m_llTimeStamp = ts//次回送信時刻
self->m_pChannel->sendto(addr, pkt);//ソケットから送信先を求めて送信する。
データ送信フォーマットは、送信フォーマット参照。
m.m_pRcvQueue->init(32, u->m_iPayloadSize, m.m_iIPversion, 1024, m.m_pChannel, m.m_pTimer);
queue.cpp
void CRcvQueue::init(const int& qsize, const int& payload, const int& version, const int& hsize, const CChannel* cc, const CTimer* t)
m_UnitQueue.init(qsize, payload, version);
int CUnitQueue::init(const int& size, const int& mss, const int& version)//キュー作成
tempq = new CQEntry;
m_pQEntry = m_pCurrQueue = m_pLastQueue = tempq;
pthread_create(&m_WorkerThread, NULL, CRcvQueue::worker, this)//受信スレッド起動
CRcvQueue::worker//スレッドとして起動
クローズするまで以下を永久ループ
self->ifNewEntry()が0でない(CRcvQueueにCUDTが登録されている。CUDT::connectで登録)
受信リストにコネクション登録
受信ハッシュにコネクション登録
self->m_pChannel->recvfrom(addr, unit->m_Packet)//
channel.cpp
int CChannel::recvfrom(sockaddr* addr, CPacket& packet)
int res = recvmsg(m_iSocket, &mh, 0);//受信
ヘッダ部、データ部をホストバイトオーダに変換。=> packet(packet.m_nHeader, packet.m_pcData)に結果を格納。
u = self->m_pHash->lookup(id)//受信パケットの宛先コネクションIDから一致する送信コネクションを求める
データ受信のとき
u->processData(unit);
int CUDT::processData(CUnit* unit)
window.cpp
m_pRcvTimeWindow->onPktArrival();
m_piPktWindowに(現在時刻-前回受信時刻をセット)。受信ごとに位置を変えてセット(ローテーションする)
プローブパケットペアのとき(シーケンス番号の下位4bitが0x0とシーケンス番号の下位4bitが0x1)
m_piProbeWindowにプローブパケットペアの受信時刻の差をセット。受信ごとに位置を変えてセット(ローテーションする。
パワーポイントの説明では、パケットペアは接近したパケット。このロジックでは接近していない??
int32_t offset = CSeqNo::seqoff(m_iRcvLastAck, packet.m_iSeqNo);//m_iRcvLastAck 前回ACK送信時の受信シーケンス+1
m_pRcvBuffer->addData(unit, offset)
m_pUnitのm_iLastAckPos + offset位置にunit(受信パケット)格納
受信シーケンス > 前に受信したパケットのシーケンス番号 + 1 のとき//損失パケットあり
損失リストに(前回受信パケットシーケンス番号+1 ~ 受信パケットシーケンス番号-1)を挿入
sendCtrl(3, NULL, lossdata, 1(損失パケット数:1) or 2 (損失パケット数:2以上)// NAK送信
損失パケット数:1のとき
ctrlpkt.pack(3, NULL, (int32_t *)rparam + 1, 4);//ctrlpktにデータセット
損失パケット数:2以上のとき
ctrlpkt.pack(3, NULL, (int32_t *)rparam, 8);
m_pSndQueue->sendto(m_pPeerAddr, ctrlpkt);// NAK送信
NAK伝送フォーマットは、伝送フォーマット参照
コントロール受信のとき
u->processCtrl(unit->m_Packet);
m_iEXPCount = 1;// Expiration counter タイムアウトカウンタ?
ACK受信 or NACK受信 or 正常ACK受信直後?のとき
次回タイムアウト時刻 = 現在時刻 + 100msec
ACKを受信したとき
受信パケットがLightAckのとき//LightAckを送るケース? draft:受信したAckがタイマーによるものでないとき。
ACKの受信シーケンス番号が増加したとき
m_iFlowWindowSizeからACKの受信シーケンス番号の増分を減じる。 意味??
m_iSndLastAckにACKの受信シーケンス番号をセット
終了
現在時刻 - 前回AckAck受信時刻 > 10msecのとき
AckAck送信
前回AckAck受信時刻に現在時刻セット
ACKの受信シーケンス番号が、前回受信のACKの受信シーケンス番号より増加したとき
m_iFlowWindowSizeに通信相手の利用可能バッファサイズをセット(m_pRcvBuffer->getAvailBufSize())
m_pSndBuffer->ackData(offset);//
void CSndBuffer::ackData(const int& offset)
m_pFirstBlockの位置をoffsetずらす。送信バッファサイズをoffset分減少する。m_pFirstBlockは、損失があるときの送信バッファ読み込みに使う。??
送信ロスリストからACK受信シーケンス-1を削除(ACKがきたから送信終了)
pthread_cond_signal(&m_SendBlockCond);
m_pSndQueue->m_pSndUList->update(this, false);
m_iRTTVar = 約RTT/2*3/4//相手のRTTも計算に入る。
m_iRTT = (7*RTT + rtt)/8 // RTT:自分のRTT, rtt:相手のRTT
m_iRTTを実測で反映している箇所がない??
m_pCC->setRTT(m_iRTT);//UDTCCのRTTを更新
m_ullMinEXPInt(最小タイムアウト時間?)にm_iRTTの約3倍を設定する。
m_iDeliveryRate = (m_iDeliveryRate * 7 + 受信ACKのリンクキャパシティ(pps)) / 8
m_iBandwidth = ( m_iBandwidth * 7 + 受信ACKの帯域) / 8
CCUDTのm_iRcvRateにm_iDeliveryRate、m_iBandwidthを設定する
m_pCC->onACK(ack);
void CUDTCC::onACK(const int32_t& ack)
スロースタートのとき(初期はtrue)
m_dCWndSizeをACK受信シーケンス番号の増加分だけ追加する。//m_dCWndSize:初期値 16
m_dCWndSize > 最大ウィンドウサイズ(値?)のとき
スロースタートOFF
ACK受信転送率があるとき
m_dPktSndPeriod = 1000000.0 / m_iRcvRate;//単位 μsec m_iRcvRate:ACK受信転送率(pps)
ACK受信転送率がないとき
m_dPktSndPeriod = m_dCWndSize / (m_iRTT + m_iRCInterval);// シーケンス番号増加b分/時間
終了
スロースタートでないとき
// 16 + ラウンドトリップタイム期間に送ったパケット数。 m_iRTT:100000(μsec), m_iRCInterval:10000(μsec)
m_dCWndSize(ウィンドウサイズ) = m_iRcvRate / 1000000.0 * (m_iRTT + m_iRCInterval) + 16;
損失があるとき
損失なしに設定
終了
m_dPktSndPeriodを帯域(ACKの帯域)を使って補正(詳細はdraft 7.2参照)//m_dPktSndPeriodはデータパケット送信間隔
m_ullInterval(転送間隔)にCUDTCCで計算したm_dPktSndPeriodを設定
m_dCongestionWindowにCUDTCCで計算したm_dCWndSizeを設定
u->checkTimers();
m_ullInterval設定(最小は、実績の最短送信間隔)
m_dCongestionWindow設定(初期値 16)
現在時刻 > 次回ACK時刻 || ACK回数 <= 受信パケット数
sendCtrl(2);//ack送信
core.cpp
損失パケットなしのとき
m_pRcvBuffer->ackData(acksize);// acksizeは、受信シーケンス番号と前回ACK時の受信シーケンス番号との差
m_iLastAckPos, m_iMaxPos更新
common.cpp
CTimer::triggerEvent();
pthread_cond_signal(&m_EventCond);//
pthread_cond_signal(&m_RecvDataCond);// recvにイベント送信
ACKシーケンス番号をincrement
現在時刻 - 前回ACK送信時刻 > 同期時間(m_ullSYNInt 10msec)のとき
ACK送信データにパケット受信速度、帯域追加
ctrlpkt.pack(2, &m_iAckSeqNo, data, 24);
CPacket::pack(const int& pkttype, void* lparam, void* rparam, const int& size)
m_pSndQueue->sendto(m_pPeerAddr, ctrlpkt);//ACK送信
m_pACKWindow->store(m_iAckSeqNo, m_iRcvLastAck);//
void CACKWindow::store(const int32_t& seq, const int32_t& ack)
次回ACK送信時刻 = 現在時刻 + ACK送信間隔(10msec)
ACKパケットフォーマットは伝送フォーマット参照
self->m_pRcvUList->update(u);//
m_vMultiplexer.insert(m_vMultiplexer.end(), m);//vectorにmを登録。CUDTUnitedのメンバ。
u->m_pSndQueue = m.m_pSndQueue;//コネクションごとに送信キュー設定
u->m_pRcvQueue = m.m_pRcvQueue;//コネクションごとに受信キュー設定
CUDTSocket *s->m_Status = CUDTSocket::OPENED; //
s->m_pUDT->connect(name); // CUDT
core.cpp
void CUDT::connect(const sockaddr* serv_addr)
CSndQueue* m_pSndQueue->sendto(serv_addr, request); // HandShakeパケットをサーバに送信 queue.cpp
m_pRcvQueue->recvfrom(m_SocketID, response) // レスポンス受信
// 受信データによってパラメータ更新
m_iMSS = res->m_iMSS;
m_iFlowWindowSize = res->m_iFlightFlagSize;
m_iPktSize = m_iMSS - 28;
m_iPayloadSize = m_iPktSize - CPacket::m_iPktHdrSize;
m_iPeerISN = res->m_iISN;
m_iRcvLastAck = res->m_iISN;
m_iRcvLastAckAck = res->m_iISN;
m_iRcvCurrSeqNo = res->m_iISN - 1;
m_PeerID = res->m_iID;
//データ作成
m_pSndBuffer = new CSndBuffer(32, m_iPayloadSize);//32:ブロックの個数, m_iPayloadSize: MSS - 28 - Header (1500 - 28 -16)
m_pRcvBuffer = new CRcvBuffer(m_iRcvBufSize, &(m_pRcvQueue->m_UnitQueue));
// after introducing lite ACK, the sndlosslist may not be cleared in time, so it requires twice space.
m_pSndLossList = new CSndLossList(m_iFlowWindowSize * 2);
m_pRcvLossList = new CRcvLossList(m_iFlightFlagSize);
m_pACKWindow = new CACKWindow(4096);
m_pRcvTimeWindow = new CPktTimeWindow(16, 64);
m_pSndTimeWindow = new CPktTimeWindow();
// CUDTへ輻輳制御登録
CCC *m_pCC = m_pCCFactory->create();// new CUDTCC。 CUDTのm_pCCに輻輳制御登録
void CUDTCC::init()
m_iRCInterval = m_iSYNInterval;//10000 UDT Rate control interval
setACKTimer(m_iRCInterval);//10000 ACKを10msecおきに送信
m_iACKPeriodに設定
m_ullInterval <- 1
m_dCongestionWindow <- 16
s->m_Status = CUDTSocket::CONNECTED; s->m_pUDT->m_pSndQueue->m_pChannel->getSockAddr(s->m_pSelfAddr);//
// register this socket for receiving data packets m_pRcvQueue->setNewEntry(this);//受信キューに登録 HandShakeの伝送フォーマットは、伝送フォーマット参照。
タイムアウトチェック、応答パケットタイプチェック、ランデブー??処理 応答パケットの値によってウィンドウサイズ、最大セグメントサイズなどの通信パラメータを更新(negotiation) データ構造(送受信データ用の領域など)を準備
UDT::send
api.cpp
UDT::send(UDTSOCKET u, const char* buf, int len, int flags)
CUDT::send(UDTSOCKET u, const char* buf, int len, int)
CUDT* udt->send((char*)buf, len)
core.cpp
CUDT::send(const char* data, const int& len)
バッファ残りサイズ:送信バッファサイズ(8192バイト) - 使用済みブロック数 * MSS
sizeはバッファ残りサイズとlenの小さいほう。
CSndBuffer* m_pSndBuffer->addBuffer(data, size);//送信バッファにデータを挿入
buffer.cpp
void CSndBuffer::addBuffer(const char* data, const int& len, const int& ttl, const bool& order)// ttl:-1, order:false
dataをセグメントで分割して複数のブロック作成、m_pLastBlockにブロック追加。
ブロックには、メッセージ番号(m_iMsgNo)を設定する。
最初のブロックは、0,1ビットが10。最後のブロックは、0,1ビットが01。残りのビットは全ブロックで共通。
次の送信でメッセージ番号はincrementする。
m_iCountに追加したブロック数を加える。
m_pSndQueue->m_pSndUList->update(this, false);//送信キューにコネクション追加
queue.cpp
void CSndUList::update(const CUDT* u, const bool& reschedule)
insert_(1, u);
void CSndUList::update(const CUDT* u, const bool& reschedule)
送信リストの最終位置更新(m_iLastEntry)
送信リストにCUDT登録
既に登録があったときの処理不明(時刻判定して1/2位置との交換。ソート??)
最初の送信のときは、送信スレッドのwaitを解除
送信は、UDT::connectで作成したスレッドで送信。
UDT::recv
UDT::close
サーバ
UDT::bind
UDT::listen
UDT::accept
伝送フォーマット
HandShake
ヘッダ部
0 1 2 3 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ |1| Type(0) | Reserved | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | Additional Info | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | Time Stamp | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | Destination Socket ID | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
データ部
struct CHandShake {
int32_t m_iVersion; // UDT version
int32_t m_iType; // UDT socket type
int32_t m_iISN; // random initial sequence number
int32_t m_iMSS; // maximum segment size
int32_t m_iFlightFlagSize; // flow control window size
int32_t m_iReqType; // connection request type: 1: regular connection request, 0: rendezvous connection request, -1/-2: response
int32_t m_iID; // socket ID
int32_t m_iCookie; // cookie
uint32_t m_piPeerIP[4]; // The IP address that the peer's UDP port is bound to
};
ACK
ヘッダ部
0 1 2 3 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ |1| Type(2) | Reserved | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | ACK packet seq. no. | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | Destination Socket ID | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
データ部(int32_t) data[0] = m_iRcvLastAck;//受信データのシーケンス番号 data[1] = m_iRTT;//ラウンドトリップタイム(μsec) data[2] = m_iRTTVar;// RTT variance(μsec) data[3] = m_pRcvBuffer->getAvailBufSize();//advertised flow window size (packets) data[4] = m_pRcvTimeWindow->getPktRcvSpeed();//link capacity (packets per second) data[5] = m_pRcvTimeWindow->getBandwidth();
m_pRcvTimeWindow->getPktRcvSpeed()の内容(CPktTimeWindow::getPktRcvSpeed()参照) 16個のデータパケット受信間隔からメディアンフィルタを通してppsを求める ・16個の中間の2個の平均を求める ・求めた値の8倍を最大値、1/8を最小値とする ・最大値、最小値の間にあるデータの平均、個数を求める ・個数が8個以上なら求めた値の逆数をppsとする。
m_pRcvTimeWindow->getBandwidth()の内容(CPktTimeWindow::getBandwidth()参照) m_pRcvTimeWindow->getPktRcvSpeed()と同様。 16個のパケットペア(16個おきに発生)間の受信間隔からメディアンフィルタを通じて求める。 bpsではなくてpps。
NAK
ヘッダ部
0 1 2 3 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ |1| Type(3) | Reserved | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | Destination Socket ID | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
データ部(int32_t)
CSeqNo::incseq(m_iRcvCurrSeqNo) | 0x80000000; 前回受信シーケンス番号 損失パケット2個以上のとき
CSeqNo::decseq(packet.m_iSeqNo); 受信シーケンス番号
データ
ヘッダ部
0 1 2 3
0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|0| Sequence Number |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|ff |o| Message Number |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Time Stamp |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Destination Socket ID |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
Sequence Number:パケット単位でincrement
Message Number:UDT:send()単位でincrement
bit ff:
11: solo message packet
10: first packet of a message
01: last packet of a message
bit o:
0: in order delivery not required // in order deliveryとは?
1: in order delivery required
データ部 送信データ(最大サイズ MSS 約 1500バイト)をバイナリで羅列。
アクセス数 -
