深入理解Node.js的Inspector

前言:Node.js提供的Inspector不仅可以用来调试Node.js代码,还可以实时收集Node.js进程的内存,CPU等数据,同时支持静态、动态开启,是一个非常强大的工具,本文从使用和原理详细讲解Inspector。

我们提供的服务有:成都网站设计、成都做网站、微信公众号开发、网站优化、网站认证、罗平ssl等。为千余家企事业单位解决了网站和推广的问题。提供周到的售前咨询和贴心的售后服务,是有科学管理、有技术的罗平网站制作公司

Node.js的文档中对inspector的描述很少,但是如果深入探索,其实里面的内容还是挺多的。我们先看一下Inspector的使用。

1 Inspector的使用

1.1 本地调试

我们先从一个例子开始。下面是一个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默认选择的端口,当然我们也可以自定义,具体可参考文档。这时候我们去浏览器打开开发者工具,菜单栏多了一个调试Node.js的按钮。

点击这个按钮。我们可以看到以下界面。

我们可以选择某一行代码打断点,比如我在第三行,这时候我们访问80端口,开发者工具就会停留在断点处。这时候我们可以看到一些执行上下文。

1.2 远程调试

但很多时候我们可能需要远程调试。比如我在一台云服务器上部署以上服务器代码。然后执行

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

不过这时候我们打开开发者工具就会发现按钮置灰或者找不到我们远程服务器的信息。这时候我们需要用另一种方式。通过在浏览器url输入框输入devtools://devtools/bundled/js_app.html?experiments=true&v8only=true&ws={host}:{port}/{path}的方式(替换{}里面的内容为你执行Node.js时输出的信息),浏览器就会去连接你输入的地址,比如1.1.1.1:9229/abc。这种比较适合于对于通用的场景。

1.3 自动探测

如果是我们自己调试的话,这种方式看起来就有点麻烦,我们可以使用浏览器提供的自动探测功能。1 url输入框输入chrome://inspect/#devices我们会看到以下界面

2 点击configure按钮,在弹出的弹框里输入你远程服务器的地址

3 配置完毕后,我们会看到界面变成这样了,或者打开新的tab,我们看到开发者工具的调试按钮也变亮了。

4 这时候我们点击inspect按钮、Open dedicated DevTools for Node按钮或者打开新tab的开发者工具,就可以开始调试。而且还可以调试Node.js的原生js模块。

2 Inspector调试的原理

下面以通过url的方式调试(可以看到network),来看看调试的时候都发生了什么,浏览器和远程服务器建立连接后,是通过websocket协议通信的。

我们看一下这命令是什么意思,首先看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解析脚本的时候就会触发这个事件,那就会告诉浏览器这个信息。

我们发现返回的都是一些元数据,没有脚本的具体代码内容,这时候浏览器会再次发起请求,

我们看到这个脚本的scriptId是103。所以请求里带了这个scriptId。对应的请求id是11。接着看一下响应。

至此,我们了解了获取脚本内容的过程,然后我们看看调试的时候是怎样的过程。当我们在浏览器上点击某一行设置断点的时候,浏览器就会发送一个请求。

这个命令的意义顾名思义,我们看一下具体定义。

  • 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端口。服务器就会在我们设置的断点处停留,并且通知浏览器。

我们看一下这个命令的意思。

这个命令就是当服务器执行到断点时通知浏览器,并且返回执行的一些上下文,比如是哪个执行到哪个断点停留了。这时候浏览器侧也会停留在对应的地方,当我们hover某个变量时,就会看到对应的上下文。这些都是通过具体的命令获取的数据。就不一一分析了,可以参考具体文档。

3 Inspector的实现

大致了解了浏览器和服务器的交互过程和协议后,我们再来深入了解一下关于inspector的一些实现。当然这里不是分析V8中Inspector的实现,而是分析如何使用V8的Inspector以及Node.js中关于Inspector的实现部分。

3.1 开源实现

因为Node.js的实现比较复杂,这里先以一个简单版的调试工具源码来分析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.                     // 收到客户的的消息后执行onMessage回调 
  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的消息后调用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客户端的实现。首先看一下websocket服务器的构造函数。

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

WebSocketServer构造函数的实现很简单,只是初始化一些字段。接着看inspector客户端的实现。

 
 
 
 
  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.     // 创建一个channel和inspector通信,收到V8消息时会执行onResponse 
  8.     channel_.reset(new V8InspectorChannelImp(isolate_, onResponse)); 
  9.     // 新建一个V8提供的inspector 
  10.     inspector_ = v8_inspector::V8Inspector::create(isolate_, this); 
  11.     // 创建一个和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.  

上面代码很多,主要是根据V8提供的API来就行。这里主要有三个概念

