作為對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<Core> {
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<IOBuffer> read_iobuffer_;
scoped_refptr<IOBuffer> 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<Core>;
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<int>(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通知上層的調用者。