QUIC實現代碼分析


 QUIC實現代碼分析

  文件介紹

  • quic_connection類文件
    主要編寫QuicConnection類,該類是quic服務端和客戶端的處理框架,它提供SendStreamData方法用來發送流數據,被QuicSession調用。 它使用QuicPacketGenerator來創建Quic幀。 而QuicPacketGenerator會被QuicConnection的OnSerializedPacket方法調用。 最后幀則會被QuicPacketWriter寫入連接中。
  • quic_session類文件
    主要編寫QuicSession類,QuicSession類是一個基礎類,當一個具體會話類被創建時將被繼承。 它主要將傳入的數據發送到正確的Quic流。 擁有QuicConnection類,用於在線上發送數據。 因此它代表一個quic連接,由多個流組成,並抽象出真實的網絡連接。 quic流會被WritevData方法用來發送數據。 反過來QuicConnection類會調用QuicConnectionVisitorInterface方法來通知會話新的數據包和對連接的更改。
  • quic_stream類文件
    主要編寫QuicStream類,它定義了quic流類需要滿足的接口。 它還實現了流的基本邏輯,如流控制,幀排序,處理流連接重置或關閉和緩沖數據寫入,用戶根據stream類來進行數據的拋出和寫入。stream流有QuicDataStream類,其實現了傳輸SPDY請求的quic流。 它要求在會話管理的專用報頭流中發送不同的報頭。 報頭通過OnStreamHeaders, OnStreamHeadersPriority和 OnStreamHeadersComplete發送給它。 初始化時會阻塞QuicStreamSequencer直到所有報頭被接收。QuicStreamSequencer類用來緩沖幀數據直到它們可以傳遞到下一層為止。 其中包括檢查重復幀,排序幀,以便數據有序,並檢查錯誤情況。QuicHeadersStream類用來處理SPDY報頭
  • quic_packet類文件
    主要編寫QuicPacket類,如QuicPacketCreator類是QuicConnection用來創建和發送包的類。 它使用QuicPacketCreator來構建幀和包。 當一個包被創建完畢,它將調用OnSerializedPacket給它的調用者。QuicReceivedPacket類用來處理接收包。QuicPacketWriter類接口定義了通過QuicConnection發送數據包的方法。
    它還定義了一些方法來判斷套接字是否被阻塞。QuicPacketGenerator::DelegateInterface類定義了當新數據包可用時QuicPacketGenerator調用的接口。 它通過QuicConnection實現。
  • quic_framer類文件
    主要編寫QuicFramer類,用來解析和構建QUIC包。 通過ProcessPacket方法來接收數據,並調用QuicFrameVisitorInterface接口來通知QuicConnection接收到新包。QuicFrameVisitorInterface類定義了QuicFrame處理新QUIC數據包的方法,它通過QuicConnection實現。

跟據以下幾種類型來判斷為什么類型的包,來進行相應的處理:

enum QuicFrameType {
  PADDING_FRAME = 0,
  RST_STREAM_FRAME = 1,
  CONNECTION_CLOSE_FRAME = 2,
  GOAWAY_FRAME = 3,
  WINDOW_UPDATE_FRAME = 4,
  BLOCKED_FRAME = 5,
  STOP_WAITING_FRAME = 6,
  PING_FRAME = 7,
  STREAM_FRAME,
  ACK_FRAME,
  MTU_DISCOVERY_FRAME,
  NUM_FRAME_TYPES
};
PADDING_FRAME:為填充字節幀,接收到這個包時會將包剩余部分填充字節。
RST_STREAM_FRAME:當由流的創建者發送時,表示創建者希望關閉流,當由接收者發送時,表示發生錯誤或者不想接收流,因此流應該被關閉。
CONNECTION_CLOSE_FRAME:連接關閉。
GOAWAY_FRAME:表示流應該被停止使用,因為之后將會被關閉,在使用的流將被繼續處理,但是發送者不會在接收流。
WINDOW_UPDATE_FRAME:用於通知對端流量控制端口接收窗口大小的更新。
BLOCKED_FRAME:表示已經准備好發送數據且有數據要發送,但是被阻塞了。
STOP_WAITING_FRAME:通知對端不需要等待包號小於特定值的包。
PING_FRAME:用來驗證對端是否保持活躍,且連接是否正常。
STREAM_FRAME:用於發送數據。
ACK_FRAME:通知對端哪些包被接收到了。

  QUIC服務端客戶端發送數據流程解析

