cronet 网络编程

📅 2026/7/1 5:07:47
cronet 网络编程
正式开搞前先简单看下 cronet 网络编程范式与之前有何不同#include string #include thread #include iostream #include cronet/cronet_c.h // 回调重定向 void on_redirect_received(Cronet_UrlRequestCallback* callback, Cronet_UrlRequest* request, Cronet_UrlResponseInfo* info, const char* new_location) { std::cout Redirect to: new_location std::endl; Cronet_UrlRequest_FollowRedirect(request); } // 回调响应开始 void on_response_started(Cronet_UrlRequestCallback* callback, Cronet_UrlRequest* request, Cronet_UrlResponseInfo* info) { std::cout Response started std::endl; Cronet_Buffer* buffer Cronet_Buffer_Create(); Cronet_Buffer_InitWithAlloc(buffer, 4096); // 4KB缓冲区 Cronet_UrlRequest_Read(request, buffer); } // 回调读取完成 void on_read_completed(Cronet_UrlRequestCallback* callback, Cronet_UrlRequest* request, Cronet_UrlResponseInfo* info, Cronet_Buffer* buffer, uint64_t bytes_read) { // 处理数据 if (bytes_read 0) { const char* data static_castconst char*(Cronet_Buffer_GetData(buffer)); std::cout Read bytes_read bytes std::endl; std::cout data std::endl; } // 释放当前buffer Cronet_Buffer_Destroy(buffer); // 继续读取如果还有数据且未完成 if (bytes_read 0) { Cronet_Buffer* new_buffer Cronet_Buffer_Create(); Cronet_Buffer_InitWithAlloc(new_buffer, 4096); Cronet_UrlRequest_Read(request, new_buffer); } else { std::cout Read completed std::endl; } } // 回调请求成功 void on_succeeded(Cronet_UrlRequestCallback* callback, Cronet_UrlRequest* request, Cronet_UrlResponseInfo* info) { std::cout Request succeeded std::endl; } // 回调请求失败 void on_failed(Cronet_UrlRequestCallback* callback, Cronet_UrlRequest* request, Cronet_UrlResponseInfo* info, Cronet_Error* error) { std::cout Request failed std::endl; } // 回调取消 void on_canceled(Cronet_UrlRequestCallback* callback, Cronet_UrlRequest* request, Cronet_UrlResponseInfo* info) { std::cout Request cancelled std::endl; } // Executor void executor_func(Cronet_Executor *executor, Cronet_Runnable *runnable) { Cronet_Runnable_Run(runnable); } int main() { // 1. 创建引擎 Cronet_EnginePtr engine Cronet_Engine_Create(); Cronet_EngineParamsPtr params Cronet_EngineParams_Create(); Cronet_Engine_StartWithParams(engine, params); // 3. 创建回调 Cronet_UrlRequestCallbackPtr callback Cronet_UrlRequestCallback_CreateWith( on_redirect_received, // 重定向回调 on_response_started, on_read_completed, on_succeeded, on_failed, on_canceled ); // 4. 配置请求 Cronet_UrlRequestParamsPtr req_params Cronet_UrlRequestParams_Create(); Cronet_UrlRequestParams_http_method_set(req_params, GET); // 添加请求头 Cronet_HttpHeaderPtr header Cronet_HttpHeader_Create(); Cronet_HttpHeader_name_set(header, User-Agent); Cronet_HttpHeader_value_set(header, Cronet-C-Client); Cronet_UrlRequestParams_request_headers_add(req_params, header); // 5. 创建执行器 Cronet_ExecutorPtr executor Cronet_Executor_CreateWith(executor_func); // 6. 创建并启动请求 Cronet_UrlRequestPtr request Cronet_UrlRequest_Create(); Cronet_UrlRequest_InitWithParams(request, engine, http://httpbin.org/get, req_params, callback, executor); Cronet_UrlRequest_Start(request); // 7. 等待请求完成 std::this_thread::sleep_for(std::chrono::seconds(15)); // 8. 清理资源 Cronet_UrlRequest_Destroy(request); Cronet_HttpHeader_Destroy(header); Cronet_UrlRequestParams_Destroy(req_params); Cronet_Executor_Destroy(executor); Cronet_UrlRequestCallback_Destroy(callback); Cronet_EngineParams_Destroy(params); Cronet_Engine_Destroy(engine); return 0; }上面是 deepseek 生成的 cronet 基于 C 语言的示例运行后有以下输出$ ./cronet_conn_stat Response started Read 279 bytes { args: {}, headers: { Accept-Encoding: gzip, deflate, Host: httpbin.org, User-Agent: Cronet-C-Client, X-Amzn-Trace-Id: Root1-69425f84-67dbe33a06e303cf4c611b72 }, origin: 111.108.111.133, url: http://httpbin.org/get } Request succeeded与 libcurl 相比Cronet_UrlRequest_Start 类似 curl_easy_perform 的角色但变为异步执行它会立即返回之后通过回调不断通知连接上的事件因此示例中是通过 sleep 15 秒来阻塞主线程的工程实践中这个完全可以和消息、事件循环集成在一起从而提高线程并发能力与 boost 相比 (特别是基于 boost::asio::ip::tcp 版本的实现)完全不需要主动 async_resolve、async_connect、async_handeshake 以及 async_write只需要在 on_read_completed 回调中无脑 Cronet_UrlRequest_Read 即可底层过程 cronet 都帮你包办了达到节省心智负担的目的。不过这也带来一个问题就是之前可以手动打桩计算的各种耗时现在都看不到了最多能获取个首分片耗时和总请求耗时其中首分片这还包含了解析、连接、ssl 握手时长的耗时相对失真了都。cronet 对链接性能信息的支持cronet 其实也有接口统计链接层的一些信息主要通过下面的接口获取/////////////////////// // Struct Cronet_Metrics. CRONET_EXPORT Cronet_MetricsPtr Cronet_Metrics_Create(void); CRONET_EXPORT void Cronet_Metrics_Destroy(Cronet_MetricsPtr self); ... // Cronet_Metrics getters. CRONET_EXPORT Cronet_DateTimePtr Cronet_Metrics_request_start_get( const Cronet_MetricsPtr self); CRONET_EXPORT Cronet_DateTimePtr Cronet_Metrics_dns_start_get(const Cronet_MetricsPtr self); CRONET_EXPORT Cronet_DateTimePtr Cronet_Metrics_dns_end_get(const Cronet_MetricsPtr self); CRONET_EXPORT Cronet_DateTimePtr Cronet_Metrics_connect_start_get( const Cronet_MetricsPtr self); CRONET_EXPORT Cronet_DateTimePtr Cronet_Metrics_connect_end_get(const Cronet_MetricsPtr self); CRONET_EXPORT Cronet_DateTimePtr Cronet_Metrics_ssl_start_get(const Cronet_MetricsPtr self); CRONET_EXPORT Cronet_DateTimePtr Cronet_Metrics_ssl_end_get(const Cronet_MetricsPtr self); CRONET_EXPORT Cronet_DateTimePtr Cronet_Metrics_sending_start_get( const Cronet_MetricsPtr self); CRONET_EXPORT Cronet_DateTimePtr Cronet_Metrics_sending_end_get(const Cronet_MetricsPtr self); CRONET_EXPORT Cronet_DateTimePtr Cronet_Metrics_push_start_get(const Cronet_MetricsPtr self); CRONET_EXPORT Cronet_DateTimePtr Cronet_Metrics_push_end_get(const Cronet_MetricsPtr self); CRONET_EXPORT Cronet_DateTimePtr Cronet_Metrics_response_start_get( const Cronet_MetricsPtr self); CRONET_EXPORT Cronet_DateTimePtr Cronet_Metrics_request_end_get(const Cronet_MetricsPtr self); CRONET_EXPORT bool Cronet_Metrics_socket_reused_get(const Cronet_MetricsPtr self); CRONET_EXPORT int64_t Cronet_Metrics_sent_byte_count_get(const Cronet_MetricsPtr self); CRONET_EXPORT int64_t Cronet_Metrics_received_byte_count_get(const Cronet_MetricsPtr self);主要是通过 Cronet_Metrics_xxx 的接口获取所需的 dns、connect、ssl、request 耗时都有耗时是通过接口返回的两个时间做差值得到的举例来说Cronet_DateTimePtr start Cronet_Metrics_connect_start(metrics); Cronet_DateTimePtr end Cronet_Metrics_connect_end(metrics); if (start end) { int64_t start_ms Cronet_DateTime_value_get(start); int64_t end_ms Cronet_DateTime_value_get(end); int64_t connect (start_ms 0 end_ms 0) ? (end_ms - start_ms) : 0; // printf(connect elapse %lld\n, connect); }注意返回的 Cronet_DateTime 对象到毫秒值还需要调用一个接口莫法子C 语言的接口就是这么废柴~现在的关键落到了如何获取 Cronet_Metrics 对象发现只有一个接口可以// Cronet_RequestFinishedInfo getters. CRONET_EXPORT Cronet_MetricsPtr Cronet_RequestFinishedInfo_metrics_get( const Cronet_RequestFinishedInfoPtr self);需要输入 Cronet_RequestFinishedInfo 对象这又是个什么东东经过一番搜索发现唯一途径是通过一个回调// The app implements abstract interface Cronet_RequestFinishedInfoListener by // defining custom functions for each method. typedef void (*Cronet_RequestFinishedInfoListener_OnRequestFinishedFunc)( Cronet_RequestFinishedInfoListenerPtr self, Cronet_RequestFinishedInfoPtr request_info, Cronet_UrlResponseInfoPtr response_info, Cronet_ErrorPtr error);这个回调又是经由 Cronet_RequestFinishedInfoListener 对象设置的/////////////////////// // Abstract interface Cronet_RequestFinishedInfoListener is implemented by the // app. // There is no method to create a concrete implementation. // Destroy an instance of Cronet_RequestFinishedInfoListener. CRONET_EXPORT void Cronet_RequestFinishedInfoListener_Destroy( Cronet_RequestFinishedInfoListenerPtr self); // Set and get app-specific Cronet_ClientContext. ... // The app implements abstract interface Cronet_RequestFinishedInfoListener by // defining custom functions for each method. typedef void (*Cronet_RequestFinishedInfoListener_OnRequestFinishedFunc)( Cronet_RequestFinishedInfoListenerPtr self, Cronet_RequestFinishedInfoPtr request_info, Cronet_UrlResponseInfoPtr response_info, Cronet_ErrorPtr error); // The app creates an instance of Cronet_RequestFinishedInfoListener by // providing custom functions for each method. CRONET_EXPORT Cronet_RequestFinishedInfoListenerPtr Cronet_RequestFinishedInfoListener_CreateWith( Cronet_RequestFinishedInfoListener_OnRequestFinishedFunc OnRequestFinishedFunc);看这个 CreateWith 接口它的唯一参数就是上面声明的用户回调。这一系列接口其实是创建了一个侦听器之后还需要关联到引擎才能生效void on_request_finished_listener( Cronet_RequestFinishedInfoListenerPtr self, Cronet_RequestFinishedInfoPtr request_info, Cronet_UrlResponseInfoPtr response_info, Cronet_ErrorPtr error) { } ... int main() { // 1. 创建引擎 Cronet_EnginePtr engine Cronet_Engine_Create(); Cronet_EngineParamsPtr params Cronet_EngineParams_Create(); Cronet_Engine_StartWithParams(engine, params); ... // 5. 创建执行器 Cronet_ExecutorPtr executor Cronet_Executor_CreateWith(executor_func); ... Cronet_RequestFinishedInfoListenerPtr listener Cronet_RequestFinishedInfoListener_CreateWith(on_request_finished_listener); if (listener) { Cronet_Engine_AddRequestFinishedListener(engine, listener, executor); std::cout request finished listener registered std::endl; } else { std::cout setup request finished listener failed, no connection statistic provided std::endl; } ... // 8. 清理资源 Cronet_UrlRequest_Destroy(request); Cronet_HttpHeader_Destroy(header); Cronet_UrlRequestParams_Destroy(req_params); if (listener) { Cronet_Engine_RemoveRequestFinishedListener(engine, listener); Cronet_RequestFinishedInfoListener_Destroy(listener); } Cronet_Executor_Destroy(executor); Cronet_UrlRequestCallback_Destroy(callback); Cronet_EngineParams_Destroy(params); Cronet_Engine_Destroy(engine); return 0; }关联侦听器的接口如下CRONET_EXPORT void Cronet_Engine_AddRequestFinishedListener( Cronet_EnginePtr self, Cronet_RequestFinishedInfoListenerPtr listener, Cronet_ExecutorPtr executor); CRONET_EXPORT void Cronet_Engine_RemoveRequestFinishedListener( Cronet_EnginePtr self, Cronet_RequestFinishedInfoListenerPtr listener);这样整个流程就串起来了在 Cronet_Engine 初始化时创建并关联一个 Cronet_RequestFinishedInfoListener 对象该对象持有一个 Cronet_RequestFinishedInfoListener_OnRequestFinished 类型的用户回调当连接结束时 cronet 会将性能信息通过该回调通知到用户用户通过回调的第二个参数request_info 获取链接性能信息即 Cronet_RequestFinishedInfo - Cronet_Metrics再通过后者的一系列接口获取感兴趣的信息。从整个流程可以看出* 性能信息只有在连接关闭时才能获取到* 性能信息并不是关联到单链接 (Cronet_UrlRequest)而是关联到全局 (Cronet_Engine)* 可以关联多个 Listener 对象但感觉没什么必要性能信息投递回到业务层面每个下载任务包含若干链接在任务结束时 (成功、失败或取消) 对链接信息进行上报平时这些信息是由链接对象管理的因此需要将位于全局回调的性能信息进行投递。用户定义的链接对象一般是关联到 Cronet_UrlRequest即通过下面的接口/////////////////////// // Concrete interface Cronet_UrlRequest. CRONET_EXPORT void Cronet_UrlRequest_SetClientContext( Cronet_UrlRequestPtr self, Cronet_ClientContext client_context); CRONET_EXPORT Cronet_ClientContext Cronet_UrlRequest_GetClientContext(Cronet_UrlRequestPtr self);顺便插一句cronet 中各种对象都支持设置用户数据命名也非常统一 XXX_Get/SetClientContext。这样就可以通过 Cronet_UrlRequest 对象找到关联的链接对象回过头来再看性能信息的回调// The app implements abstract interface Cronet_RequestFinishedInfoListener by // defining custom functions for each method. typedef void (*Cronet_RequestFinishedInfoListener_OnRequestFinishedFunc)( Cronet_RequestFinishedInfoListenerPtr self, Cronet_RequestFinishedInfoPtr request_info, Cronet_UrlResponseInfoPtr response_info, Cronet_ErrorPtr error);