chromium中FTP网络资源的加载
2016-08-17 13:04
363 查看
FTP网络资源的加载
render进程的处理
我们在浏览器地址中输入ftp://ftp.sjtu.edu.cn/,来请求ftp数据。此时windbg会中断,因为浏览器启动了一个render进程来渲染绘制。我们在render进程中如下下断
bu chrome_child!content::ResourceDispatcher::StartAsync
这个函数由render进程调用,用来和browser进程通信,通知browser来加载网络资源
int ResourceDispatcher::StartAsync(const RequestInfo& request_info, ResourceRequestBody* request_body, scoped_ptr<RequestPeer> peer) { GURL frame_origin; scoped_ptr<ResourceHostMsg_Request> request = CreateRequest(request_info, request_body, &frame_origin); // Compute a unique request_id for this renderer process. int request_id = MakeRequestID(); pending_requests_[request_id] = make_scoped_ptr(new PendingRequestInfo( std::move(peer), request->resource_type, request->origin_pid, frame_origin, request->url, request_info.download_to_file)); if (resource_scheduling_filter_.get() && request_info.loading_web_task_runner) { resource_scheduling_filter_->SetRequestIdTaskRunner( request_id, make_scoped_ptr(request_info.loading_web_task_runner->clone())); } message_sender_->Send(new ResourceHostMsg_RequestResource( request_info.routing_id, request_id, *request)); return request_id; }
通知browser进程的消息是ResourceHostMsg_RequestResource,我们来看一下render进程的调用情况
8:010> kc # Call Site 00 chrome_child!content::ResourceDispatcher::StartAsync 01 chrome_child!content::WebURLLoaderImpl::Context::Start 02 chrome_child!blink::ResourceLoader::start 03 chrome_child!blink::Resource::load 04 chrome_child!blink::ResourceFetcher::requestResource 05 chrome_child!blink::RawResource::fetchMainResource 06 chrome_child!blink::DocumentLoader::startLoadingMainResource 07 chrome_child!blink::FrameLoader::startLoad 08 chrome_child!blink::FrameLoader::load 09 chrome_child!blink::WebLocalFrameImpl::load 0a chrome_child!content::RenderFrameImpl::NavigateInternal 0b chrome_child!content::RenderFrameImpl::OnNavigate 0c chrome_child!base::DispatchToMethodImpl 0d chrome_child!base::DispatchToMethod 0e chrome_child!IPC::DispatchToMethod 0f chrome_child!IPC::MessageT<FrameMsg_Navigate_Meta,std::tuple<content::CommonNavigationParams,content::StartNavigationParams,content::RequestNavigationParams>,void>::Dispatch<content::RenderFrameImpl,content::RenderFrameImpl,void,void (__cdecl content::RenderFrameImpl::*)(content::CommonNavigationParams const & __ptr64,content::StartNavigationParams const & __ptr64,content::RequestNavigationParams const & __ptr64) __ptr64> 10 chrome_child!content::RenderFrameImpl::OnMessageReceived 11 chrome_child!IPC::MessageRouter::RouteMessage 12 chrome_child!content::ChildThreadImpl::OnMessageReceived 13 chrome_child!IPC::ChannelProxy::Context::OnDispatchMessage 14 chrome_child!base::Callback<void __cdecl(void)>::Run 15 chrome_child!base::debug::TaskAnnotator::RunTask 16 chrome_child!scheduler::TaskQueueManager::ProcessTaskFromWorkQueue 17 chrome_child!scheduler::TaskQueueManager::DoWork 18 chrome_child!base::internal::RunnableAdapter<void (__cdecl scheduler::TaskQueueManager::*)(base::TimeTicks,bool)>::Run 19 chrome_child!base::internal::InvokeHelper<1,void,base::internal::RunnableAdapter<void (__cdecl scheduler::TaskQueueManager::*)(base::TimeTicks,bool) __ptr64> >::MakeItSo<base::WeakPtr<scheduler::TaskQueueManager>,base::TimeTicks const & __ptr64,bool const & __ptr64> 1a chrome_child!base::internal::Invoker<base::IndexSequence<0,1,2>,base::internal::BindState<base::internal::RunnableAdapter<void (__cdecl scheduler::TaskQueueManager::*)(base::TimeTicks,bool) __ptr64>,void __cdecl(scheduler::TaskQueueManager * __ptr64,base::TimeTicks,bool),base::WeakPtr<scheduler::TaskQueueManager>,base::TimeTicks,bool>,base::internal::InvokeHelper<1,void,base::internal::RunnableAdapter<void (__cdecl scheduler::TaskQueueManager::*)(base::TimeTicks,bool) __ptr64> >,void __cdecl(void)>::Run 1b chrome_child!base::Callback<void __cdecl(void)>::Run 1c chrome_child!base::debug::TaskAnnotator::RunTask 1d chrome_child!base::MessageLoop::RunTask 1e chrome_child!base::MessageLoop::DeferOrRunPendingTask 1f chrome_child!base::MessageLoop::DoWork 20 chrome_child!base::MessagePumpDefault::Run 21 chrome_child!base::MessageLoop::RunHandler 22 chrome_child!base::RunLoop::Run 23 chrome_child!base::MessageLoop::Run 24 chrome_child!content::RendererMain 25 chrome_child!content::RunNamedProcessTypeMain 26 chrome_child!content::ContentMainRunnerImpl::Run 27 chrome_child!content::ContentMain 28 chrome_child!ChromeMain 29 chrome!MainDllLoader::Launch 2a chrome!wWinMain 2b chrome!__tmainCRTStartup 2c kernel32!BaseThreadInitThunk 2d ntdll!RtlUserThreadStart
从上面可以看出,这是通过blink模块进行资源加载,而实际的任务通过browser进程。
browser进程的处理
ResourceLoader
我们到browser进程中的相应函数中下断,看看browser如何进行实际的资源加载和网络访问的。bp chrome_7feee760000!content::ResourceDispatcherHostImpl::OnRequestResource
下断后,运行,中断。
0:019> kc # Call Site 00 chrome_7feee760000!content::ResourceLoader::StartRequest 01 chrome_7feee760000!content::ResourceDispatcherHostImpl::StartLoading 02 chrome_7feee760000!content::ResourceDispatcherHostImpl::BeginRequestInternal 03 chrome_7feee760000!content::ResourceDispatcherHostImpl::BeginRequest 04 chrome_7feee760000!content::ResourceDispatcherHostImpl::OnRequestResource 05 chrome_7feee760000!base::DispatchToMethodImpl 06 chrome_7feee760000!base::DispatchToMethod 07 chrome_7feee760000!IPC::DispatchToMethod 08 chrome_7feee760000!IPC::MessageT<ResourceHostMsg_RequestResource_Meta,std::tuple<int,int,ResourceHostMsg_Request>,void>::Dispatch<content::ResourceDispatcherHostImpl,content::ResourceDispatcherHostImpl,void,void (__cdecl content::ResourceDispatcherHostImpl::*)(int,int,ResourceHostMsg_Request const & __ptr64) __ptr64> 09 chrome_7feee760000!content::ResourceDispatcherHostImpl::OnMessageReceived 0a chrome_7feee760000!content::BrowserMessageFilter::Internal::DispatchMessageW 0b chrome_7feee760000!content::BrowserMessageFilter::Internal::OnMessageReceived 0c chrome_7feee760000!IPC::`anonymous namespace'::TryFiltersImpl 0d chrome_7feee760000!IPC::MessageFilterRouter::TryFilters 0e chrome_7feee760000!IPC::ChannelProxy::Context::TryFilters 0f chrome_7feee760000!IPC::ChannelProxy::Context::OnMessageReceived 10 chrome_7feee760000!IPC::internal::ChannelReader::DispatchMessageW 11 chrome_7feee760000!IPC::internal::ChannelReader::HandleExternalMessage 12 chrome_7feee760000!IPC::internal::ChannelReader::HandleTranslatedMessage 13 chrome_7feee760000!IPC::internal::ChannelReader::TranslateInputData 14 chrome_7feee760000!IPC::internal::ChannelReader::AsyncReadComplete 15 chrome_7feee760000!IPC::ChannelWin::OnIOCompleted 16 chrome_7feee760000!base::MessagePumpForIO::WaitForIOCompletion 17 chrome_7feee760000!base::MessagePumpForIO::DoRunLoop 18 chrome_7feee760000!base::MessagePumpWin::Run 19 chrome_7feee760000!base::MessageLoop::RunHandler 1a chrome_7feee760000!base::RunLoop::Run 1b chrome_7feee760000!base::MessageLoop::Run 1c chrome_7feee760000!base::Thread::Run 1d chrome_7feee760000!content::BrowserThreadImpl::IOThreadRun 1e chrome_7feee760000!content::BrowserThreadImpl::Run 1f chrome_7feee760000!base::Thread::ThreadMain 20 chrome_7feee760000!base::`anonymous namespace'::ThreadFunc 21 kernel32!BaseThreadInitThunk 22 ntdll!RtlUserThreadStart
如上的堆栈示意,这是位于IO线程中,通过IPC的完成端口接收到来自render进程的消息,然后分发这个消息,遇到了浏览器的BrowserMessageFilter,将消息分发到ResourceDispatcherHostImpl来专门的处理这个消息,ResourceDispatcherHostImpl进一步的通过ResourceLoader来加载资源。
void ResourceLoader::StartRequest() { if (delegate_->HandleExternalProtocol(this, request_->url())) { CancelAndIgnore(); return; } // Give the handler a chance to delay the URLRequest from being started. bool defer_start = false; if (!handler_->OnWillStart(request_->url(), &defer_start)) { Cancel(); return; } if (defer_start) { deferred_stage_ = DEFERRED_START; } else { StartRequestInternal(); } }
在ResourceLoader中有个延迟启动的策略,我们这个情况下就是延迟启动的。
URLRequest
不过无论是延迟启动还是正常的启动都是要创建URLrequest的。如下下断:bp chrome_7feee760000!net::URLRequestFtpJob::Start
我们是个ftp协议,针对的是ftp的URLRequestFtpJob进行特殊的处理。
0:019> kc # Call Site 00 chrome_7feee760000!net::FtpNetworkTransaction::Start 01 chrome_7feee760000!net::URLRequestFtpJob::StartFtpTransaction 02 chrome_7feee760000!net::URLRequestFtpJob::OnResolveProxyComplete 03 chrome_7feee760000!net::URLRequestFtpJob::Start 04 chrome_7feee760000!net::URLRequest::StartJob 05 chrome_7feee760000!net::URLRequest::BeforeRequestComplete 06 chrome_7feee760000!net::URLRequest::Start 07 chrome_7feee760000!content::ResourceLoader::StartRequestInternal 08 chrome_7feee760000!content::ResourceLoader::Resume 09 chrome_7feee760000!content::ThrottlingResourceHandler::ResumeStart 0a chrome_7feee760000!content::ThrottlingResourceHandler::Resume 0b chrome_7feee760000!base::internal::RunnableAdapter<void (__cdecl extensions::NativeMessageProcessHost::*)(int)>::Run 0c chrome_7feee760000!base::internal::InvokeHelper<1,void,base::internal::RunnableAdapter<void (__cdecl extensions::NativeMessageProcessHost::*)(int)> >::MakeItSo 0d chrome_7feee760000!base::internal::Invoker<base::IndexSequence<0>,base::internal::BindState<base::internal::RunnableAdapter<void (__cdecl extensions::NativeMessageProcessHost::*)(int) __ptr64>,void __cdecl(extensions::NativeMessageProcessHost * __ptr64,int),base::WeakPtr<extensions::NativeMessageProcessHost> >,base::internal::InvokeHelper<1,void,base::internal::RunnableAdapter<void (__cdecl extensions::NativeMessageProcessHost::*)(int) __ptr64> >,void __cdecl(int const & __ptr64)>::Run 0e chrome_7feee760000!base::Callback<void __cdecl(std::list<content::IndexedDBInfo,std::allocator<content::IndexedDBInfo> > const &)>::Run 0f chrome_7feee760000!base::internal::InvokeHelper<0,void,base::Callback<void __cdecl(std::list<content::IndexedDBInfo,std::allocator<content::IndexedDBInfo> > const &)> >::MakeItSo 10 chrome_7feee760000!base::internal::Invoker<base::IndexSequence<0>,base::internal::BindState<base::Callback<void __cdecl(std::list<content::IndexedDBInfo,std::allocator<content::IndexedDBInfo> > const & __ptr64)>,void __cdecl(std::list<content::IndexedDBInfo,std::allocator<content::IndexedDBInfo> > const & __ptr64),std::list<content::IndexedDBInfo,std::allocator<content::IndexedDBInfo> > & __ptr64>,base::internal::InvokeHelper<0,void,base::Callback<void __cdecl(std::list<content::IndexedDBInfo,std::allocator<content::IndexedDBInfo> > const & __ptr64)> >,void __cdecl(void)>::Run 11 chrome_7feee760000!base::Callback<void __cdecl(void)>::Run 12 chrome_7feee760000!base::debug::TaskAnnotator::RunTask 13 chrome_7feee760000!base::MessageLoop::RunTask 14 chrome_7feee760000!base::MessageLoop::DeferOrRunPendingTask 15 chrome_7feee760000!base::MessageLoop::DoWork 16 chrome_7feee760000!base::MessagePumpForIO::DoRunLoop 17 chrome_7feee760000!base::MessagePumpWin::Run 18 chrome_7feee760000!base::MessageLoop::RunHandler 19 chrome_7feee760000!base::RunLoop::Run 1a chrome_7feee760000!base::MessageLoop::Run 1b chrome_7feee760000!base::Thread::Run 1c chrome_7feee760000!content::BrowserThreadImpl::IOThreadRun 1d chrome_7feee760000!content::BrowserThreadImpl::Run 1e chrome_7feee760000!base::Thread::ThreadMain 1f chrome_7feee760000!base::`anonymous namespace'::ThreadFunc 20 kernel32!BaseThreadInitThunk 21 ntdll!RtlUserThreadStart
当URLRequest处理过后,会启动一个针对ftp的URLRequestFtpJob来继续处理,后者启动ftp的网络传输FtpNetworkTransaction。
void URLRequestFtpJob::StartFtpTransaction() { // Create a transaction. DCHECK(!ftp_transaction_); ftp_request_info_.url = request_->url(); ftp_transaction_ = ftp_transaction_factory_->CreateTransaction(); int rv; if (ftp_transaction_) { rv = ftp_transaction_->Start( &ftp_request_info_, base::Bind(&URLRequestFtpJob::OnStartCompleted, base::Unretained(this)), request_->net_log()); if (rv == ERR_IO_PENDING) return; } else { rv = ERR_FAILED; } // The transaction started synchronously, but we need to notify the // URLRequest delegate via the message loop. OnStartCompletedAsync(rv); }
FtpNetworkTransaction
资源加载的启动过程job启动ftp传输的时候,先使用ftp_transaction_factory_来创建一个网络传输。而这个成员变量是在创建URLRequestFtpJob的时候就传递了数据,传递者是URLRequest。
int FtpNetworkTransaction::Start(const FtpRequestInfo* request_info, const CompletionCallback& callback, const BoundNetLog& net_log) { net_log_ = net_log; request_ = request_info; ctrl_response_buffer_.reset(new FtpCtrlResponseBuffer(net_log_)); if (request_->url.has_username()) { base::string16 username; base::string16 password; GetIdentityFromURL(request_->url, &username, &password); credentials_.Set(username, password); } else { credentials_.Set(base::ASCIIToUTF16("anonymous"), base::ASCIIToUTF16("chrome@example.com")); } DetectTypecode(); next_state_ = STATE_CTRL_RESOLVE_HOST; int rv = DoLoop(OK); if (rv == ERR_IO_PENDING) user_callback_ = callback; return rv; }
当网络传输创建完成后悔启动这个网络传输,设置用户名和密码,然后设置传输状态,首先就是要解析主机地址,设置状态为STATE_CTRL_RESOLVE_HOST,,进入状态循环处理函数。
FTP所要处理的状态如下:
enum State { // Control connection states: STATE_CTRL_RESOLVE_HOST, STATE_CTRL_RESOLVE_HOST_COMPLETE, STATE_CTRL_CONNECT, STATE_CTRL_CONNECT_COMPLETE, STATE_CTRL_READ, STATE_CTRL_READ_COMPLETE, STATE_CTRL_WRITE, STATE_CTRL_WRITE_COMPLETE, STATE_CTRL_WRITE_USER, STATE_CTRL_WRITE_PASS, STATE_CTRL_WRITE_SYST, STATE_CTRL_WRITE_TYPE, STATE_CTRL_WRITE_EPSV, STATE_CTRL_WRITE_PASV, STATE_CTRL_WRITE_PWD, STATE_CTRL_WRITE_RETR, STATE_CTRL_WRITE_SIZE, STATE_CTRL_WRITE_CWD, STATE_CTRL_WRITE_LIST, STATE_CTRL_WRITE_QUIT, // Data connection states: STATE_DATA_CONNECT, STATE_DATA_CONNECT_COMPLETE, STATE_DATA_READ, STATE_DATA_READ_COMPLETE, STATE_NONE };
分控制连接状态和数据连接状态,我们接触了第一个状态就是解析主机的状态,我们看这些状态,还有的状态是完成状态,当一种控制或数据完成后会设置一个完成状态,在完成状态的处理函数中进行必要的处理。
int FtpNetworkTransaction::DoLoop(int result) { DCHECK(next_state_ != STATE_NONE); int rv = result; do { State state = next_state_; next_state_ = STATE_NONE; switch (state) { case STATE_CTRL_RESOLVE_HOST: DCHECK(rv == OK); rv = DoCtrlResolveHost(); break; case STATE_CTRL_RESOLVE_HOST_COMPLETE: rv = DoCtrlResolveHostComplete(rv); break; ...... } } while (rv != ERR_IO_PENDING && next_state_ != STATE_NONE); return rv; }
而状态循环的函数是DoLoop,仿佛状态机的处理函数,我们当前的状态是解析主机状态,自然我们进入的函数是DoCtrlResolveHost。
int FtpNetworkTransaction::DoCtrlResolveHost() { next_state_ = STATE_CTRL_RESOLVE_HOST_COMPLETE; HostResolver::RequestInfo info(HostPortPair::FromURL(request_->url)); // No known referrer. return resolver_.Resolve( info, DEFAULT_PRIORITY, &addresses_, base::Bind(&FtpNetworkTransaction::OnIOComplete, base::Unretained(this)), net_log_); }
函数中首先设置了下一跳状态为STATE_CTRL_RESOLVE_HOST_COMPLETE,然后使用resolver来解析主机地址,并设置了回调函数。我们在回调函数上下断。
void FtpNetworkTransaction::OnIOComplete(int result) { int rv = DoLoop(result); if (rv != ERR_IO_PENDING) DoCallback(rv); } int FtpNetworkTransaction::DoCtrlResolveHostComplete(int result) { if (result == OK) next_state_ = STATE_CTRL_CONNECT; return result; }
当地址解析完成后,调用IO完成回调函数,函数中继续进行循环处理,处理下一状态,在解析主机完成函数中设置状态为控制连接状态,开始准备连接。
int FtpNetworkTransaction::DoCtrlConnect() { next_state_ = STATE_CTRL_CONNECT_COMPLETE; ctrl_socket_ = socket_factory_->CreateTransportClientSocket( addresses_, net_log_.net_log(), net_log_.source()); net_log_.AddEvent( NetLog::TYPE_FTP_CONTROL_CONNECTION, ctrl_socket_->NetLog().source().ToEventParametersCallback()); return ctrl_socket_->Connect(io_callback_); }
连接状态中创建传输客户端socket,这是一个控制socket,然后使用这个socket来连接主机,并设置连接完成回调。
当连接完成后会交互一些命令,就是上面的一列状态的处理。
当控制连接建立完成后,会建立数据连接,都这一切都准备好了之后,上层会调用读取操作。
资源加载的读取过程
启动完成后,ResourceLoader会进行持续的读取操作。
0:019> kc # Call Site 00 chrome_7feee760000!net::FtpNetworkTransaction::Read 01 chrome_7feee760000!net::URLRequestFtpJob::ReadRawData 02 chrome_7feee760000!net::URLRequestJob::ReadRawDataHelper 03 chrome_7feee760000!net::URLRequestJob::Read 04 chrome_7feee760000!net::URLRequest::Read 05 chrome_7feee760000!content::ResourceLoader::ReadMore 06 chrome_7feee760000!content::ResourceLoader::StartReading 07 chrome_7feee760000!content::ResourceLoader::ResumeReading 08 chrome_7feee760000!base::internal::RunnableAdapter<void (__cdecl syncer_v2::SharedModelTypeProcessor::*)(void)>::Run 09 chrome_7feee760000!base::internal::InvokeHelper<1,void,base::internal::RunnableAdapter<void (__cdecl syncer_v2::SharedModelTypeProcessor::*)(void)> >::MakeItSo 0a chrome_7feee760000!base::internal::Invoker<base::IndexSequence<0>,base::internal::BindState<base::internal::RunnableAdapter<void (__cdecl syncer_v2::SharedModelTypeProcessor::*)(void) __ptr64>,void __cdecl(syncer_v2::SharedModelTypeProcessor * __ptr64),base::WeakPtr<syncer_v2::SharedModelTypeProcessor> >,base::internal::InvokeHelper<1,void,base::internal::RunnableAdapter<void (__cdecl syncer_v2::SharedModelTypeProcessor::*)(void) __ptr64> >,void __cdecl(void)>::Run 0b chrome_7feee760000!base::Callback<void __cdecl(void)>::Run 0c chrome_7feee760000!base::debug::TaskAnnotator::RunTask 0d chrome_7feee760000!base::MessageLoop::RunTask 0e chrome_7feee760000!base::MessageLoop::DeferOrRunPendingTask 0f chrome_7feee760000!base::MessageLoop::DoWork 10 chrome_7feee760000!base::MessagePumpForIO::DoRunLoop 11 chrome_7feee760000!base::MessagePumpWin::Run 12 chrome_7feee760000!base::MessageLoop::RunHandler 13 chrome_7feee760000!base::RunLoop::Run 14 chrome_7feee760000!base::MessageLoop::Run 15 chrome_7feee760000!base::Thread::Run 16 chrome_7feee760000!content::BrowserThreadImpl::IOThreadRun 17 chrome_7feee760000!content::BrowserThreadImpl::Run 18 chrome_7feee760000!base::Thread::ThreadMain 19 chrome_7feee760000!base::`anonymous namespace'::ThreadFunc 1a kernel32!BaseThreadInitThunk 1b ntdll!RtlUserThreadStart
int FtpNetworkTransaction::Read(IOBuffer* buf, int buf_len, const CompletionCallback& callback) { DCHECK(buf); DCHECK_GT(buf_len, 0); read_data_buf_ = buf; read_data_buf_len_ = buf_len; next_state_ = STATE_DATA_READ; int rv = DoLoop(OK); if (rv == ERR_IO_PENDING) user_callback_ = callback; return rv; }
函数中设置了状态,进入状态循环函数,处理数据读取。
int FtpNetworkTransaction::DoDataRead() { DCHECK(read_data_buf_.get()); DCHECK_GT(read_data_buf_len_, 0); if (data_socket_ == NULL || !data_socket_->IsConnected()) { // If we don't destroy the data socket completely, some servers will wait // for us (http://crbug.com/21127). The half-closed TCP connection needs // to be closed on our side too. data_socket_.reset(); if (ctrl_socket_->IsConnected()) { // Wait for the server's response, we should get it before sending QUIT. next_state_ = STATE_CTRL_READ; return OK; } // We are no longer connected to the server, so just finish the transaction. return Stop(OK); } next_state_ = STATE_DATA_READ_COMPLETE; read_data_buf_->data()[0] = 0; return data_socket_->Read( read_data_buf_.get(), read_data_buf_len_, io_callback_); } int FtpNetworkTransaction::DoDataReadComplete(int result) { return result; }
数据读取函数使用data_socket进行读取,并设置了IO完成回调函数。
相关文章推荐
- chromium中FTP网络资源的加载
- chromium网络资源加载分析(二) 主资源加载逻辑分析 之head部分加载---chromium39
- chromium中HTTP网络资源的加载过程
- chromium网络资源加载分析(一) 主资源加载逻辑分析 ---chromium39
- chromium网络资源加载分析(三) 主资源加载逻辑分析 之body部分加载---chromium39
- (转)在Unity3D的网络游戏中实现资源动态加载
- 【转】在Unity3D的网络游戏中实现资源动态加载
- 在Unity3D的网络游戏中实现资源动态加载
- 理解WebKit和Chromium: WebKit资源加载机制
- 在Unity3D的网络游戏中实现资源动态加载
- android 网络加载图片,对图片资源进行优化,并且实现内存双缓存 + 磁盘缓存
- 在Unity3D的网络游戏中实现资源动态加载
- 在Unity3D的网络游戏中实现资源动态加载
- Chromium的多进程资源加载
- Unity3D的网络游戏中实现资源动态加载
- Unity3D的网络游戏中实现资源动态加载
- [IOS]UIWebView 请求网络页面或者加载本地资源页面
- 玩玩思维导图【网络资源】stl思维导图 ,h3c思维导图 ,sed思维导图 ,awk思维导图, vim思维导图, iptable思维导图【图片很多,加载请耐心】
- 在Unity3D的网络游戏中实现资源动态加载
- Android 中加载网络资源时的优化 缓存和异步机制