Chromium源码解读--客户端和服务端实现

本文详细解析了QUIC客户端的初始化、连接过程、数据发送与接收,以及服务器端如何处理CHLO和CHLO包。涉及类QuicParseCommandLineFlags、QuicSimpleClientFactory、QuicConfig等核心组件,以及关键函数如CreateClient、Connect和ProcessPacket的实现。

参考文献

[1] 参考博客一
[2] 参考博客二
[3] QUIC源码学习–该文章比较详细

客户端处理逻辑和实现

在这里插入图片描述

1. 单步调试过程中遇到的类

2.1 QuicParseCommandLineFlags

Quic Parse命令行标志类,主要是解析命令行输入的指令

2.2 QuicSimpleClientFactory

一个工厂模式类,作用是基于简单工厂模式创建基于QUIC协议的Client。他的基类的是ClientFactory。ClientFactory没有父类。简单工厂模式的实现可以参考上述博客。

class QuicSimpleClientFactory : public quic::QuicToyClient::ClientFactory

2.3 ClientFactory

该类主要包含一个关键的成员函数

  • CreateClient():创建客户端

2.4 QuicToyClient

  • 该类中实现了类ClientFactory
  • 一个关键成员函数: SendRequestsAndPrintResponses()。作用是发送请求并打印响应

该类中包含一个私有变

  • ClientFactory* client_factory_;其就是创建一个生产Client的工厂

2.5 QuicUrl

这是一个包装GURL的实用程序类。不是很明白其作用。

2.6 GetQuicFlags

这应该是一个Quic相关参数的解析函数,但是不是很明白是干什么的。

2.7 ParsedQuicVersionVector

该类主要是获取当前支持的QUIC协议版本,并创建一个vector进行存储。

2.8 ProofVerifier

数字签名认证

2.9 QuicCofing

获取配置文件并初始化一个配置变量。QuicConfig包含在加密握手中协商的非加密配置选项。
QuicConfig在实例时会调用SetDefaults()函数。SetDfaults()函数就是设置一一些初始化的变量。

void QuicConfig::SetDefaults() {
  SetIdleNetworkTimeout(QuicTime::Delta::FromSeconds(kMaximumIdleTimeoutSecs));
  SetMaxBidirectionalStreamsToSend(kDefaultMaxStreamsPerConnection);
  SetMaxUnidirectionalStreamsToSend(kDefaultMaxStreamsPerConnection);
  max_time_before_crypto_handshake_ =
      QuicTime::Delta::FromSeconds(kMaxTimeForCryptoHandshakeSecs);
  max_idle_time_before_crypto_handshake_ =
      QuicTime::Delta::FromSeconds(kInitialIdleTimeoutSecs);
  max_undecryptable_packets_ = kDefaultMaxUndecryptablePackets;

  SetInitialStreamFlowControlWindowToSend(kMinimumFlowControlSendWindow);
  SetInitialSessionFlowControlWindowToSend(kMinimumFlowControlSendWindow);
  SetMaxAckDelayToSendMs(kDefaultDelayedAckTimeMs);
  SetAckDelayExponentToSend(kDefaultAckDelayExponent);
  SetMaxPacketSizeToSend(kMaxIncomingPacketSize);
  SetMaxDatagramFrameSizeToSend(kMaxAcceptedDatagramFrameSize);
}

2.10 AddressList

2.11 SynchronousHostResolver

同步解析主机名

2.12 QuicIpAdress

从给定主机名确定要连接的IP地址

2.13 QuicServerId

用于标识会话的id。包括主机名、端口、方案和保密模式。

// QuicServerId的构造函数
QuicServerId::QuicServerId(const std::string& host,
                           uint16_t port,
                           bool privacy_mode_enabled)
    : host_(host), port_(port), privacy_mode_enabled_(privacy_mode_enabled) {}

2.13 QuicSoketAddress

quic的socket address

3. 重要的函数

3.1 CreateClient函数

该函数是简单工厂类(QuicSimpleClientFactory)中的一个函数,主要是为了创建一个客户端使实体。

3.2 Initialize函数

  • QuicClient派生于QuicSpdyClientBase
  • QuicSpdyClientBase 派生于 QuicClientBase, QuicClientPushPromiseIndex::Delegate, QuicSpdyStream::Visitor

调用方式:client->Initialize()
这个是QuicClienbase中的一个成员函数,该函数的作用如下:

  • 初始化,接收窗口session 15MB, stream 6MB; 并且初始化network_helper_,网络事件机制;
  • 在StartConnect 或者 Connect之前调用
// Initialize()函数的实现
bool QuicClientBase::Initialize() {
  //......
  // If an initial flow control window has not explicitly been set, then use the
  // same values that Chrome uses.
  const uint32_t kSessionMaxRecvWindowSize = 15 * 1024 * 1024;  // 15 MB
  const uint32_t kStreamMaxRecvWindowSize = 6 * 1024 * 1024;    //  6 MB

  //.....

  if (!network_helper_->CreateUDPSocketAndBind(server_address_,
                                               bind_to_address_, local_port_)) {
    return false;
  }

  initialized_ = true;
  return true;
}

3.3 Connect()函数

bool Connect() :连接quic server,包括同步加密密钥,和handshake。会调用StartConnect()

3.4 StartConnect()函数

void QuicClientBase::StartConnect(); :连接quic server,包括 同步加密密钥,和handshake;

  • 创建QuicPacketWriter;
  • 创建QuicSession;
  • 创建QuicConnection(其最为QuecSession的成员对象);
