Chrome源代码分析之socket(一)

 
作为对HTTP连接的分析,首先跟踪一下Chrome对一个新的URL请求的处理流程。
 
从Chrome的实现来看,对一个URL资源的请求是放在Browser进程中来实现的,而不是由各个Render进程来实现,据说开发文档中提到这样做的三个主要优势,一是避免子进程进行网络通信,增加安全性,二是有利于Cookie等持久化资源在不同页面中的共享,否则在不同Render进程中传递Cookie比较麻烦,第三是由Browser统一进行网络通信可以减少HTTP连接的数量。
一般来说,当你在地址栏输入URL地址并且回车确认之后,由于AutocompleteEditViewWin继承自ATL的CRichEditCtrl类,因而他也拥有了消息映射的能力,在消息映射的控制下,最终OnKeyDownOnlyWritable函数被调用了,OnKeyDownOnlyWritable里面的通过switch对各种按键分别处理,如果是回车键,就调用model_->AcceptInput,这个函数要对传入的URL地址做一些简单判断,由于中间的调用过程比较漫长,这里不对每个函数进行分析了,只把调用流程列出来:
src\chrome\browser\autocomplete\autocomplete_edit.cc         #351view_->OpenURL
src\chrome\browser\autocomplete\autocomplete_edit_view_win.cc #612 model_->OpenURL
src\chrome\browser\autocomplete\autocomplete_edit.cc         #612 model_->OpenURL,
src\chrome\browser\autocomplete\autocomplete_edit.cc         #412 controller_->OnAutocompleteAccept。
src\chrome\browser\views\location_bar\location_bar_view.cc #791 command_updater_->ExecuteCommand(IDC_OPEN_CURRENT_URL);
src\chrome\browser\command_updater.cc                                  #45 delegate_->ExecuteCommand(id);
src\chrome\browser\browser.cc                                                      #2318 ExecuteCommandWithDisposition(id, CURRENT_TAB);
src\chrome\browser\browser.cc                                                      #1245 browser::Navigate(¶ms);
src\chrome\browser\browser_navigator.cc                                   #324 params->target_contents->controller().LoadURL
src\chrome\browser\tab_contents\navigation_controller.cc      #496 LoadEntry(entry);
src\chrome\browser\tab_contents\navigation_controller.cc      #282 NavigateToPendingEntry(NO_RELOAD);
src\chrome\browser\tab_contents\navigation_controller.cc      #1039 tab_contents_->NavigateToPendingEntry(reload_type)
src\chrome\browser\tab_contents\tab_contents.cc                     #853 NavigateToEntry(*controller_.pending_entry(), reload_type);
src\chrome\browser\tab_contents\tab_contents.cc                     #891 dest_render_view_host->Navigate(navigate_params);
src\chrome\browser\renderer_host\render_view_host.cc         #250 Send(nav_message);
我们看看最后2行, dest_render_view_host->Navigate(navigate_params);发生在TabContents::NavigateToEntry函数中,dest_render_view_host是一个指针,在函数开始的时候通过调用render_manager_.Navigate(entry)得到,我们知道在Chrome的进程模型里面,Browser进程管理其他进程和页面,一个进程就是一个RenderProcessHost实例,一个页面就是一个RenderViewHost实例。而页面实际上是承载在RenderProcess上的,一个RenderProcess上可以有多个RenderView。render_manager_是TabContents中负责管理RenderViewHost的类,这里它返回一个可用的RenderViewHost实例,RenderViewHost收集于此次URL请求相关的所有信息,生成一个消息并通过IPC机制发送给对于的RenderProcess,在Send之前的函数调用都发生在Browser进程的MainThread中,当需要发送消息给RenderProcess的时候则进入了I/O Thread 的领域内。至于IPC机制我们留到以后再分析。
消息发送出去以后,接收消息的应该是RenderProcess的RenderThread和RenderView,消息首先在RenderProcess里面经过分类和路由,后来传到给了RenderView的
OnMessageReceived函数,下面继续列出流程:
src\chrome\renderer\render_view.cc                                                    #764 IPC_MESSAGE_HANDLER(ViewMsg_Navigate, OnNavigate)
src\chrome\renderer\render_view.cc                                                    #1226 main_frame->loadRequest(request);
src\third_party\WebKit\WebKit\chromium\src\WebFrameImpl.cpp #883 m_frame->loader()->load(resourceRequest, false);
src\third_party\WebKit\WebCore\loader\FrameLoader.cpp              #1362  load(request, SubstituteData(), lockHistory);
src\third_party\WebKit\WebCore\loader\FrameLoader.cpp              #1375 load(loader.get());
src\third_party\WebKit\WebCore\loader\FrameLoader.cpp              #1433 loadWithDocumentLoader(newDocumentLoader, type, 0);
src\third_party\WebKit\WebCore\loader\FrameLoader.cpp              #1477 continueLoadAfterNavigationPolicy(loader->request(), formState, false);
src\third_party\WebKit\WebCore\loader\FrameLoader.cpp              #2975 continueLoadAfterWillSubmitForm();
src\third_party\WebKit\WebCore\loader\FrameLoader.cpp              #2463 m_provisionalDocumentLoader->startLoadingMainResource(identifier)
src\third_party\WebKit\WebCore\loader\DocumentLoader.cpp       #765 m_mainResourceLoader->load(m_request, m_substituteData)
src\third_party\WebKit\WebCore\loader\MainResourceLoader.cpp#583 loadNow(request)
src\third_party\WebKit\WebCore\loader\MainResourceLoader.cpp #556 ResourceHandle::create
src\third_party\WebKit\WebKit\chromium\src\ResourceHandle.cpp      #223 newHandle->start(context)
src\third_party\WebKit\WebKit\chromium\src\ResourceHandle.cpp      #251 d->start();
src\third_party\WebKit\WebKit\chromium\src\ResourceHandle.cpp      #111 m_loader->loadAsynchronously(wrappedRequest, this);
src\webkit\glue\weburlloader_impl.cc                               #717 context_->Start(request, NULL);  
src\webkit\glue\weburlloader_impl.cc                               #476 bridge_->Start(this)
src\chrome\common\resource_dispatcher.cc                           #186 dispatcher_->message_sender()->Send
到了这里,通过IPC机制将页面渲染的请求再封装为一个消息,发送给Browser进程。当然在调用dispatcher_->message_sender()->Send之前代码依然在RenderThread上执行,不过消息的发送实际上是在MainThread中进行的,其中的原因在IPC机制中再行分析。
作为接受消息的Browser进程,这个消息首先I/O Thrad中被处理,然后转发给MainThread,MainThread再进行一些处理最后映射到ResourceDispatcherHost的OnMessageReceived函数。