1 V8Inspector是V8提供的类。

2 session表示和V8 inspector通信的会话。

3 channel用于和V8 inspector通信,从API来看,channel只能从V8获取数据,写入数据是另外的API。

这时候的架构如下

至此,websocket服务器和inspector客户端就分析完毕了,回到最开始的代码,初始化完毕后会执行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服务器。启动完毕后就等待客户的连接。连接成功后执行onMessage_。我们看一下onMessage的实现。

 
 
 
 
  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客户端,对应的函数是sendResponse。V8InspectorChannelImp是继承V8提供的Channel,sendResponse是一个纯虚函数,由V8InspectorChannelImp实现。

 
 
 
 
  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初始化时设置的,对应函数是inspector客户端的sendMessage。

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

sendMessage通过websocket服务器把V8 Inspector返回的消息返回给客户的。至此,整个通信流程就完成了。

3.2 Node.js的实现(v14)

Node.js的实现非常复杂并且很绕,本文根据也无法通俗易懂地介绍和分析,只能按照我自己的思路大致讲解一下流程,有兴趣的同学可以自行阅读圆源码。

3.2.1 初始化

在Node.js初始化的时候,会创建一个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中会执行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.  

继续看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其实是创建了一个InspectorIo对象,我们看一下构造函数里的逻辑。

 
 
 
 
  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.  

构造函数里创建了一个线程并在线程中执行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.   // 创建一个处理请求的delegate对象 
  17.   std::unique_ptr delegate( 
  18.       new InspectorIoDelegate(queue, main_thread_, id_, 
  19.                               script_path, script_name_)); 
  20.   // 创建一个服务器                             
  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.   // 进入事件循环 
  38.   uv_run(&loop, UV_RUN_DEFAULT); 
  39.   CheckedUvLoopClose(&loop); 
  40.  

我们分析一下服务器相关的逻辑。先看一下创建一个服务器的逻辑

 
 
 
 
  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.   // 把服务器对象关联到delete对象中 
  13.   delegate_->AssignServer(this); 
  14.   // 状态为初始化 
  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.     // 真正创建一个服务器 
  17.     auto server_socket = ServerSocketPtr(new ServerSocket(this)); 
  18.     // 监听地址,服务器启动 
  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.  

启动服务器的本质就是监听某些IP+端口,所以我们接着分析一下ServerSocket对象的Listen函数。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.   // 监听socket,监听成功后执行SocketConnectedCallback 
  8.   uv_listen(reinterpret_cast(server), 511, ServerSocket::SocketConnectedCallback); 
  9.   return err; 
  10.  

继续看SocketConnectedCallback

 
 
 
 
  1. void ServerSocket::SocketConnectedCallback(uv_stream_t* tcp_socket, 
  2.                                            int status) { 
  3.   // 监听成功                                         
  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处理连接中的数据 
  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.     // 设置处理连接数据的协议handler,即把数据当作HTTP协议处理 
  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.   // 设置等待可读事件,回调是OnDataReceivedCb 
  12.   uv_read_start(tcp, allocate_buffer, OnDataReceivedCb); 
  13.  

当数据到来时,我们看一下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会调用handler_的onData函数,handler_就是HttpHandler。

 
 
 
 
  1. void OnData(std::vector* data) override { 
  2.     llhttp_errno_t err; 
  3.     // 解析http协议 
  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协议到websocket协议的请求 
  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。即最后调用的是SocketSession::Delegate的OnSocketUpgrade函数。

 
 
 
 
  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.  

继续看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协议到websocket协议的升级

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根据当前处理协议的handler进一步处理,目前是HTTP协议。

 
 
 
 
  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给客户端说明同意协议升级 
  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.        // 切换协议为websocket,协议升级成功后,后续的数据被当作websocket协议处理 
  18.       inspector_->SwitchProtocol(new WsHandler(inspector_, std::move(tcp_))); 
  19.     } else { 
  20.       tcp_.reset(); 
  21.     } 

AcceptUpgrade完成了协议升级,接着服务器还需要和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 构造函数。

 
 
 
 
  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)); 

构造函数中执行了MainThreadSessionState::Connect函数

 
 
 
 
  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最后调用了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
    转载来源:http://www.shufengxianlan.com/qtweb/news41/26091.html

    网站建设、网络推广公司-创新互联,是专注品牌与效果的网站制作,网络营销seo公司;服务项目有等

    广告

    声明:本网站发布的内容(图片、视频和文字)以用户投稿、用户转载内容为主,如果涉及侵权请尽快告知,我们将会在第一时间删除。文章观点不代表本网站立场,如需处理请联系客服。电话:028-86922220;邮箱:631063699@qq.com。内容未经允许不得转载,或转载时需注明来源: 创新互联