<output id="qn6qe"></output>

    1. <output id="qn6qe"><tt id="qn6qe"></tt></output>
    2. <strike id="qn6qe"></strike>

      亚洲 日本 欧洲 欧美 视频,日韩中文字幕有码av,一本一道av中文字幕无码,国产线播放免费人成视频播放,人妻少妇偷人无码视频,日夜啪啪一区二区三区,国产尤物精品自在拍视频首页,久热这里只有精品12
      代碼改變世界

      基于AppDomain的"插件式"開發(fā)

      2011-08-01 09:58  空逸云  閱讀(12021)  評論(47)    收藏  舉報(bào)

      很多時(shí)候,我們都想使用(開發(fā))USB式(熱插拔)的應(yīng)用,例如,開發(fā)一個(gè)WinForm應(yīng)用,并且這個(gè)WinForm應(yīng)用能允許開發(fā)人員定制擴(kuò)展插件,又例如,我們可能維護(hù)著一個(gè)WinService管理系統(tǒng),這個(gè)WinService系統(tǒng)管理的形形色色各種各樣的服務(wù),這些服務(wù)也是各個(gè)"插件式"的類庫,例如:

          public interface IJob
          {
              void Run(DateTime time);
          }
      
          public class CollectUserInfo : IJob
          {
      
              public void Run(DateTime time)
              {
                  //doing some thing...
              }
          }

      我們提供了一個(gè)IJob接口,所有"服務(wù)"都繼承該接口,然后做相關(guān)的配置,在服務(wù)啟動(dòng)時(shí),就可以根據(jù)配置,反射加載程序集,執(zhí)行我們預(yù)期的任務(wù).

      更新程序集(dll/exe)

      服務(wù)/插件程序(后面只稱為服務(wù),雖然兩者應(yīng)用不同,但是在此處他們所運(yùn)用的原理和作用是相同的 :-) )很健穩(wěn)的運(yùn)行著.但在服務(wù)/插件程序運(yùn)行一段時(shí)間之后,某些"插件"的業(yè)務(wù)需求發(fā)生的變化,或者版本升級等種種外部原因,導(dǎo)致我們對原本的"插件"程序集進(jìn)行了升級(可能從v1.0升級至v2.0).當(dāng)我們想像Asp.net應(yīng)用一樣.把新的dll替換舊dll的時(shí)候,錯(cuò)誤發(fā)生了.

      image

      發(fā)生該錯(cuò)誤的原因很簡單,因?yàn)槲覀兊某绦蛑幸呀?jīng)調(diào)用了該dll,那么在CLR加載該dll到文件流中也給其加了鎖,所以,當(dāng)我們要進(jìn)行覆蓋,修改,刪除的時(shí)候自然就無法操作該文件了.那我們該怎么做?為什么Asp.net可以直接覆蓋?

      AppDomain登場

      我們知道,AppDomain是.Net平臺(tái)里一個(gè)很重要的特性,在.Net以前,每個(gè)程序是"封裝"在不同的進(jìn)程中的,這樣導(dǎo)致的結(jié)果就造就占用資源大,可復(fù)用性低等缺點(diǎn).而AppDomain在同一個(gè)進(jìn)程內(nèi)劃分出多個(gè)"域",一個(gè)進(jìn)程可以運(yùn)行多個(gè)應(yīng)用,提高了資源的復(fù)用性,數(shù)據(jù)通信等.詳見應(yīng)用程序域

      CLR在啟動(dòng)的時(shí)候會(huì)創(chuàng)建系統(tǒng)域(System Domain),共享域(Shared Domain)和默認(rèn)域(Default Domain),系統(tǒng)域與共享域?qū)τ谟脩羰遣豢梢姷?默認(rèn)域也可以說是當(dāng)前域,它承載了當(dāng)前應(yīng)用程序的各類信息(堆棧),所以,我們的一切操作都是在這個(gè)默認(rèn)域上進(jìn)行."插件式"開發(fā)很大程度上就是依靠AppDomain來進(jìn)行.

      "熱插拔"實(shí)現(xiàn)說明

      當(dāng)加載了一個(gè)程序集之后,該程序集就會(huì)被加入到指定AppDomain中,按照原來的想法,要實(shí)現(xiàn)"熱插拔",只要在需要使用該"插件"的時(shí)候,加載該"插件"的程序集(dll),使用結(jié)束后,卸載掉該程序集便可達(dá)到我們預(yù)期的效果.加載程序集很簡單,.C#提供一個(gè)Assembly類,方便又快捷.

      var  _assembly = Assembly.LoadFrom(assemblyFile);
      

      Assembly提供了數(shù)個(gè)加載方法詳見Assembly類.

      然后,C#卻沒有提供卸載程序集的方法,唯一能卸載程序集的方法只有卸載該程序集所在的AppDomain,這樣,該AppDomain下的程序集都會(huì)被釋放.知道這一點(diǎn),我們便可以利用AppDomain來達(dá)到我們預(yù)期的效果.

      AppDomain實(shí)現(xiàn)"熱插拔"

      首先,我們需要先實(shí)例化一個(gè)新AppDomain作為"插件"的宿主.在實(shí)例化一個(gè)Domain之前,先聲明該Domain的一些基本配置信息

                  AppDomainSetup setup = new AppDomainSetup();
                  setup.ApplicationName = "ApplicationLoader";    
                  setup.ApplicationBase = AppDomain.CurrentDomain.BaseDirectory;  
                  setup.PrivateBinPath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "private");
                  setup.CachePath = setup.ApplicationBase;
                  setup.ShadowCopyFiles = "true"; //啟用影像復(fù)制程序集
                  setup.ShadowCopyDirectories = setup.ApplicationBase;
                  AppDomain.CurrentDomain.SetShadowCopyFiles();
      

      setup.ShadowCopyFiles = "true";這句很重要,其作用就是啟用影像復(fù)制程序集,什么是影像復(fù)制程序集,復(fù)制程序集是保證"熱插拔"

      實(shí)現(xiàn)的主要工作.AppDomain加載程序集的時(shí)候,如果沒有ShadowCopyFiles,那就直接加載程序集,結(jié)果就是程序集被鎖定,相反,如果啟用了ShadowCopyFiles,則CLR會(huì)將準(zhǔn)備加載的程序集拷貝一份至CachePath,再加載CachePath的這一份程序集,這樣原程序集也就不會(huì)被鎖定了. AppDomain.CurrentDomain.SetShadowCopyFiles();的作用就是當(dāng)前AppDomain也啟用ShadowCopyFiles,在此,當(dāng)前AppDomain也就是前面我們說過的那個(gè)默認(rèn)域(Default Domain),為什么當(dāng)前域也要啟用ShadowCopyFiles呢?

      主AppDomian在調(diào)用子AppDomain提供過來的類型,方法,屬性的時(shí)候,也會(huì)將該程序集添加到自身程序集引用當(dāng)中去,所以,"插件"程序集就被主AppDomain鎖定,這也是為什么創(chuàng)建了單獨(dú)的AppDomain程序集也不能刪除,替換(釋放)的根本原因

      利用SOS,可以很清楚的看到這一點(diǎn)

      0:018> !dumpdomain
      --------------------------------------
      System Domain:      5b912478
      LowFrequencyHeap:   5b912784
      HighFrequencyHeap:  5b9127d0
      StubHeap:           5b91281c
      Stage:              OPEN
      Name:               None
      --------------------------------------
      Shared Domain:      5b912140
      LowFrequencyHeap:   5b912784
      HighFrequencyHeap:  5b9127d0
      StubHeap:           5b91281c
      Stage:              OPEN
      Name:               None
      Assembly:           00109de0 [C:\Windows\Microsoft.Net\assembly\GAC_32\mscorlib\v4.0_4.0.0.0__b77a5c561934e089\mscorlib.dll]
      ClassLoader:        00110f68
        Module Name
      58631000            C:\Windows\Microsoft.Net\assembly\GAC_32\mscorlib\v4.0_4.0.0.0__b77a5c561934e089\mscorlib.dll
      
      --------------------------------------
      Domain 1:           000f4598
      LowFrequencyHeap:   000f4914
      HighFrequencyHeap:  000f4960
      StubHeap:           000f49ac
      Stage:              OPEN
      SecurityDescriptor: 000f5568
      Name:               AppDomainTest.exe
      Assembly:           00109de0 [C:\Windows\Microsoft.Net\assembly\GAC_32\mscorlib\v4.0_4.0.0.0__b77a5c561934e089\mscorlib.dll]
      ClassLoader:        00110f68
      SecurityDescriptor: 001097b0
        Module Name
      58631000            C:\Windows\Microsoft.Net\assembly\GAC_32\mscorlib\v4.0_4.0.0.0__b77a5c561934e089\mscorlib.dll
      
      Assembly:           0011d448 [E:\Test\AppDomainTest\AppDomainTest\bin\Debug\AppDomainTest.exe]
      ClassLoader:        00117fd0
      SecurityDescriptor: 0011d3c0
        Module Name
      001c2e9c            E:\Test\AppDomainTest\AppDomainTest\bin\Debug\AppDomainTest.exe
      
      Assembly:           00131370 [C:\Windows\Microsoft.Net\assembly\GAC_MSIL\System.Windows.Forms\v4.0_4.0.0.0__b77a5c561934e089\System.Windows.Forms.dll]
      ClassLoader:        0011fa00
      SecurityDescriptor: 001299a0
        Module Name
      579c1000            C:\Windows\Microsoft.Net\assembly\GAC_MSIL\System.Windows.Forms\v4.0_4.0.0.0__b77a5c561934e089\System.Windows.Forms.dll
      
      Assembly:           00131400 [C:\Windows\Microsoft.Net\assembly\GAC_MSIL\System.Drawing\v4.0_4.0.0.0__b03f5f7f11d50a3a\System.Drawing.dll]
      ClassLoader:        00131490
      SecurityDescriptor: 0012e9c0
        Module Name
      62661000            C:\Windows\Microsoft.Net\assembly\GAC_MSIL\System.Drawing\v4.0_4.0.0.0__b03f5f7f11d50a3a\System.Drawing.dll
      
      Assembly:           00131d20 [C:\Windows\Microsoft.Net\assembly\GAC_MSIL\System\v4.0_4.0.0.0__b77a5c561934e089\System.dll]
      ClassLoader:        00133d08
      SecurityDescriptor: 0012f078
        Module Name
      5aa81000            C:\Windows\Microsoft.Net\assembly\GAC_MSIL\System\v4.0_4.0.0.0__b77a5c561934e089\System.dll
      
      Assembly:           00131ed0 [C:\Windows\Microsoft.Net\assembly\GAC_MSIL\System.Configuration\v4.0_4.0.0.0__b03f5f7f11d50a3a\System.Configuration.dll]
      ClassLoader:        001415a8
      SecurityDescriptor: 0012f430
        Module Name
      5a981000            C:\Windows\Microsoft.Net\assembly\GAC_MSIL\System.Configuration\v4.0_4.0.0.0__b03f5f7f11d50a3a\System.Configuration.dll
      
      Assembly:           00132080 [C:\Windows\Microsoft.Net\assembly\GAC_MSIL\System.Xml\v4.0_4.0.0.0__b77a5c561934e089\System.Xml.dll]
      ClassLoader:        00141620
      SecurityDescriptor: 0012f5c8
        Module Name
      546e1000            C:\Windows\Microsoft.Net\assembly\GAC_MSIL\System.Xml\v4.0_4.0.0.0__b77a5c561934e089\System.Xml.dll
      
      Assembly:           00132ce0 [E:\Test\AppDomainTest\AppDomainTest\bin\Debug\CrossDomainController.dll]
      ClassLoader:        001b3450
      SecurityDescriptor: 06f94560
        Module Name
      001c7428            E:\Test\AppDomainTest\AppDomainTest\bin\Debug\CrossDomainController.dll
      
      Assembly:           00132350 [C:\Users\kong\AppData\Local\assembly\dl3\6ZYK3XE9.86Q\2AQ35O7C.VHE\1f704bbb\b7cca5cf_8c4fcc01\ShowHelloPlug.DLL]
      ClassLoader:        001b32e8
      SecurityDescriptor: 070a8620
        Module Name
      001c7d78            C:\Users\kong\AppData\Local\assembly\dl3\6ZYK3XE9.86Q\2AQ35O7C.VHE\1f704bbb\b7cca5cf_8c4fcc01\ShowHelloPlug.DLL
      
      --------------------------------------
      Domain 2:           06fd0238
      LowFrequencyHeap:   06fd05b4
      HighFrequencyHeap:  06fd0600
      StubHeap:           06fd064c
      Stage:              OPEN
      SecurityDescriptor: 06724510
      Name:               ApplicationLoaderDomain
      Assembly:           00109de0 [C:\Windows\Microsoft.Net\assembly\GAC_32\mscorlib\v4.0_4.0.0.0__b77a5c561934e089\mscorlib.dll]
      ClassLoader:        00110f68
      SecurityDescriptor: 06f93bd0
        Module Name
      58631000            C:\Windows\Microsoft.Net\assembly\GAC_32\mscorlib\v4.0_4.0.0.0__b77a5c561934e089\mscorlib.dll
      
      Assembly:           00132e90 [E:\Test\AppDomainTest\AppDomainTest\bin\Debug\ApplicationLoader\assembly\dl3\c91a2898\f6f7f865_9a4fcc01\CrossDomainController.DLL]
      ClassLoader:        001b3540
      SecurityDescriptor: 06f92be0
        Module Name
      00a833c4            E:\Test\AppDomainTest\AppDomainTest\bin\Debug\ApplicationLoader\assembly\dl3\c91a2898\f6f7f865_9a4fcc01\CrossDomainController.DLL
      
      Assembly:           001330d0 [E:\Test\AppDomainTest\AppDomainTest\bin\Debug\ApplicationLoader\assembly\dl3\32519346\b7cca5cf_8c4fcc01\ShowHelloPlug.DLL]
      ClassLoader:        001b39f0
      SecurityDescriptor: 06f92f98
        Module Name
      00a83adc            E:\Test\AppDomainTest\AppDomainTest\bin\Debug\ApplicationLoader\assembly\dl3\32519346\b7cca5cf_8c4fcc01\ShowHelloPlug.DLL
      
      

      除了新建的AppDomain(Domain2)中的Module引用了ShowHelloPlug.dll,默認(rèn)域(Domian1)也有ShowHelloPlug.dll的

      程序集引用.

      應(yīng)用程序域之間的通信

      每個(gè)AppDomain都有自己的堆棧,內(nèi)存塊,也就是說它們之間的數(shù)據(jù)并非共享了.若想共享數(shù)據(jù),則涉及到應(yīng)用程序域之間的通信.C#提供了MarshalByRefObject類進(jìn)行跨域通信,那么,我們必須提供自己的跨域訪問器.

          public class RemoteLoader : MarshalByRefObject
          {
              private Assembly _assembly;
      
              public void LoadAssembly(string assemblyFile)
              {
                  try
                  {
                     _assembly = Assembly.LoadFrom(assemblyFile);
                      //return _assembly;
                  }
                  catch (Exception ex)
                  {
                      throw ex;
                  }
              }
      
              public T GetInstance<T>(string typeName) where T : class
              {
                  if (_assembly == null) return null;
                  var type = _assembly.GetType(typeName);
                  if (type == null) return null;
                  return Activator.CreateInstance(type) as T;
              }
      
              public void ExecuteMothod(string typeName, string methodName)
              {
                  if (_assembly == null) return;
                  var type = _assembly.GetType(typeName);
                  var obj = Activator.CreateInstance(type);
                  Expression<Action> lambda = Expression.Lambda<Action>(Expression.Call(Expression.Constant(obj), type.GetMethod(methodName)), null);
                  lambda.Compile()();
              }
          }
      

      為了更好的操作這個(gè)跨域訪問器,接下來我構(gòu)建了一個(gè)名為AssemblyDynamicLoader的類,它內(nèi)部封裝了RemoteLoader類

      的操作.

          public class AssemblyDynamicLoader
          {
              private AppDomain appDomain;
              private RemoteLoader remoteLoader;
              public AssemblyDynamicLoader()
              {
                  AppDomainSetup setup = new AppDomainSetup();
                  setup.ApplicationName = "ApplicationLoader";    
                  setup.ApplicationBase = AppDomain.CurrentDomain.BaseDirectory;  
                  setup.PrivateBinPath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "private");
                  setup.CachePath = setup.ApplicationBase;
                  setup.ShadowCopyFiles = "true"; 
                  setup.ShadowCopyDirectories = setup.ApplicationBase;
                  AppDomain.CurrentDomain.SetShadowCopyFiles();
                  this.appDomain = AppDomain.CreateDomain("ApplicationLoaderDomain", null, setup);
                  String name = Assembly.GetExecutingAssembly().GetName().FullName;
                  this.remoteLoader = (RemoteLoader)this.appDomain.CreateInstanceAndUnwrap(name, typeof(RemoteLoader).FullName);
              }
      
              public void LoadAssembly(string assemblyFile)
              {
                  remoteLoader.LoadAssembly(assemblyFile);
              }
      
              public T GetInstance<T>(string typeName) where T : class
              {
                  if (remoteLoader == null) return null;
                  return remoteLoader.GetInstance<T>(typeName);
              }
      
              public void ExecuteMothod(string typeName, string methodName)
              {
                  remoteLoader.ExecuteMothod(typeName, methodName);
              }
      
              public void Unload()
              {
                  try
                  {
                      if (appDomain == null) return;
                      AppDomain.Unload(this.appDomain);
                      this.appDomain = null;
                  }
                  catch (CannotUnloadAppDomainException ex)
                  {
                      throw ex;
                  }
              }
          }
      

      這樣我們每次都是通過AssemblyDynamicLoader類進(jìn)行跨域的訪問.

                  AppDomain.CurrentDomain.SetShadowCopyFiles();
                  this.appDomain = AppDomain.CreateDomain("ApplicationLoaderDomain", null, setup);
                  String name = Assembly.GetExecutingAssembly().GetName().FullName;
                  this.remoteLoader = (RemoteLoader)this.appDomain.CreateInstanceAndUnwrap(name, typeof(RemoteLoader).FullName);
      

      通過我們前面構(gòu)造的一個(gè)AppDomainSetup,構(gòu)建了一個(gè)我們所需的AppDomain,并且在這個(gè)appDomain中構(gòu)建了

      一個(gè)RemoteLoader類的實(shí)例(此時(shí)該實(shí)例已具備跨域訪問能力,也就是說我們在主域能獲取子域內(nèi)部的數(shù)據(jù)信息).目前RemoteLoader只提供了少數(shù)的幾個(gè)方法.

      跨域操作

      下面,我們就模擬一次"插件式"的跨域操作.首先我們構(gòu)造了一個(gè)窗體,其有以下元素.

      image

      選擇程序集路徑之后,加載程序集,然后就觸發(fā)程序集指定類型(通過配置獲取)的特定操作.這里我們定義了一個(gè)公共接口,它是所有"插件"操作的主要入口了.

          public interface IPlug
          {
              void Run();
          }
      

      隨后定義了一個(gè)實(shí)現(xiàn)該接口的類.

          [Serializable]
          public class ShowHelloPlug : IPlug
          {
              public void Run()
              {
                  MessageBox.Show("Hello World...");
              }
          }
      

      這個(gè)"插件"的工作很簡單.僅僅彈出一個(gè)對話框,說聲"Hello World…",接下來將其編譯成一個(gè)dll.

      image

      回到界面,選擇剛才編譯的Dll,然后直接加載.

      image

      到這里,我們的工作完成了一半了.呼呼.OK.我們的需求發(fā)生了變化,不再是彈出Hello World了.而時(shí)候彈出Hi,I'm Kinsen,我們修改剛才的子類,并再編譯一次.再將Dll替換剛才的Dll,這次,Dll沒有沒鎖定(因?yàn)槲覀兦懊鎲⒂昧薙hadowCopyFiles.).再加載一下程序集,你會(huì)發(fā)現(xiàn)結(jié)果并不是"Hi,I'm Kinsen",而是"Hello World.."為什么會(huì)這樣呢?這時(shí)候,借助SOS的力量(前面有SOS結(jié)果).

      我們發(fā)現(xiàn)Domain1(Default Domain)和Domain2(新創(chuàng)建Domain)都引用了程序集ShowHelloPlug.DLL,但是兩個(gè)引用的Dll地址卻不相同,這是因?yàn)閱⒂昧薙hadowCopyFiles,它們加載的都是各自程序集的備份,我們根據(jù)Domain2的Assembly地址查看ShowHelloPlug的編譯代碼.

      0:011> !dumpmt 00fc40ac     
      00fc40ac is not a MethodTable
      0:011> !dumpmd 00fc40ac     
      Method Name:  Plug.ShowHelloPlug.Run()
      Class:        046812b4
      MethodTable:  00fc40bc
      mdToken:      06000001
      Module:       00fc3adc
      IsJitted:     no
      CodeAddr:     ffffffff
      Transparency: Critical
      

      從IsJitted為no可以看出,該程序集并沒有被調(diào)用,那調(diào)用的是誰?我們再次查看Domain1(Default Domain

      )中的ShowHelloPlug.

      0:011> !dumpmd 001f8240      
      Method Name:  Plug.ShowHelloPlug.Run()
      Class:        004446e4
      MethodTable:  001f8250
      mdToken:      06000001
      Module:       001f7d78
      IsJitted:     yes
      CodeAddr:     00430de0
      Transparency: Critical
      

      已知每個(gè)AppDomain都有自己的堆棧信息,各自不互相影響,所以,當(dāng)我們在主域中獲取到了子域中的數(shù)據(jù),并非新建一個(gè)指向該實(shí)例的引用,而是在自己的堆棧上開辟出一塊空間"深度拷貝"該實(shí)例,那么必然就達(dá)不到我們我需的結(jié)果.

      子域內(nèi)部調(diào)用

      那么為了達(dá)到我們預(yù)期的效果,我們必須在子域內(nèi)部執(zhí)行我們所需的操作(調(diào)用),所以在RemoteLoader類中增加了一個(gè)Execute方法

              public void ExecuteMothod(string typeName, string methodName)
              {
                  if (_assembly == null) return;
                  var type = _assembly.GetType(typeName);
                  var obj = Activator.CreateInstance(type);
                  Expression<Action> lambda = Expression.Lambda<Action>(Expression.Call(Expression.Constant(obj), type.GetMethod(methodName)), null);
                  lambda.Compile()();
              }
      

      此處我暫時(shí)只想到了利用反射調(diào)用,這樣的代價(jià)就是調(diào)用所需消耗的資源更多,效率低下.目前還沒有

      想出較好的解決方案,有經(jīng)驗(yàn)的童鞋歡迎交流.

      這樣外部的調(diào)用就變成以下

      loader = new AssemblyDynamicLoader();
      loader.LoadAssembly(txt_dllName.Text);
      //var obj = loader.GetInstance<IPlug>("Plug.ShowHelloPlug");
      //obj.Run();
      loader.ExecuteMothod("Plug.ShowHelloPlug", "Run");
      

      現(xiàn)在在將Dll替換,結(jié)果正常.

      image

      尾聲

      做"插件式"開發(fā),除了利用AppDomain之外,也有童鞋給出了另一種解決方案,也就是在加載Dll的時(shí)候,先將Dll在內(nèi)存中復(fù)制一份,這樣原來的Dll也就不會(huì)被鎖定了.詳見插件的“動(dòng)態(tài)替換”.

      以上實(shí)例本人皆做過實(shí)驗(yàn),但可能還存在一定不足或概念錯(cuò)誤,若有不當(dāng)之處,歡迎各位童鞋批評指點(diǎn).

      更多

      通過應(yīng)用程序域AppDomain加載和卸載程序集

      什么是的AppDomain

      主站蜘蛛池模板: japanese边做边乳喷| 日产精品久久久久久久| 日韩大片高清播放器| 亚洲天堂亚洲天堂亚洲色图| 欧美一级高清片久久99| 国产精品久久久久无码网站| 日韩无矿砖一线二线卡乱| 欧美喷潮最猛视频| 久久天天躁狠狠躁夜夜躁2o2o| 国产黄色看三级三级三级| 日韩亚av无码一区二区三区 | 日韩激情无码免费毛片| 国产日韩精品欧美一区灰 | 鲜嫩高中生无套进入| 亚洲婷婷综合色香五月| 国产日韩综合av在线| 确山县| 99久久精品久久久久久婷婷| 国内精品视频一区二区三区| 少妇人妻偷人精品免费| 国产成人亚洲精品在线看| 久久人与动人物a级毛片 | 亚洲中文字幕乱码一区| 在线国产精品中文字幕| 精品人妻人人做人人爽夜夜爽| 蜜臀av性久久久久蜜臀aⅴ麻豆| 亚洲中文字幕有综合久久| 中文字幕在线视频不卡一区二区| 成人啪啪高潮不断观看| 亚洲精品日本一区二区| 亚洲色精品vr一区区三区| 丰满人妻熟妇乱又仑精品| 国产精品午夜福利精品| 哈巴河县| 色猫咪av在线观看| 强伦姧人妻免费无码电影| 中文字幕在线国产精品| 国产av一区二区麻豆熟女| 久久人妻无码一区二区三区av| 亚洲国产成人va在线观看天堂| 国产AV影片麻豆精品传媒|