src\chrome\browser\renderer_host\resource_dispatcher_host.cc       #542 BeginRequestInternal(request);
src\chrome\browser\renderer_host\resource_dispatcher_host.cc       #1310 InsertIntoResourceQueue(request, *info);
src\chrome\browser\renderer_host\resource_dispatcher_host.cc       #1316 resource_queue_.AddRequest(request, request_info);
src\chrome\browser\renderer_host\resource_queue.cc                 #62 request->Start();
src\net\url_request\url_request.cc                                 #307  StartJob(GetJobManager()->CreateJob(this));
src\net\url_request\url_request.cc                                 #336 job_->Start();
src\net\url_request\url_request_http_job.cc                        #159 AddCookieHeaderAndStart();
src\net\url_request\url_request_http_job.cc                        #739 OnCanGetCookiesCompleted(policy);
src\net\url_request\url_request_http_job.cc                        #460  StartTransaction();
src\net\url_request\url_request_http_job.cc                        #632 transaction_->Start
src\net\http\http_network_transaction.cc                           #144 DoLoop(OK);
src\net\http\http_network_transaction.cc                           #446 DoCreateStream();
src\net\http\http_network_transaction.cc                           #449 DoCreateStreamComplete(rv);
src\net\http\http_network_transaction.cc                           #456 DoInitStreamComplete(rv);
src\net\http\http_network_transaction.cc                           #463 DoGenerateProxyAuthTokenComplete(rv);
src\net\http\http_network_transaction.cc                           #470 DoGenerateServerAuthTokenComplete(rv);
src\net\http\http_network_transaction.cc                           #475 DoSendRequest();
src\net\http\http_network_transaction.cc                           #646 stream_->SendRequest
src\net\http\http_basic_stream.cc                                  #48 parser_->SendRequest
src\net\http\http_stream_parser.cc                                 #63 DoLoop(OK)
src\net\http\http_stream_parser.cc                                 #145 DoSendHeaders(result);
src\net\http\http_stream_parser.cc                                 #228 connection_->socket()->Write
 