quic實現的C/S端代碼位於proto-quic\src\net\tools\quic
根據quic C/S端代碼,其服務端和客戶端代碼的main()文件為server_bin.cc和client_bin.cc等文件。
client.main()函數基本實現步驟:
創建QuicClient類client,調用client.Initialize()進行初始化。

Initialize()實現: 
{ 
定義流窗口大小 
定義session窗口大小 
設置epoll_server超時時間 
調用CreateUDPSocket()創建UDP套接字 
注冊epoll時間回調函數 
} 

之后調用client.Connect()進行對話的連接

Connect()實現 
{ 
寫數據類PacketWrite類創建 
創建session類 
初始化session,InitializeSession() 
WaitForEvent 
}

調用client.CreateClientStream()與session中創建一個stream類流,用於發送數據。
調用stream->WriteStringPiece()來進行數據的發送。
調用client.WaitForEvents()等待事件。
調用stream->CloseConnection(net::QUIC_NO_ERROR);來關閉連接。
調用client.Disconnect()關閉client。

服務端與client端基本類似。

發送和接收:

發送:

最外層的發送數據接口為調用stream流的WriteOrBufferData(body, fin, nullptr)方法其中body是要發的數據,fin是標識是否是改流的最后一個數據。之后會在流中進行相應的判斷和處理,如流上是否有足夠的空間來發送這個數據,發送窗口大小是否合適,是否阻塞等。如果判斷可以進行發送之后便會調用session類的方法WritevData()。
在session類會調用connection類的SendStreamData方法發送數據,並根據實際發送的數據更新相應stream流的數據消費的數值。
在connection類會調用PacketGenerator類的ConsumeData方法來發送數據。其中會根據包來進行ack的綁定。
之后會返回connection類,根據消息隊列情況調用WritePacket()進行socket上包的寫入,該方法實現於PacketWriter類。

接收:

當Server端創建好之后循環調用StartReading(),進行接收包,根據synchronous_read_count_ 來判斷是否是CHLO包。

void QuicSimpleServer::StartReading() {
  if (synchronous_read_count_ == 0) {
    // Only process buffered packets once per message loop.
    dispatcher_->ProcessBufferedChlos(kNumSessionsToCreatePerSocketEvent);
  }
...
  int result = socket_->RecvFrom(
      read_buffer_.get(), read_buffer_->size(), &client_address_,
      base::Bind(&QuicSimpleServer::OnReadComplete, base::Unretained(this)));
...
OnReadComplete(result);
}

OnReadComplete()中會調用dispatcher的處理包方法

void QuicSimpleServer::OnReadComplete(int result) {
...
  dispatcher_->ProcessPacket(
      QuicSocketAddress(QuicSocketAddressImpl(server_address_)),
      QuicSocketAddress(QuicSocketAddressImpl(client_address_)), packet);

  StartReading();
}
void QuicDispatcher::ProcessPacket(const QuicSocketAddress& server_address,
                                   const QuicSocketAddress& client_address,
                                   const QuicReceivedPacket& packet) {
...
  framer_.ProcessPacket(packet);
...
}

跳轉到Framer類的處理方法

bool QuicFramer::ProcessPacket(const QuicEncryptedPacket& packet) {
...
  if (!visitor_->OnUnauthenticatedPublicHeader(public_header)) {
    // The visitor suppresses further processing of the packet.
    return true;
  }
...
}

visitor_指向dispatch類,跳轉到

QuicDispatcher::OnUnauthenticatedPublicHeader(){
...
  QuicConnectionId connection_id = header.connection_id;
  SessionMap::iterator it = session_map_.find(connection_id);
  if (it != session_map_.end()) {
    DCHECK(!buffered_packets_.HasBufferedPackets(connection_id));
    it->second->ProcessUdpPacket(current_server_address_,
                                 current_client_address_, *current_packet_);
    return false;
  }
...
}

當包頭的connection_id 能在session_map里找到時,直接調用connection的ProcessUdpPacket處理,server端的session_map維護在dispatch類里,創建session類都會記錄下來。

之后經過處理跳轉到Framer類的ProcessFrameData()方法里,其中對stream Framer和ACK Framer分別進行了處理,
如果是stream包,則對其進行解析后會調用OnStreamFrame()拋到上層

if (!ProcessStreamFrame(reader, frame_type, &frame)) {
          return RaiseError(QUIC_INVALID_STREAM_DATA);
        }
        if (!visitor_->OnStreamFrame(frame)) {
          QUIC_DVLOG(1) << ENDPOINT
                        << "Visitor asked to stop further processing.";
          // Returning true since there was no parsing error.
          return true;
        }
}

