日本综合一区二区|亚洲中文天堂综合|日韩欧美自拍一区|男女精品天堂一区|欧美自拍第6页亚洲成人精品一区|亚洲黄色天堂一区二区成人|超碰91偷拍第一页|日韩av夜夜嗨中文字幕|久久蜜综合视频官网|精美人妻一区二区三区

RELATEED CONSULTING
相關咨詢
選擇下列產(chǎn)品馬上在線溝通
服務時間:8:30-17:00
你可能遇到了下面的問題
關閉右側工具欄

新聞中心

這里有您想知道的互聯(lián)網(wǎng)營銷解決方案
深入理解Node.js的Inspector

前言:Node.js提供的Inspector不僅可以用來調(diào)試Node.js代碼,還可以實時收集Node.js進程的內(nèi)存,CPU等數(shù)據(jù),同時支持靜態(tài)、動態(tài)開啟,是一個非常強大的工具,本文從使用和原理詳細講解Inspector。

我們提供的服務有:成都網(wǎng)站設計、成都做網(wǎng)站、微信公眾號開發(fā)、網(wǎng)站優(yōu)化、網(wǎng)站認證、羅平ssl等。為千余家企事業(yè)單位解決了網(wǎng)站和推廣的問題。提供周到的售前咨詢和貼心的售后服務,是有科學管理、有技術的羅平網(wǎng)站制作公司

Node.js的文檔中對inspector的描述很少,但是如果深入探索,其實里面的內(nèi)容還是挺多的。我們先看一下Inspector的使用。

1 Inspector的使用

1.1 本地調(diào)試

我們先從一個例子開始。下面是一個http服務器。

 
 
 
 
  1. const http = require('http'); 
  2. http.createServer((req, res) => { 
  3.     res.end('ok'); 
  4.  
  5. }).listen(80); 

然后我們以node --inspect httpServer.js的方式啟動。我們可以看到以下輸出。

 
 
 
 
  1. Debugger listening on ws://127.0.0.1:9229/fbbd9d8f-e088-48cc-b1e0-e16bfe58db44 
  2. For help, see: https://nodejs.org/en/docs/inspector 

9229端口是Node.js默認選擇的端口,當然我們也可以自定義,具體可參考文檔。這時候我們?nèi)g覽器打開開發(fā)者工具,菜單欄多了一個調(diào)試Node.js的按鈕。

點擊這個按鈕。我們可以看到以下界面。

我們可以選擇某一行代碼打斷點,比如我在第三行,這時候我們訪問80端口,開發(fā)者工具就會停留在斷點處。這時候我們可以看到一些執(zhí)行上下文。

1.2 遠程調(diào)試

但很多時候我們可能需要遠程調(diào)試。比如我在一臺云服務器上部署以上服務器代碼。然后執(zhí)行

 
 
 
 
  1. node --inspect=0.0.0.0:8888 httpServer.js 

不過這時候我們打開開發(fā)者工具就會發(fā)現(xiàn)按鈕置灰或者找不到我們遠程服務器的信息。這時候我們需要用另一種方式。通過在瀏覽器url輸入框輸入devtools://devtools/bundled/js_app.html?experiments=true&v8only=true&ws={host}:{port}/{path}的方式(替換{}里面的內(nèi)容為你執(zhí)行Node.js時輸出的信息),瀏覽器就會去連接你輸入的地址,比如1.1.1.1:9229/abc。這種比較適合于對于通用的場景。

1.3 自動探測

如果是我們自己調(diào)試的話,這種方式看起來就有點麻煩,我們可以使用瀏覽器提供的自動探測功能。1 url輸入框輸入chrome://inspect/#devices我們會看到以下界面

2 點擊configure按鈕,在彈出的彈框里輸入你遠程服務器的地址

3 配置完畢后,我們會看到界面變成這樣了,或者打開新的tab,我們看到開發(fā)者工具的調(diào)試按鈕也變亮了。

4 這時候我們點擊inspect按鈕、Open dedicated DevTools for Node按鈕或者打開新tab的開發(fā)者工具,就可以開始調(diào)試。而且還可以調(diào)試Node.js的原生js模塊。

2 Inspector調(diào)試的原理

下面以通過url的方式調(diào)試(可以看到network),來看看調(diào)試的時候都發(fā)生了什么,瀏覽器和遠程服務器建立連接后,是通過websocket協(xié)議通信的。

我們看一下這命令是什么意思,首先看Debugger.scriptParsed。

  • Debugger.scriptParsed # Fired when virtual machine parses script. This event is also fired for all known and uncollected scripts upon enabling debugger.

從說明中我們看到,當V8解析腳本的時候就會觸發(fā)這個事件,那就會告訴瀏覽器這個信息。

我們發(fā)現(xiàn)返回的都是一些元數(shù)據(jù),沒有腳本的具體代碼內(nèi)容,這時候瀏覽器會再次發(fā)起請求,

我們看到這個腳本的scriptId是103。所以請求里帶了這個scriptId。對應的請求id是11。接著看一下響應。

至此,我們了解了獲取腳本內(nèi)容的過程,然后我們看看調(diào)試的時候是怎樣的過程。當我們在瀏覽器上點擊某一行設置斷點的時候,瀏覽器就會發(fā)送一個請求。

這個命令的意義顧名思義,我們看一下具體定義。

  • Debugger.setBreakpointByUrl # Sets JavaScript breakpoint at given location specified either by URL or URL regex. Once this command is issued, all existing parsed scripts will have breakpoints resolved and returned in locations property. Further matching script parsing will result in subsequent breakpointResolved events issued. This logical breakpoint will survive page reloads.

接著服務返回響應。

這時候我們從另外一個tab訪問80端口。服務器就會在我們設置的斷點處停留,并且通知瀏覽器。

我們看一下這個命令的意思。

這個命令就是當服務器執(zhí)行到斷點時通知瀏覽器,并且返回執(zhí)行的一些上下文,比如是哪個執(zhí)行到哪個斷點停留了。這時候瀏覽器側也會停留在對應的地方,當我們hover某個變量時,就會看到對應的上下文。這些都是通過具體的命令獲取的數(shù)據(jù)。就不一一分析了,可以參考具體文檔。

3 Inspector的實現(xiàn)

大致了解了瀏覽器和服務器的交互過程和協(xié)議后,我們再來深入了解一下關于inspector的一些實現(xiàn)。當然這里不是分析V8中Inspector的實現(xiàn),而是分析如何使用V8的Inspector以及Node.js中關于Inspector的實現(xiàn)部分。

3.1 開源實現(xiàn)

因為Node.js的實現(xiàn)比較復雜,這里先以一個簡單版的調(diào)試工具源碼來分析inspector的原理。我們先看一下初始化代碼。

 
 
 
 
  1. inspector = std::unique_ptr(new Inspector(v8Platform, context, port)); 
  2. inspector->startAgent(); 

首先新建一個Inspector。然后啟動它。接下來看看Inspector里的邏輯。

 
 
 
 
  1. Inspector::Inspector( 
  2.         const std::unique_ptr &platform, 
  3.         const v8::Local &context, 
  4.         const int webSocketPort) { 
  5.  
  6.     context_ = context; 
  7.     // 新建一個websocket server用于和客戶端通信 
  8.     websocket_server_ = std::unique_ptr
  9.             new WebSocketServer( 
  10.                     webSocketPort, 
  11.                     // 收到客戶的的消息后執(zhí)行onMessage回調(diào) 
  12.                     std::bind(&Inspector::onMessage, this, std::placeholders::_1) 
  13.                 ) 
  14.             ); 
  15.     // 新建一個inspector client和V8通信 
  16.     inspector_client_ = std::unique_ptr
  17.             new V8InspectorClientImpl( 
  18.                     platform, 
  19.                     context_, 
  20.                     // 收到V8的消息后調(diào)用sendMessage回復給客戶的 
  21.                     std::bind(&Inspector::sendMessage, this, std::placeholders::_1), 
  22.                     std::bind(&Inspector::waitForFrontendMessage, this) 
  23.                 ) 
  24.             ); 
  25.  

代碼看起來很復雜,不過我們不需要深究。主要是兩個部分,一個是新建一個websocket服務器,一個是新建一個inspector客戶端(用于和V8 Inspector通信),整體架構如下。

接下來分別看一下websocket服務器和inspector客戶端的實現(xiàn)。首先看一下websocket服務器的構造函數(shù)。

 
 
 
 
  1. WebSocketServer::WebSocketServer(int port, std::function onMessage){ 
  2.     port_ = port; 
  3.     onMessage_ = std::move(onMessage); 
  4.  

WebSocketServer構造函數(shù)的實現(xiàn)很簡單,只是初始化一些字段。接著看inspector客戶端的實現(xiàn)。

 
 
 
 
  1. V8InspectorClientImpl:: V8InspectorClientImpl(const std::unique_ptr &platform, const v8::Local &context, const std::function &onResponse, const std::function &onWaitFrontendMessageOnPause) { 
  2.  
  3.     platform_ = platform.get(); 
  4.     context_ = context; 
  5.     onWaitFrontendMessageOnPause_ = onWaitFrontendMessageOnPause; 
  6.     isolate_ = context_->GetIsolate(); 
  7.     // 創(chuàng)建一個channel和inspector通信,收到V8消息時會執(zhí)行onResponse 
  8.     channel_.reset(new V8InspectorChannelImp(isolate_, onResponse)); 
  9.     // 新建一個V8提供的inspector 
  10.     inspector_ = v8_inspector::V8Inspector::create(isolate_, this); 
  11.     // 創(chuàng)建一個和inspector通信的session。 
  12.     session_ = inspector_->connect(kContextGroupId, channel_.get(), v8_inspector::StringView()); 
  13.     context_->SetAlignedPointerInEmbedderData(1, this); 
  14.     v8_inspector::StringView contextName = convertToStringView("inspector"); 
  15.     inspector_->contextCreated(v8_inspector::V8ContextInfo(context, kContextGroupId, contextName)); 
  16.     terminated_ = true; 
  17.     run_nested_loop_ = false; 
  18.  

上面代碼很多,主要是根據(jù)V8提供的API來就行。這里主要有三個概念

1 V8Inspector是V8提供的類。

2 session表示和V8 inspector通信的會話。

3 channel用于和V8 inspector通信,從API來看,channel只能從V8獲取數(shù)據(jù),寫入數(shù)據(jù)是另外的API。

這時候的架構如下

至此,websocket服務器和inspector客戶端就分析完畢了,回到最開始的代碼,初始化完畢后會執(zhí)行startAgent。

 
 
 
 
  1. void Inspector::startAgent() { 
  2.     websocket_server_->run(); 
  3.  
  4.  
  5.  
  6.  
  7. // 啟動websocket服務器 
  8.  
  9. void WebSocketServer::run() { 
  10.  
  11.     auto const address = net::ip::make_address("127.0.0.1"); 
  12.     net::io_context ioc{1}; 
  13.     tcp::acceptor acceptor{ioc, {address, static_cast(port_)}}; 
  14.     tcp::socket socket{ioc}; 
  15.     acceptor.accept(socket); 
  16.     ws_ = std::unique_ptr>(new websocket::stream(std::move(socket))); 
  17.     startListening(); 
  18.  
  19.  
  20.  
  21.  
  22. // 等待連接 
  23.  
  24. void WebSocketServer::startListening(){ 
  25.  
  26.    ws_->accept(); 
  27.    while (true) { 
  28.        waitFrontendMessage(); 
  29.    }}// 讀取連接中的消息void WebSocketServer::waitFrontendMessage(){ 
  30.     beast::flat_buffer buffer; 
  31.     ws_->read(buffer); 
  32.     std::string message = boost::beast::buffers_to_string(buffer.data()); 
  33.     onMessage_(std::move(message)); 
  34.  

startAgent的邏輯就是啟動websocket服務器。啟動完畢后就等待客戶的連接。連接成功后執(zhí)行onMessage_。我們看一下onMessage的實現(xiàn)。

 
 
 
 
  1. void Inspector::onMessage(const std::string& message) { 
  2.     std::cout << "CDT message: " << message << std::endl; 
  3.     // StringView是V8要求的格式 
  4.     v8_inspector::StringView protocolMessage = convertToStringView(message); 
  5.     // 通知V8 Inspector 
  6.     inspector_client_->dispatchProtocolMessage(protocolMessage); 
  7.  

onMessage通過Inspector客戶端把消息交給V8 Inspector處理。V8 Inspector處理完后,通過channel通知Inspector客戶端,對應的函數(shù)是sendResponse。V8InspectorChannelImp是繼承V8提供的Channel,sendResponse是一個純虛函數(shù),由V8InspectorChannelImp實現(xiàn)。

 
 
 
 
  1. void V8InspectorChannelImp::sendResponse(int callId, std::unique_ptr message) { 
  2.     const std::string response = convertToString(isolate_, message->string()); 
  3.     onResponse_(response); 
  4.  

onResponse_是在Chnnel初始化時設置的,對應函數(shù)是inspector客戶端的sendMessage。

 
 
 
 
  1. void Inspector::sendMessage(const std::string& message) { 
  2.     websocket_server_->sendMessage(message); 
  3.  

sendMessage通過websocket服務器把V8 Inspector返回的消息返回給客戶的。至此,整個通信流程就完成了。

3.2 Node.js的實現(xiàn)(v14)

Node.js的實現(xiàn)非常復雜并且很繞,本文根據(jù)也無法通俗易懂地介紹和分析,只能按照我自己的思路大致講解一下流程,有興趣的同學可以自行閱讀圓源碼。

3.2.1 初始化

在Node.js初始化的時候,會創(chuàng)建一個inspector::Agent并啟動。

 
 
 
 
  1. inspector_agent_ = std::make_unique(this); 
  2. env->InitializeInspector({}); 

接著看InitializeInspector。

 
 
 
 
  1. inspector_agent_->Start(inspector_path, 
  2.                         options_->debug_options(), 
  3.                         inspector_host_port(), 
  4.                         is_main_thread()); 

Start中會執(zhí)行StartIoThread進行進一步操作。

 
 
 
 
  1. bool Agent::StartIoThread() { 
  2.   io_ = InspectorIo::Start(client_->getThreadHandle(), 
  3.                            path_, 
  4.                            host_port_, 
  5.                            debug_options_.inspect_publish_uid); 
  6.   return true; 
  7.  

繼續(xù)看InspectorIo::Start

 
 
 
 
  1. std::unique_ptr InspectorIo::Start( 
  2.     std::shared_ptr main_thread, 
  3.     const std::string& path, 
  4.     std::shared_ptr host_port, 
  5.     const InspectPublishUid& inspect_publish_uid) { 
  6.  
  7.   auto io = std::unique_ptr
  8.       new InspectorIo(main_thread, 
  9.                       path, 
  10.                       host_port, 
  11.                       inspect_publish_uid)); 
  12.   return io; 
  13.  

InspectorIo::Start其實是創(chuàng)建了一個InspectorIo對象,我們看一下構造函數(shù)里的邏輯。

 
 
 
 
  1. InspectorIo::InspectorIo(std::shared_ptr main_thread, 
  2.                          const std::string& path, 
  3.                          std::shared_ptr host_port, 
  4.                          const InspectPublishUid& inspect_publish_uid) 
  5.     : main_thread_(main_thread), 
  6.       host_port_(host_port), 
  7.       inspect_publish_uid_(inspect_publish_uid), 
  8.       thread_(), 
  9.       script_name_(path), 
  10.       id_(GenerateID()) { 
  11.   Mutex::ScopedLock scoped_lock(thread_start_lock_); 
  12.   CHECK_EQ(uv_thread_create(&thread_, InspectorIo::ThreadMain, this), 0); 
  13.   thread_start_condition_.Wait(scoped_lock); 
  14.  

構造函數(shù)里創(chuàng)建了一個線程并在線程中執(zhí)行InspectorIo::ThreadMain。我們接著看ThreadMain。

 
 
 
 
  1. void InspectorIo::ThreadMain(void* io) { 
  2.   static_cast(io)->ThreadMain(); 
  3.  
  4.  
  5.  
  6.  
  7. void InspectorIo::ThreadMain() { 
  8.  
  9.   uv_loop_t loop; 
  10.   loop.data = nullptr; 
  11.   int err = uv_loop_init(&loop); 
  12.   CHECK_EQ(err, 0); 
  13.   std::shared_ptr queue(new RequestQueueData(&loop), 
  14.                                           RequestQueueData::CloseAndFree); 
  15.   std::string script_path = ScriptPath(&loop, script_name_); 
  16.   // 創(chuàng)建一個處理請求的delegate對象 
  17.   std::unique_ptr delegate( 
  18.       new InspectorIoDelegate(queue, main_thread_, id_, 
  19.                               script_path, script_name_)); 
  20.   // 創(chuàng)建一個服務器                             
  21.   InspectorSocketServer server(std::move(delegate), 
  22.                                &loop, 
  23.                                host_port_->host(), 
  24.                                host_port_->port(), 
  25.                                inspect_publish_uid_); 
  26.   request_queue_ = queue->handle(); 
  27.   // Its lifetime is now that of the server delegate 
  28.   queue.reset(); 
  29.   { 
  30.     Mutex::ScopedLock scoped_lock(thread_start_lock_); 
  31.     // 啟動服務器 
  32.     if (server.Start()) { 
  33.       host_port_->set_port(server.Port()); 
  34.     } 
  35.     thread_start_condition_.Broadcast(scoped_lock); 
  36.   } 
  37.   // 進入事件循環(huán) 
  38.   uv_run(&loop, UV_RUN_DEFAULT); 
  39.   CheckedUvLoopClose(&loop); 
  40.  

我們分析一下服務器相關的邏輯。先看一下創(chuàng)建一個服務器的邏輯

 
 
 
 
  1. InspectorSocketServer::InspectorSocketServer( 
  2.     std::unique_ptr delegate, uv_loop_t* loop, 
  3.     const std::string& host, int port, 
  4.     const InspectPublishUid& inspect_publish_uid, FILE* out) 
  5.     : loop_(loop), 
  6.       delegate_(std::move(delegate)), 
  7.       host_(host), 
  8.       port_(port), 
  9.       inspect_publish_uid_(inspect_publish_uid), 
  10.       next_session_id_(0), 
  11.       out_(out) { 
  12.   // 把服務器對象關聯(lián)到delete對象中 
  13.   delegate_->AssignServer(this); 
  14.   // 狀態(tài)為初始化 
  15.   state_ = ServerState::kNew; 
  16.  

接著看啟動服務器的邏輯。

 
 
 
 
  1. bool InspectorSocketServer::Start() { 
  2.   std::unique_ptr delegate_holder; 
  3.   // We will return it if startup is successful 
  4.   delegate_.swap(delegate_holder); 
  5.   struct addrinfo hints; 
  6.   memset(&hints, 0, sizeof(hints)); 
  7.   hints.ai_flags = AI_NUMERICSERV; 
  8.   hints.ai_socktype = SOCK_STREAM; 
  9.   uv_getaddrinfo_t req; 
  10.   const std::string port_string = std::to_string(port_); 
  11.   // 獲取地址信息 
  12.   int err = uv_getaddrinfo(loop_, &req, nullptr, host_.c_str(), 
  13.                            port_string.c_str(), &hints); 
  14.   for (addrinfo* address = req.addrinfo; address != nullptr; 
  15.        address = address->ai_next) { 
  16.     // 真正創(chuàng)建一個服務器 
  17.     auto server_socket = ServerSocketPtr(new ServerSocket(this)); 
  18.     // 監(jiān)聽地址,服務器啟動 
  19.     err = server_socket->Listen(address->ai_addr, loop_); 
  20.     if (err == 0) 
  21.       server_sockets_.push_back(std::move(server_socket)); 
  22.   } 
  23.   uv_freeaddrinfo(req.addrinfo); 
  24.   delegate_.swap(delegate_holder); 
  25.   state_ = ServerState::kRunning; 
  26.   // 打印提示,讓用戶知道連接到哪個url 
  27.   PrintDebuggerReadyMessage(host_, 
  28.                             server_sockets_, 
  29.                             delegate_->GetTargetIds(), 
  30.                             inspect_publish_uid_.console, 
  31.                             out_); 
  32.   return true; 
  33.  

啟動服務器的本質(zhì)就是監(jiān)聽某些IP+端口,所以我們接著分析一下ServerSocket對象的Listen函數(shù)。ServerSocket是對Libuv結構體uv_tcp_t的封裝,表示一個基于TCP的socket。另外ServerSocket還保存了所屬的InspectorSocketServer對象。

 
 
 
 
  1. int ServerSocket::Listen(sockaddr* addr, uv_loop_t* loop) { 
  2.   uv_tcp_t* server = &tcp_socket_; 
  3.   // 初始化Libuv handle 
  4.   CHECK_EQ(0, uv_tcp_init(loop, server)); 
  5.   // 在handle上綁定地址 
  6.   uv_tcp_bind(server, addr, 0); 
  7.   // 監(jiān)聽socket,監(jiān)聽成功后執(zhí)行SocketConnectedCallback 
  8.   uv_listen(reinterpret_cast(server), 511, ServerSocket::SocketConnectedCallback); 
  9.   return err; 
  10.  

繼續(xù)看SocketConnectedCallback

 
 
 
 
  1. void ServerSocket::SocketConnectedCallback(uv_stream_t* tcp_socket, 
  2.                                            int status) { 
  3.   // 監(jiān)聽成功                                         
  4.   if (status == 0) { 
  5.     // uv_tcp_t獲取所屬的ServerSocket對象 
  6.     ServerSocket* server_socket = ServerSocket::FromTcpSocket(tcp_socket); 
  7.     // Memory is freed when the socket closes. 
  8.     server_socket->server_->Accept(server_socket->port_, tcp_socket); 
  9.   } 
  10.  

接著看InspectorSocketServer::Accept。

 
 
 
 
  1. void InspectorSocketServer::Accept(int server_port, 
  2.                                    uv_stream_t* server_socket) { 
  3.   // 新建一個session表示一個連接對應的會話                                  
  4.   std::unique_ptr session( 
  5.       new SocketSession(this, next_session_id_++, server_port)); 
  6.   // 申請一個Delegate處理連接中的數(shù)據(jù) 
  7.   InspectorSocket::DelegatePointer delegate = 
  8.       InspectorSocket::DelegatePointer( 
  9.           new SocketSession::Delegate(this, session->id())); 
  10.   // 摘取一個連接 
  11.   InspectorSocket::Pointer inspector = 
  12.       InspectorSocket::Accept(server_socket, std::move(delegate)); 
  13.   if (inspector) { 
  14.     // 設置session對應的InspectorSocket 
  15.     session->Own(std::move(inspector)); 
  16.     connected_sessions_[session->id()].second = std::move(session); 
  17.   } 
  18.  

接著看InspectorSocket::Accept

 
 
 
 
  1. InspectorSocket::Pointer InspectorSocket::Accept(uv_stream_t* server, 
  2.                                                  DelegatePointer delegate) { 
  3.   // 摘取一個TCP連接                                                
  4.   auto tcp = TcpHolder::Accept(server, std::move(delegate)); 
  5.   // 操作成功 
  6.   if (tcp) { 
  7.     // 新建一個InspectorSocket表示和客戶的通信的對象 
  8.     InspectorSocket* inspector = new InspectorSocket(); 
  9.     // 設置處理連接數(shù)據(jù)的協(xié)議handler,即把數(shù)據(jù)當作HTTP協(xié)議處理 
  10.     inspector->SwitchProtocol(new HttpHandler(inspector, std::move(tcp))); 
  11.     return InspectorSocket::Pointer(inspector); 
  12.   } else { 
  13.     return InspectorSocket::Pointer(nullptr); 
  14.   } 
  15.  

接著看TcpHolder::Accept

 
 
 
 
  1. TcpHolder::Pointer TcpHolder::Accept( 
  2.     uv_stream_t* server, 
  3.     InspectorSocket::DelegatePointer delegate) { 
  4.   // 新建一個TcpHolder表示TCP連接 
  5.   TcpHolder* result = new TcpHolder(std::move(delegate)); 
  6.   uv_stream_t* tcp = reinterpret_cast(&result->tcp_); 
  7.   // 初始化 
  8.   uv_tcp_init(server->loop, &result->tcp_); 
  9.   // 這才是真正的摘取連接邏輯,uv_accept會把連接的fd保存到tcp結構體中 
  10.   uv_accept(server, tcp); 
  11.   // 設置等待可讀事件,回調(diào)是OnDataReceivedCb 
  12.   uv_read_start(tcp, allocate_buffer, OnDataReceivedCb); 
  13.  

當數(shù)據(jù)到來時,我們看一下OnDataReceivedCb是怎么處理的。

 
 
 
 
  1. void TcpHolder::OnDataReceivedCb(uv_stream_t* tcp, ssize_t nread, 
  2.                                  const uv_buf_t* buf) { 
  3.   TcpHolder* holder = From(tcp); 
  4.   holder->ReclaimUvBuf(buf, nread); 
  5.   if (nread < 0 || nread == UV_EOF) { 
  6.     holder->handler_->OnEof(); 
  7.   } else { 
  8.     holder->handler_->OnData(&holder->buffer); 
  9.   } 
  10.  

OnDataReceivedCb會調(diào)用handler_的onData函數(shù),handler_就是HttpHandler。

 
 
 
 
  1. void OnData(std::vector* data) override { 
  2.     llhttp_errno_t err; 
  3.     // 解析http協(xié)議 
  4.     err = llhttp_execute(&parser_, data->data(), data->size()); 
  5.  
  6.     if (err == HPE_PAUSED_UPGRADE) { 
  7.       err = HPE_OK; 
  8.       llhttp_resume_after_upgrade(&parser_); 
  9.     } 
  10.     // 省略一些步驟,第一個請求是一個升級http協(xié)議到websocket協(xié)議的請求 
  11.     delegate()->OnSocketUpgrade(event.host, event.path, event.ws_key); 
  12.  } 

看看HttpHandler的delegate(ProtocolHandler是HttpHandler的基類)

 
 
 
 
  1. InspectorSocket::Delegate* ProtocolHandler::delegate() { 
  2.   return tcp_->delegate(); 
  3.  
  4.  
  5.  
  6. InspectorSocket::Delegate* TcpHolder::delegate() { 
  7.   return delegate_.get(); 
  8.  

獲取的是TcpHolder的delegate。而TcpHolder的delegate是SocketSession::Delegate。即最后調(diào)用的是SocketSession::Delegate的OnSocketUpgrade函數(shù)。

 
 
 
 
  1. void SocketSession::Delegate::OnSocketUpgrade(const std::string& host, 
  2.                                               const std::string& path, 
  3.                                               const std::string& ws_key) { 
  4.   std::string id = path.empty() ? path : path.substr(1); 
  5.   server_->SessionStarted(session_id_, id, ws_key); 
  6.  

繼續(xù)看server_->SessionStarted。

 
 
 
 
  1. void InspectorSocketServer::SessionStarted(int session_id, 
  2.                                            const std::string& id, 
  3.                                            const std::string& ws_key) { 
  4.   SocketSession* session = Session(session_id); 
  5.   connected_sessions_[session_id].first = id; 
  6.   // 處理wesocket和客戶端的會話 
  7.   session->Accept(ws_key); 
  8.   // 開啟一個服務器中agent和V8 inspector會話 
  9.   delegate_->StartSession(session_id, id); 
  10.  

server_->SessionStarted主要有三個邏輯

1 保存一個和客戶端通信會話的關系

2 完成http協(xié)議到websocket協(xié)議的升級

3 新建一個和V8 Inspector的會話,Node.js類似一個代理。

我們先看如何處理websocket和客戶端的會話

 
 
 
 
  1. void Accept(const std::string& ws_key) { 
  2.     ws_socket_->AcceptUpgrade(ws_key); 

ws_socket_是session對應的InspectorSocket對象。

 
 
 
 
  1. void InspectorSocket::AcceptUpgrade(const std::string& ws_key) { 
  2.   protocol_handler_->AcceptUpgrade(ws_key); 
  3.  

InspectorSocket::AcceptUpgrade根據(jù)當前處理協(xié)議的handler進一步處理,目前是HTTP協(xié)議。

 
 
 
 
  1. void AcceptUpgrade(const std::string& accept_key) override { 
  2.     char accept_string[ACCEPT_KEY_LENGTH]; 
  3.     generate_accept_string(accept_key, &accept_string); 
  4.     // 回復101 Switching Protocols給客戶端說明同意協(xié)議升級 
  5.     const char accept_ws_prefix[] = "HTTP/1.1 101 Switching Protocols\r\n" 
  6.                                     "Upgrade: websocket\r\n" 
  7.                                     "Connection: Upgrade\r\n" 
  8.                                     "Sec-WebSocket-Accept: "; 
  9.     const char accept_ws_suffix[] = "\r\n\r\n"; 
  10.     std::vector reply(accept_ws_prefix, 
  11.                             accept_ws_prefix + sizeof(accept_ws_prefix) - 1); 
  12.     reply.insert(reply.end(), accept_string, 
  13.                  accept_string + sizeof(accept_string)); 
  14.     reply.insert(reply.end(), accept_ws_suffix, 
  15.                  accept_ws_suffix + sizeof(accept_ws_suffix) - 1); 
  16.     if (WriteRaw(reply, WriteRequest::Cleanup) >= 0) { 
  17.        // 切換協(xié)議為websocket,協(xié)議升級成功后,后續(xù)的數(shù)據(jù)被當作websocket協(xié)議處理 
  18.       inspector_->SwitchProtocol(new WsHandler(inspector_, std::move(tcp_))); 
  19.     } else { 
  20.       tcp_.reset(); 
  21.     } 

AcceptUpgrade完成了協(xié)議升級,接著服務器還需要和V8 inspector建立一個會話。

 
 
 
 
  1. void InspectorIoDelegate::StartSession(int session_id, 
  2.                                        const std::string& target_id) { 
  3.   auto session = main_thread_->Connect( 
  4.       std::unique_ptr
  5.           new IoSessionDelegate(request_queue_->handle(), session_id)), true); 
  6.   if (session) { 
  7.     sessions_[session_id] = std::move(session); 
  8.     fprintf(stderr, "Debugger attached.\n"); 
  9.   } 
  10.  

main_thread_是MainThreadHandle對象。

 
 
 
 
  1. std::unique_ptr MainThreadHandle::Connect( 
  2.     std::unique_ptr delegate, 
  3.     bool prevent_shutdown) { 
  4.   return std::unique_ptr
  5.       new CrossThreadInspectorSession(++next_session_id_, 
  6.                                       shared_from_this(), 
  7.                                       std::move(delegate), 
  8.                                       prevent_shutdown)); 
  9.  

這里新建了一個CrossThreadInspectorSession對象。我們看看CrossThreadInspectorSessionde 構造函數(shù)。

 
 
 
 
  1. CrossThreadInspectorSession( 
  2.       int id, 
  3.       std::shared_ptr thread, 
  4.       std::unique_ptr delegate, 
  5.       bool prevent_shutdown) 
  6.       : state_(thread, std::bind(MainThreadSessionState::Create, 
  7.                                  std::placeholders::_1, 
  8.                                  prevent_shutdown)) { 
  9.     state_.Call(&MainThreadSessionState::Connect, std::move(delegate)); 

構造函數(shù)中執(zhí)行了MainThreadSessionState::Connect函數(shù)

 
 
 
 
  1. void Connect(std::unique_ptr delegate) { 
  2.     Agent* agent = thread_->inspector_agent(); 
  3.     if (agent != nullptr) 
  4.       session_ = agent->Connect(std::move(delegate), prevent_shutdown_); 

Connect最后調(diào)用了agent的Connect。

 
 
 
 
  1. std::unique_ptr Agent::Connect( 
  2.     std::unique_ptr delegate, 
  3.     bool prevent_shutdown) { 
  4.   CHECK_NOT_NULL(client_); 
  5.   int session_id = client_->connectFrontend(std::move(delegate), 
  6.                                             prevent_shutdown); 
  7.   return std::unique_ptr
  8.       new Sa
    本文標題:深入理解Node.js的Inspector
    URL地址:http://www.dlmjj.cn/article/ccisheo.html