request->Start()函数这里进入网络通信部分,从connection_->socket()->Write开始详细分析。
这里的Write调用实际上是TCPClientSocketWin类的成员函数,函数传入的三个参数分别是发送缓存buf,缓存长度buf_len,回调函数指针callback。
 
接着用下面的代码将发送数据保存起来
 
  core_->write_buffer_.len = buf_len;
  core_->write_buffer_.buf = buf->data();
  core_->write_buffer_length_ = buf_len;
core_是TCPClientSocketWin的成员变量,实际上是一个类,在TCPClientSocketWin读写网络数据的过程中发挥这核心作用,先看看core_的定义:
class TCPClientSocketWin::Core : public base::RefCounted {
 public:
  explicit Core(TCPClientSocketWin* socket);
  void WatchForRead();
  void WatchForWrite();
  void Detach() { socket_ = NULL; }
  OVERLAPPED read_overlapped_;
  OVERLAPPED write_overlapped_;
  WSABUF read_buffer_;
  WSABUF write_buffer_;
  scoped_refptr read_iobuffer_;
  scoped_refptr write_iobuffer_;
  int write_buffer_length_;
  int ThrottleReadSize(int size) {
    if (slow_start_throttle_ < kMaxSlowStartThrottle) {
      size = std::min(size, slow_start_throttle_);
      slow_start_throttle_ *= 2;
    }
    return size;
  }
 private:
  friend class base::RefCounted;
  class ReadDelegate : public base::ObjectWatcher::Delegate {
   public:
    explicit ReadDelegate(Core* core) : core_(core) {}
    virtual ~ReadDelegate() {}
    virtual void OnObjectSignaled(HANDLE object);
   private:
    Core* const core_;
  };
  class WriteDelegate : public base::ObjectWatcher::Delegate {
   public:
    explicit WriteDelegate(Core* core) : core_(core) {}
    virtual ~WriteDelegate() {}
    virtual void OnObjectSignaled(HANDLE object);
   private:
    Core* const core_;
  };
  ~Core();
  TCPClientSocketWin* socket_;
  ReadDelegate reader_;
  WriteDelegate writer_;
  base::ObjectWatcher read_watcher_;
  base::ObjectWatcher write_watcher_;
  static const int kInitialSlowStartThrottle = 1 * 1024;
  static const int kMaxSlowStartThrottle = 32 * kInitialSlowStartThrottle;
  int slow_start_throttle_;
  DISALLOW_COPY_AND_ASSIGN(Core);
};
总的来说类Core的作用就是为基于事件通知异步I/O的TCP连接提供和协调所有相关的事件对象,发送接收缓存,线程池控制以及回调处理等相关功能。其中OVERLAPPED read_overlapped_和OVERLAPPED write_overlapped_分别是读写的重叠I/O控制变量,WSABUF read_buffer_和WSABUF write_buffer_维护读写的缓存。

在Core还在类体中即时定义了2个私有类:class ReadDelegate : public base::ObjectWatcher::Delegate和class WriteDelegate : public base::ObjectWatcher::Delegate。这2个类都只有一个成员函数OnObjectSignaled,这个函数在Chrome自建线程的消息循环中将被调用。至于OnObjectSignaled为什么会被调用,这跟另外两个成员变量base::ObjectWatcher read_watcher_和base::ObjectWatcher write_watcher_直接相关,后面再详细分析。

我们接着全面分析一下TCPClientSocketWin类,先看看它是如何建立一个TCP连接,TCPClientSocketWin首先调用成员函数Connect,它只是简单的设置next_connect_state_的状态,以及给current_ai_赋值,current_ai_是一个addrinfo类型的结构体,addrinfo兼容IPV4以及IPV6的地址信息,因此我们可以透明化的处理这2个协议族的地址。接着调用DoConnectLoop这个函数,再看看DoConnectLoop的实现,只有一个简单的循环,首先调用DoConnect,如果连接立即成功的话接着调用DoConnectComplete,否则返回一个ERR_IO_PENDING显示连接请求处于异步出流流程中,或者是调用失败,返回错误代码。


下面看看DoConnect()函数的实现,不仅仅因为在写入数据之前需要调用connect来接连新连接,而且在这个函数中还 初始化了套接口的异步I/O模型,下面看看这个函数的主要代码:

#1 int TCPClientSocketWin::DoConnect() {
#2 const struct addrinfo* ai = current_ai_;
#3   DCHECK(ai);
#4   DCHECK_EQ(0, connect_os_error_);
#5   if (previously_disconnected_) {
#6     use_history_.Reset();
#7    previously_disconnected_ = false;
#8  }
#9  net_log_.BeginEvent(NetLog::TYPE_TCP_CONNECT_ATTEMPT,
#10                       new NetLogStringParameter(
#11                          "address", NetAddressToStringWithPort(current_ai_)));//这里写入之日方便调试
#12   next_connect_state_ = CONNECT_STATE_CONNECT_COMPLETE;
#13   connect_os_error_ = CreateSocket(ai); //创建新的socket
#14   if (connect_os_error_ != 0)
#15     return MapWinsockError(connect_os_error_);
#16   DCHECK(!core_);
#17   core_ = new Core(this);    //创建一个core_
#18   core_->read_overlapped_.hEvent = WSACreateEvent(); //创建重叠I/O结构
#19   WSAEventSelect(socket_, core_->read_overlapped_.hEvent, FD_CONNECT);//初始化I/O模型,WSAEventSelect是一个异步I/O模型,具体可以参考MSDN等材料的说明
#20   core_->write_overlapped_.hEvent = WSACreateEvent();
#21   if (!connect(socket_, ai->ai_addr, static_cast(ai->ai_addrlen))) {//异步模式下connect调用后立即返回
#22    NOTREACHED();
#23     if (ResetEventIfSignaled(core_->read_overlapped_.hEvent))
#24       return OK;
#25   } else {
#26     int os_error = WSAGetLastError();
#27    if (os_error != WSAEWOULDBLOCK) {
#28      LOG(ERROR) << "connect failed: " << os_error;
#29       connect_os_error_ = os_error;
#30       return MapConnectError(os_error);
#31     }
#32   }
#34   core_->WatchForRead(); //这里安装回调函数
#35   return ERR_IO_PENDING;

#36 }

在第十三行调用CreateSocket创建一个新的套接口,接着创建一个Core的对象core_,并给read_overlapped_和write_overlapped_分别创建用于异步I/O的事件对象,在19行初始化WSAEventSelect模型,用于实现connect的异步调用,根据微软的文档,一旦调用了WSAEventSelect,目标套接口将转为非阻塞模式,connect不再等待服务器的响应而是立即返回,返回值是SOCKET_ERRORE,应该说connect永远不会返回0,但是在这里他们仍对返回0的情况做了处理,调用ResetEventIfSignaled来判断事件对象是否处于已传信状态,还可以看到,connect与read共用了read_overlapped_,因此在Core::ReadDelegate::OnObjectSignaled函数中,会区分connect和read并分别调用相应的处理函数。最后34行调用core_->WatchForRead()讲这个连接交给线程模型,如果连接成功的话,DidCompleteConnect将被调用,最后调用DoReadCallback通知上层的调用者。

 

你可能感兴趣的:(Chrome网络部分源代码分析)