visitor_在Framer類里,由創建connection類時初始化,指向connection類,在到connection類里調用visitor_->OnStreamFrame(),visitor_指向session類,在由session類拋到stream類的OnDataAvailable()將數據進行處理,注意基礎stream類里沒有實現OnDataAvailable()的方法,需要編寫,下面是官方tools文件里的處理。

OnDataAvailable() {
  while (HasBytesToRead()) {
    struct iovec iov;
    if (GetReadableRegions(&iov, 1) == 0) {
      // No more data to read.
      break;
    }
    QUIC_DVLOG(1) << "Stream " << id() << " processed " << iov.iov_len
                  << " bytes.";
    body_.append(static_cast<char*>(iov.iov_base), iov.iov_len);

    if (content_length_ >= 0 &&
        body_.size() > static_cast<uint64_t>(content_length_)) {
      QUIC_DVLOG(1) << "Body size (" << body_.size() << ") > content length ("
                    << content_length_ << ").";
      SendErrorResponse();
      return;
    }
    MarkConsumed(iov.iov_len);
  }
  if (!sequencer()->IsClosed()) {
    sequencer()->SetUnblocked();
    return;
  }

  // If the sequencer is closed, then all the body, including the fin, has been
  // consumed.
  OnFinRead();

  if (write_side_closed() || fin_buffered()) {
    return;
  }
}

如果是ACK包,則會對ack進行處理,並進行擁塞算法的運算。

    if (!ProcessAckFrame(reader, frame_type, &frame)) {
      return RaiseError(QUIC_INVALID_ACK_DATA);
    }
    if (!visitor_->OnAckFrame(frame)) {
      QUIC_DVLOG(1) << ENDPOINT
                    << "Visitor asked to stop further processing.";
      // Returning true since there was no parsing error.
      return true;
    }

OnAckFrame()跳轉到connection類的OnAckFrame,其中調用QuicSentPacketManager類OnIncomingAck()方法,其中進行了rtt和帶寬的更新,並對丟包進行判斷。

(gdb) bt
#0  posix_quic::QuicConnectionVisitor::OnAckFrame (this=0x83b238, frame=...) at /root/posix_quic/src/connection_visitor.cpp:109
#1  0x000000000043331c in net::QuicConnection::OnAckFrame(net::QuicAckFrame const&) ()
#2  0x000000000043fd08 in net::QuicFramer::ProcessFrameData(net::QuicDataReader*, net::QuicPacketHeader const&) [clone .part.162] [clone .constprop.172] ()
#3  0x0000000000440258 in net::QuicFramer::ProcessDataPacket(net::QuicDataReader*, net::QuicPacketHeader*, net::QuicEncryptedPacket const&, char*, unsigned long) [clone .part.163] [clone .constprop.166] ()
#4  0x0000000000440684 in net::QuicFramer::ProcessPacket(net::QuicEncryptedPacket const&) ()
#5  0x0000000000434958 in net::QuicConnection::ProcessUdpPacket(net::QuicSocketAddress const&, net::QuicSocketAddress const&, net::QuicReceivedPacket const&) ()
#6  0x00000000004171f8 in posix_quic::QuicSocketEntry::ProcessUdpPacket (this=this@entry=0x83b080, self_address=..., peer_address=..., packet=...) at /root/posix_quic/src/socket_entry.cpp:415
#7  0x0000000000406ff0 in posix_quic::QuicEpollerEntry::Wait (this=this@entry=0x837950, events=0x461790 <net::QuicSocketAddressImpl::QuicSocketAddressImpl(sockaddr_storage const&)+48>, events@entry=0xffffffff9128, maxevents=65535, 
    maxevents@entry=1024, timeout=timeout@entry=6000) at /root/posix_quic/src/epoller_entry.cpp:376
#8  0x00000000004157cc in posix_quic::QuicEpollWait (epfd=3, epfd@entry=0, events=events@entry=0xffffffff9128, maxevents=maxevents@entry=1024, timeout=timeout@entry=6000) at /root/posix_quic/src/quic_socket.cpp:494
#9  0x0000000000401dfc in doLoop (ep=0, ep@entry=3) at /root/posix_quic/test/client/src/client.cpp:37
#10 0x00000000004009c8 in main () at /root/posix_quic/test/client/src/client.cpp:143
(gdb) 

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM