quic sever端的处理逻辑分析

 博客[1]的分析很好。[2]的代码可以作为测试,免去下载所有chromium代码的麻烦。
 本文主要在其基础上分析server端的处理逻辑。
 server进行端口监听,并向epoll_server上注册需要监听的socket描述符。

//https://cs.chromium.org/chromium/src/net/third_party/quic/tools/quic_server.cc?l=110
bool QuicServer::CreateUDPSocketAndListen(const QuicSocketAddress& address) {
  epoll_server_.RegisterFD(fd_, this, kEpollFlags);
  dispatcher_.reset(CreateQuicDispatcher());
  dispatcher_->InitializeWithWriter(CreateWriter(fd_));
}
//QuicServer::Run() 不断询问是否有IO事件,若有,epoll_server会回调OnEvent函数
void QuicServer::Run() {
  WaitForEvents();
  base::ThreadTaskRunnerHandle::Get()->PostTask(
      FROM_HERE, base::BindOnce(&QuicServer::Run, weak_factory_.GetWeakPtr()));
}
void QuicServer::OnEvent(int fd, net::EpollEvent* event) {
  if (event->in_events & EPOLLIN) {
    QUIC_DVLOG(1) << "EPOLLIN";

    dispatcher_->ProcessBufferedChlos(kNumSessionsToCreatePerSocketEvent);

    bool more_to_read = true;
    while (more_to_read) {
    //packet_reader_负责数据包的读取与分发,数据读取完毕后,回调dispatcher_
    //中ProcessPacket函数
      more_to_read = packet_reader_->ReadAndDispatchPackets(
          fd_, port_, QuicEpollClock(&epoll_server_), dispatcher_.get(),
          overflow_supported_ ? &packets_dropped_ : nullptr);
    }
 }
}

 在新版本里,新的session在ProcessBufferedChlos被创建,session创建的同时也创建了connection这个对象。以 QuicSimpleDispatcher为例,它继承了QuicDispatcher。

QuicServerSessionBase* QuicSimpleDispatcher::CreateQuicSession(
    QuicConnectionId connection_id,
    const QuicSocketAddress& client_address,
    QuicStringPiece /*alpn*/) {
  // The QuicServerSessionBase takes ownership of |connection| below.
  QuicConnection* connection = new QuicConnection(
      connection_id, client_address, helper(), alarm_factory(),
      CreatePerConnectionWriter(),
      /* owns_writer= */ true, Perspective::IS_SERVER, GetSupportedVersions());

  QuicServerSessionBase* session = new QuicSimpleServerSession(
      config(), connection, this, session_helper(), crypto_config(),
      compressed_certs_cache(), quic_simple_server_backend_);
  session->Initialize();
  return session;
}

 分析dispatcher中的处理逻辑。之后 framer_的处理逻辑请参考博文[1]。

//https://cs.chromium.org/chromium/src/net/third_party/quic/core/quic_dispatcher.cc?g=0&l=291
void QuicDispatcher::ProcessPacket(const QuicSocketAddress& self_address,
                                   const QuicSocketAddress& peer_address,
                                   const QuicReceivedPacket& packet) {
  current_self_address_ = self_address;
  current_peer_address_ = peer_address;
  // GetClientAddress must be called after current_peer_address_ is set.
  current_client_address_ = GetClientAddress();
  current_packet_ = &packet;
  // ProcessPacket will cause the packet to be dispatched in
  // OnUnauthenticatedPublicHeader, or sent to the time wait list manager
  // in OnUnauthenticatedHeader.
  framer_.ProcessPacket(packet);
  // TODO(rjshade): Return a status describing if/why a packet was dropped,
  //                and log somehow.  Maybe expose as a varz.
  // TODO(wub): Consider invalidate the current_* variables so processing of the
  //            next packet does not use them incorrectly.
}

 dispatch根据数据包中的connection_id信息,查找相应的session,调用session类中的ProcessUdpPacket函数。

void QuicSession::ProcessUdpPacket(const QuicSocketAddress& self_address,
                                   const QuicSocketAddress& peer_address,
                                   const QuicReceivedPacket& packet) {
   //connection_实在QuicSimpleDispatcher中随着session创建的
  connection_->ProcessUdpPacket(self_address, peer_address, packet);
}
//https://cs.chromium.org/chromium/src/net/third_party/quic/core/quic_connection.cc?type=cs&g=0&l=1594
void QuicConnection::ProcessUdpPacket(const QuicSocketAddress& self_address,
                                      const QuicSocketAddress& peer_address,
                                      const QuicReceivedPacket& packet) {
  //将数据包交给QuicFramer处理
 if (!framer_.ProcessPacket(packet)){}
                                      }
bool QuicFramer::ProcessPacket(const QuicEncryptedPacket& packet) {
rv = ProcessDataPacket(&reader, &header, packet, buffer, kMaxPacketSize);
}
bool QuicFramer::ProcessDataPacket(QuicDataReader* encrypted_reader,
                                   QuicPacketHeader* header,
                                   const QuicEncryptedPacket& packet,
                                   char* decrypted_buffer,
                                   size_t buffer_length)
{
 if (!ProcessFrameData(&reader, *header)) {}
}
bool QuicFramer::ProcessFrameData(QuicDataReader* reader,
                                  const QuicPacketHeader& header) {
//这里只是对数据进行了一些简单的读取,主要的处理逻辑在visitor_->OnStreamFrame中
if (!ProcessStreamFrame(reader, frame_type, &frame)) {
          return RaiseError(QUIC_INVALID_STREAM_DATA);
     }
//此处的visitor_在QuicConnection中注册framer_.set_visitor(this);
if (!visitor_->OnStreamFrame(frame)){}
}
bool QuicFramer::ProcessStreamFrame(QuicDataReader* reader,
                                    uint8_t frame_type,
                                    QuicStreamFrame* frame){
  frame->data_buffer = data.data();
  frame->data_length = static_cast(data.length());
}

visitor_->OnStreamFrame调用的就是QuicConnection中的OnStreamFrame函数。

bool QuicConnection::OnStreamFrame(const QuicStreamFrame& frame)
{
visitor_->OnStreamFrame(frame);
}

 此处的visitor_创建QuicConnection时被注册。因此需要分析QuicSession中的OnStreamFrame函数。到这里,处理逻辑只是负责读取了数据包,没有考虑数据包的乱序情况。connection_中就需要处理数据包乱序的情况,当数据包有序的时候,负责通知上层处理。

void QuicSession::Initialize() {
  connection_->set_visitor(this);
  connection_->SetSessionNotifier(this);
  connection_->SetDataProducer(this);
  connection_->SetFromConfig(config_);

  DCHECK_EQ(kCryptoStreamId, GetMutableCryptoStream()->id());
  static_stream_map_[kCryptoStreamId] = GetMutableCryptoStream();
}
void QuicSession::OnStreamFrame(const QuicStreamFrame& frame)
{
//这里体现quic的多流复用的处理逻辑,就是同一个连接上,可以有多个数据流
QuicStreamId stream_id = frame.stream_id;
QuicStream* stream = GetOrCreateStream(stream_id);
stream->OnStreamFrame(frame);
}
void QuicStream::OnStreamFrame(const QuicStreamFrame& frame) {
//考虑数据包的丢包,乱序,这里需要一个缓冲区,可以等效理解为tcp中recv buffer
sequencer_.OnStreamFrame(frame); 
}
void QuicStreamSequencer::OnStreamFrame(const QuicStreamFrame& frame)
{
stream_->OnDataAvailable();//有数据可以读,比如之前读取了数据包1,2,3,若5先到,则不可读,数据包4到达后,可以一块读取数据包4,5.
}

 所以说,要使用quic传输数据,首先需要继承QuicSession,定义出满足自己业务需求的session。
 接下来记录下,QuicStream的创建过程。

QuicStream* QuicSession::GetOrCreateStream(const QuicStreamId stream_id) {
  StaticStreamMap::iterator it = static_stream_map_.find(stream_id);
  if (it != static_stream_map_.end()) {
    return it->second;
  }
  //map中没有查到,需要创建新的QuicStream
  return GetOrCreateDynamicStream(stream_id);
}
QuicStream* QuicSession::GetOrCreateDynamicStream(
    const QuicStreamId stream_id) {
    return CreateIncomingDynamicStream(stream_id);
    }
    //CreateIncomingDynamicStream 虚函数,由继承了QuicStream的子类实现。

[1]How to Write a QUIC Endpoint Program

你可能感兴趣的:(QUIC)