void QuicClientBase::StartConnect() {
  .......
  QuicPacketWriter* writer = network_helper_->CreateQuicPacketWriter();//创建writer接口
  ......
  session_ = CreateQuicClientSession(
      supported_versions(),
      new QuicConnection(GetNextConnectionId(), server_address(), helper(),
                         alarm_factory(), writer,
                         /* owns_writer= */ false, Perspective::IS_CLIENT,
                         can_reconnect_with_different_version
                             ? ParsedQuicVersionVector{mutual_version}
                             : supported_versions()));
  // Reset |writer()| after |session()| so that the old writer outlives the old
  // session.
  set_writer(writer);
  InitializeSession();
  set_connected_or_attempting_connect(true);
}

4. 客户端建立连接的过程

(1) 首先基于简单工程模式实例化一个客户端工程后调用SendRequestsAndPrintResponses()函数。

  QuicSimpleClientFactory factory;
  quic::QuicToyClient client(&factory);
  return client.SendRequestsAndPrintResponses(urls);

(2) 创建具体的“客户端产品”

std::unique_ptr<QuicSpdyClientBase> client = client_factory_->CreateClient()

(3) 客户端的初始化

  • 设置接受和发送缓冲区大小;
  • 基于UDP创建socket并绑定服务端的IP和Port,调用函数CreateUDPSocketAndBind();
  • 添加QuicChromiumPacketReader类
bool QuicClientBase::Initialize() {
   
   

  ...
  
  // If an initial flow control window has not explicitly been set, then use the
  // same values that Chrome uses.
  const uint32_t kSessionMaxRecvWindowSize = 15 * 1024 * 1024;  // 15 MB
  const uint32_t kStreamMaxRecvWindowSize = 6 * 1024 * 1024;    //  6 MB

  ...

  if (!CreateUDPSocketAndBind(server_address_, bind_to_address_, local_port_)) {
   
   
    return false;
  }

  initialized_ = true;
  return true;
}


bool QuicSimpleClient::CreateUDPSocketAndBind(IPEndPoint server_address,
                                              IPAddress bind_to_address,
                                              int bind_to_port) {
   
   
  std::unique_ptr<UDPClientSocket> socket(
      new UDPClientSocket(DatagramSocket::DEFAULT_BIND, RandIntCallback(),
                          &net_log_, NetLogSource()));

  int address_family = server_address.GetSockAddrFamily();
  if (bind_to_address.size() != 0) {
   
   
    client_address_ = IPEndPoint(bind_to_address, bind_to_port);
  } else if (address_family == AF_INET) {
   
   
    client_address_ = IPEndPoint(IPAddress::IPv4AllZeros(), bind_to_port);
  } else {
   
   
    client_address_ = IPEndPoint(IPAddress::IPv6AllZeros(), bind_to_port);
  }
  // 基于操作系统提供的UDP套接字连接服务器
  int rc = socket->Connect(server_address);
  if (rc != OK) {
   
   
    LOG(ERROR) << "Connect failed: " << ErrorToShortString(rc);
    return false;
  }

  // 设置接受缓冲区大小
  rc = socket->SetReceiveBufferSize(kDefaultSocketReceiveBuffer);
  if (rc != OK) {
   
   
    LOG(ERROR) << "SetReceiveBufferSize() failed: " << ErrorToShortString(rc);
    return false;
  }
  // 设置发送缓冲区大小
  rc = socket->SetSendBufferSize(kDefaultSocketReceiveBuffer);
  if (rc != OK) {
   
   
    LOG(ERROR) << "SetSendBufferSize() failed: " << ErrorToShortString(rc);
    return false;
  }

  rc = socket->GetLocalAddress(&client_address_);
  if (rc != OK) {
   
   
    LOG(ERROR) << "GetLocalAddress failed: " << ErrorToShortString(rc);
    return false;
  }

  socket_.swap(socket);

  // std::unique_ptr<QuicChromiumPacketReader> packet_reader_
  packet_reader_.reset(new QuicChromiumPacketReader(</
1. 资源内容概览 本资源基于官方Chromium源码编译的CEF(Chromium Embedded Framework) 项目,并扩展了FFmpeg对多种视频和音频格式的解码与播放支持。 包含编译后的二进制文件、库文件以及必要的开发头文件,方便用户快速集成到项目中。 提供基础的编译配置修改说明和集成示例。 2. 核心功能特点 增强的媒体格式支持:在CEF标准版基础上,额外支持了主流的H.264、MPEG-4、MP3、AAC、FLV等视频音频格式的解码。 源码级修改保障:通过修改Chromium源码树中的FFmpeg配置脚本(如 build_ffmpeg.py 和 config.h),确保相关编解码器在编译时被启用。 开箱即用:提供已经编译好的库文件,省去你繁琐的编译配置过程。 3. 适用人群与场景 客户端应用开发者:需要在桌面应用程序(如Electron替代方案、OBS插件)中嵌入浏览器内核并播放多种媒体格式。 CEF二次开发爱好者:希望深入了解Chromium源码结构及CEF框架定制。 多媒体项目开发:致力于开发支持丰富媒体格式的Web内核嵌入式应用。 4. 使用说明与集成指南 资源包内包含Release版本的动态库及链接库。 详细配置步骤请参考资源内的README.md文件。 主要步骤: 将本资源中的libcef.dll、libcef_dll_wrapper等文件放置于你的项目合适目录。 在你的开发环境中正确配置包含目录和库目录。 在代码中初始化CEF时,确保正确设置沙箱等参数。 5. 编译信息 (供参考) 基础源码Chromium (对应CEF分支版本号,branch=7339) 编译环境:Visual Studio 2022 17.13.4, CMake, Windows 10.0.26100.3323 SDK 支持环境:win10+
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值