亚洲香蕉成人av网站在线观看_欧美精品成人91久久久久久久_久久久久久久久久久亚洲_热久久视久久精品18亚洲精品_国产精自产拍久久久久久_亚洲色图国产精品_91精品国产网站_中文字幕欧美日韩精品_国产精品久久久久久亚洲调教_国产精品久久一区_性夜试看影院91社区_97在线观看视频国产_68精品久久久久久欧美_欧美精品在线观看_国产精品一区二区久久精品_欧美老女人bb

首頁 > 編程 > Delphi > 正文

用Delphi建立通訊與數據交換服務器—Transceiver技術剖析(下)

2019-11-18 18:23:34
字體:
來源:轉載
供稿:網友
Delphi建立通訊與數據交換服務器—Transceiver技術剖析(下)
作者:火鳥 redbirdli@hotmail.com
二、           Transceiver Service詳解
1.Transceiver Service分析概要
Transceiver Service是Transceiver系統的核心構成,Transceiver Kernel負責從系統配置庫讀取Transceiver Console設定的Port、Channel定義與參數,運行時動態創建和管控通訊Port及其關聯關系,對數據的收、發、緩沖進行調度、對日志、隊列進行管理等。Transceiver Shell則是所支持全部類型的用于數據收發的Port的實現。
2.Transceiver Service設計概要
Transceiver Service是由Delphi中Service application開發而成,Service Application可運行于系統態而非用戶態,由操作系統Service Control Manager (SCM)負責程序的運行管理,Service沒有用戶界面,屬于系統的后臺程序。Transceiver Kernel是Transceiver類的一系列對Transceiver Shell建立和控管的方法,而Transceiver Shell則是一系列負責通訊的對象集合。
注:由于性能和負載的考慮,Transceiver Kernel只是從邏輯上實現上架構圖中的功能劃分,構成模塊并未以完全對象化的方式實現。
3.Transceiver Service實現概要
                       i.              建立一個Service Application
從Delphi主菜單File中選擇NEW|Other…在彈出的New Items對話框中選擇NEW|Service Application ,可以看到生成的程序框架如下:
PRogram Project1;
uses
  SvcMgr,
  Unit1 in 'Unit1.pas' {Service1: TService};
{$R *.RES}
begin
  Application.Initialize;
  Application.CreateForm(TService1, Service1);
  Application.Run;
end.
 
 
unit Unit1;
interface
uses
  Windows, Messages, SysUtils, Classes, Graphics, Controls, SvcMgr, Dialogs;
type
  TService1 = class(TService)
  private
    { Private declarations }
  public
    function GetServiceController: TServiceController; override;
    { Public declarations }
  end;
var
  Service1: TService1;
implementation
{$R *.DFM}
procedure ServiceController(CtrlCode: DWord); stdcall;
begin
  Service1.Controller(CtrlCode);
end;
function TService1.GetServiceController: TServiceController;
begin
  Result := ServiceController;
end;
end.
可以看到除了在uses單元引用了用于服務管理的SvcMgr、TService1繼承自TServiced而非TForm及一個重載的GetServiceController函數和以stdcall方式調用的ServiceController過程之外,用Delphi建立一個服務程序并沒有太多特別之處,Delphi Fans也許又要歡呼了,這就是Delphi RAD的強大迷人之處。另外,Service Application由于無法直接在運行時調試,也沒有用戶界面,開發時應考慮調試信息的無界面輸出以利于調試排錯。
                     ii.              創始滿足特定需求的Port類
要使用運行處理機制統一的Transceiver Kernel,就要求Transceiver Shell中的Port有統一的處理規則,Shell中有些Port是Delphi開發環境中已有的組件類(如TCP、FTP等),而有些則不是(如MSMQ、File等)這時就需要自己動手建立一個可以滿足需要的類。如:
type//由于沒有用戶界面,所以繼承自TComponent而非TControl
   TFilePort=class(TComponent)
   private
       FilePath:string;//獲取或保存文件的文件夾位置
       Prefix:string;//文件前綴
       suffix:string;//文件后綴
   end;
建立TFilePort類以后,Transceiver Kernel就可以使用統一的類處理方式引用和管理對象,達到從FilePath指定的文件夾下存取特定文件的目的。如果用于信源(Source),將從特定文件夾下獲取滿足條件的文件,如果用于信宿(Target),將把從相應信源(Source)得到的數據寫入到指定文件中(事實上每一個Port對象的實際參數都來源于系統配置庫中Port表的定義)。
另一個例子:
type
   TCOMPort=class(TComponent)
   private
       ComFace:string;//獲取或提交數據的COM接口
   end;
TCOMPort將用于從指定COM組件接口中獲取數據或將數據提交到指定的COM組件接口上進行后續處理。在Delphi中OleVariant類是實現COM組件調用的途徑之一,使用TCOMPort類的必要性在于,Transceiver在必要的數據存取時才會將TCOMPort定義的COM接口實例化為OleVariant對象,使用結束即釋放對象,這樣能減少Transceiver和COM服務器的負載壓力。其它類似組件也有相同考慮。作者此處的類舉例只是一種模型,必要時應加入適當的方法與事件。在開發中作者實現的類有:TCOMPort、TMSMQPort、TDBPort、TFilePort等
                           iii.              多Channel的支持—聲明Port的對象數組
Transceiver把一個通訊過程看作是源(Source)到目標(Target)的數據流過程,這樣一個過程是Transceiver中的一個Channel,而這個Channel又是由至少兩個Port構成的(一個用于Source,一個用于Target),所以要定義不定數量并且Source、Target自由組合的多個Channel,必須分別聲明用于Source 和Target 的多種Port類的對象數組(并為他們建立對應的關聯關系,稍后您將看到)。如:
  private
    { Private declarations }
TCPSource:array of TServerSocket;// 用于TCP Source的對象數組
TCPTarget:array of TClientSocket;//用于TCP Target的對象數組
    MailSource:array of TIdPOP3;  //用于Mail Source的對象數組
MailTarget:array of TIdSMTP;  //用于Mail Target的對象數組
fileSource:array of TFilePort;   //用于File Source的對象數組
    fileTarget:array of TFilePort;   //用于File Target的對象數組
    comSource:array of TCOMPort;//用于COM Source的對象數組
comTarget:array of TCOMPort; // 用于COM Target的對象數組
注:由于同一類型的用于Source和Target的Port運行規則的也完全不同,在Transceiver概念中被視為是完全不同并且無直接關系的對象。所以同一類型的Port,對象數組也按Source和Target分別建立。
                          iv.              運行時實例化對象數組
每一個對象數組的元素個數由Port Builder在運行時管理,如果用戶通過Transceiver Console定義了一些某種類型的Port,Port Builder將按照其個數和各自參數實例化該對象數組。否則,該對象數組將不會被實例化。在Source類型的Port對象中,Name屬性被設置為'Receive'+Port ID 的形式,在之后的數據接收觸發中,這將有助于Data Dispatcher定位對象和對不同類型的Port對象進行統一調度。Tag屬性被用來向Channel Controller提供其所在Channel的target ID信息。
以下是Port Builder中對comSource對象數組的實例化部分
     begin //Create COM/ Receive Port
       itmp:=high(comSource)+1;
// 獲取comSource的當前最大個數,itmp為integer變量
SetLength(comSource,itmp+1); // 添加一個comSource數組成員
       comSource [itmp]:=TCOMPort.Create(self);// 實例化成員
comSource[itmp].Name:= 'Receive'+inttostr(isource);
//設置Name屬性為'Receive'+Port ID,isource為整型的當前PortID
       comSource [itmp].Tag:= itarget;//設置為其所在Channel的target ID
       NullTest:=rece.Fields['Address'].value;
//得到系統配置COMFace的值,NullTest為Variant變量
       if (NullTest <>null) and (trim(NullTest)<>'') then
             begin
comSource [itmp].ComFace:=NullTest; //將有效值賦與ComFace
NullTest:=rece.Fields['interval'].value;
//得到系統配置中COM對象獲取數據的觸發時間間隔
SetTimer(application.handle,isource,NullTest*60000,nil);
//為當前Port建立用于定時收取數據的觸發時鐘, isource為Port ID
end
else
 comSource [itmp].Tag:=-1;//初始化失敗,標識為無效Port
    end;
comSource是用于在一定的時間間隔后對ComFace中定義的接口進行調用并獲取數據的Source類Port,相應comTarget的實現與其類似,只是由于向comTarget的ComFace提交數據是一個實時過程,所以不需要用到觸發間隔,省略建立時鐘的兩條語句即可。其它類型的Port對象創建和初始化大同小異。如,另一個MailTarget實現片段:
              begin //Create SMTP/Send Port
                itmp:=high(MailTarget)+1;
                SetLength(MailTarget,itmp+1);
                MailTarget[itmp]:=TIdSMTP.Create(self);
                MailTarget[itmp].Name:=’send’+ inttostr(itarget);
                MailTarget[itmp].Tag:=3;// 設置為Target Port類型標識
                NullTest:=rece.Fields['Address'].value; //郵件服務器地址
                if (NullTest <>null) and (trim(NullTest)<>'') then
                                        MailTarget[itmp].Host :=NullTest
 else  bValid:=false;
                NullTest:=rece.Fields['Port'].value; //郵件服務器端口
                if NullTest <>null  then
 (if NullTest<>0 then MailTarget[itmp].Port :=NullTest)
 else  bValid:=false;
                NullTest:=rece.Fields['user'].value;//登錄用戶名
                if NullTest <>null then
 MailTarget[itmp].UserId :=NullTest
else  bValid:=false;
                NullTest:=rece.Fields['password'].value;//登錄口令
                  ……………
                                 ……………
              end;
或許你會有這樣的疑惑,大量的Transceiver Shell通訊組件在運行時被Port Builder創建,Transceiver Service的性能會高嗎?事實上,Port Builder的使命是在ServiceCreate事件發生時一次性完成的,Shell Port的數目只會影響Transceiver Service的初始化速度,Shell Port的通訊速度和Transceiver Servicer的整體性能將不受影響,當然系統資源可能會占用更多一些。
                            v.              事件的動態分配和處理
在Transceiver Shell所支持的若干種通訊Port當中,使用TServerSocket(可能您更傾向于使用Indy的通訊組件,但這并不違背Transceiver Service的設計思想,只是Shell層面的修改或增加而已)實現的TCPSource是比較有特點的一種,因為TServerSocket作為一種Source Port,不同于COM或POP3之類需要定時觸發的對象,它是在Transceiver Service啟動后時刻處于監聽狀態,當有ClientSocket連接并發送數據時產生相應事件的組件。以下是TCPSource的實例化片段:
begin //Create TCP/Receive Port
  itmp:=high(TCPSource)+1;
 SetLength(TCPSource,itmp+1);
  TCPSource [itmp]:=TServerSocket.Create(self);
  TCPSource [itmp].OnClientRead:=TCPServersClientRead;
//分配OnClientRead事件的處理過程為TCPServersClientRead
  TCPSource [itmp].OnClientError:=TCPServerClientError;
//分配OnClientError事件的處理過程為TCPServerClientError
TCPSource [itmp].Name:= 'Receive'+inttostr(isource);
//設置Name屬性為'Receive'+Port ID
  TCPSource [itmp].Tag:=itarget; //設置為其所在Channel的target ID
TCPSource [itmp].Socket.Data:=@ TCPSource [itmp].Tag;
//將此Port對象的target ID作為指針數據附于Socket對象上
     ……………
     ……………
end;
回來接著看我們的comSource的處理,在實例化時我們為其建立了觸發時鐘,但如何來處理時鐘觸發時的事件呢?同理,也是事件處理的動態分配。
comSource的時鐘的處理定義可在ServiceCreate事件處理中加入:  application.OnMessage:=Timer;
實現對消息處理的重載,當有Application的消息產生時,Timer就將被觸發,在Timer事件中我們過濾處理時鐘觸發的WM_TIMER消息,就可以按Port ID和類型實現對特定Source Port的數據獲取方法的調用:
Procedure TCarrier.Timer(var Msg: TMsg; var Handled: Boolean);
var  stmp:string;
     Obj:TComponent;
begin
  if Msg.message =WM_TIMER then//處理時鐘消息
  begin//根據觸發消息的Port ID找到定義此消息的對象
Obj:=FindComponent('Receive'+inttostr(Msg.WParam));
    if obj=nil then exit;//沒有找到就退出處理
    stmp:=obj.ClassName;//反射獲得此Port對象的類型信息
    if stmp='TIdPOP3' then GetPOP3(TIdPOP3(Obj));
    if stmp='TIdFTP' then GetFTP(TIdFTP(obj));
    if stmp='TFilePort' then GetFile(TFilePort(Obj));
if stmp='TCOMPort' then GetCOM(TCOMPort(Obj));
//調用COMSource的數據獲取過程
     ……………
     ……………
  end;
end;
                          vi.              獲取數據
以下是COMSource的數據獲取處理
procedure TCarrier.GetCOM(COMObj: TCOMPort);
var stmp:string;
    COMInterface:OleVariant;
begin
    try//根據ComFace的值建立COM組件對象
      COMInterface:=CreateOleObject(COMObj.ComFace);
      stmp:=COMInterface.GetData; //調用約定的接口方法,獲取數據
      while stmp<>#0 do  // #0為約定的數據提取結束標志
      begin
         DataArrive(stmp,COMObj.Tag);
//交由data Dispatcher統一處理, COMObj.Tag為對象所在Channel的Target Port ID
         stmp:=COMInterface.GetData;
      end;
      COMInterface:= Unassigned;
    except
      COMInterface:= Unassigned;
    end;
end;// 完成數據提取操作,釋放組件對象,直至下一次觸發調用
以下是TCPSource的數據獲取處理:
procedure TCarrier.TCPServersClientRead(Sender: TObject; Socket:TCustomWinSocket);
begin
DataArrive(socket.ReceiveText,integer(TServerWinSocket(sender).data^));
//交由data Dispatcher統一處理, 第二個參數為附于Socket對象sender上的Target Port ID指針值,
end;
不同類型的Source Port對象其接收數據的方式也不盡相同,但最終都將所接收到的數據交由data Dispatcher做統一處理。從實現層面講,每加入一種數據接收對象并實現其數據接收,就為Transceiver Shell實現了一種新的Source Port。注:此處作者只是實現了接收文本數據,可能用戶需要接收的是內存對象、數據流或二進制數據,對接收代碼稍做更改即可。
                         vii.              數據調度
Transceiver Service的數據調度是由data Dispatcher邏輯單元完成的,Data Dispatcher的主要任務是對從不同的Source Port接收到的數據進行統一的管理與控制、與Channel Controller協同工作,按Channel的定義向不同的Target Port進行數據分發、監視其發送結果成功與否,并根據發送結果和系統配置庫的設置決定數據是否需要提交到Queue Manager和Log Recorder進行緩沖和日志處理等等。接下來看看Source Port提交數據的DataArrive方法:
procedure TCarrier.DataArrive(sData:String;PortID:Integer);
var dTime:Datetime;
    iLogID:integer;
    bSendSeccess:Boolean;
begin
  if sData='' then exit;//如數據為空則跳出
            iLogID:=-1;
  dTime:= now;   //接收時間
  if sData[length(sdata)]=#0 then sdata:=copy(sdata,1,length(sdata)-1);
//用于兼容C語言的的字符串格式
bSendSeccess:=DataSend(sdata,PortID) ;
//調用 Data Dispatcher發送調度方法,PortID為Target Port ID
if (TSCfg.LogOnlyError=false) or (bSendSeccess=false)  then
 iLogID:=writeLog(dTime, now,sData, PortID, bSendSeccess);
//根據系統配置信息中的日志處理規則和發送結果記錄日志
    if  (TSCfg.Queueing=True) and (bSendSeccess=false)  then
          PutQueue(dTime, now,sData, PortID, bSendSeccess, iLogID);
   //根據封裝系統配置信息中Queue配置定義決定Queue處理
end;
以上是Data Dispatcher的DataArrive方法,其中Queue的處理是按照系統配置信息和發送狀態決定的,也可以調整為強制性的隊列化處理。下面是Data Dispatcher的DataSend方法,用于將數據按Target Port類型分發處理:
Function TCarrier.DataSend(sData:String;PortID:Integer):boolean;
var Obj:TComponent;
begin
  DataSend:=false;
Obj:=FindComponent('Send'+inttostr(PortID)); //根據Port ID找到對象
  if (obj=nil) or (obj.Tag =-1) then exit;
//對象不存在或因初始化失敗已被標識為無效Port
  case obj.Tag of
  1:DataSend:=PutTCP(TClientSocket(obj),sdata);
  3:DataSend:=PutSMTP(TIdSMTP(obj),sdata);
  5:DataSend:=PutFTP(TIdFTP(obj),sdata);
  7:DataSend:=PutHTTP(TIdHTTP(obj),sdata);
  9:DataSend:=PutFile(TFilePort(obj),sdata);
  11:DataSend:=PutMSMQ(TMSMQPort (obj),sdata);
  13:DataSend:=PutDB(TDBPort(obj),sdata);
  15:DataSend:=PutCOM(TCOMPort (obj),sdata);
     ……………
     ……………
  end;
end;
值得注意的是,如果沒有使用對象數組,而是每種類型的Port只有一個實例的話,處理數據分發處理的更佳辦法應該是使用回調(Callback)函數,但在現在的情況下,那將導致不知應該由對象數組中哪一個成員處理數據。另外,現在的處理方法使Transceiver Kernel與Transceiver Shell沒有徹底剝離,應該尋求更加抽象、獨立性好的處理方法。
                       viii.              數據發送
以下是TCP的發送
Function TCarrier.PutTCP(TCPOBJ:TClientSocket;sdata:string):Boolean;
var itime:integer;
begin
  PutTCP:=false;
  try
    TCPOBJ.Close;
    TCPOBJ.Open;
    itime:=gettickcount;//起始時間
    repeat
      application.ProcessMessages;
    until (TCPOBJ.Active=true) or (gettickcount-itime>5000);
     //連接成功或5秒超時就跳出循環
    if TCPOBJ.Active then
    begin
      TCPOBJ.Socket.SendText(sdata);
      PutTCP:=true;//發送數據成功時,返回值才為True
end;
TCPOBJ.Close;
  Except
TCPOBJ.Close;
  end;
  end;
以下是COM的發送
Function TCarrier.PutCOM(COMOBJ:TCOMPort;sdata:string):Boolean;
var Com:OleVariant;
begin
  PutCOM:=false;
  try
    Com:=CreateOleObject(COMOBJ.ComFace);//建立預定義的接口
PutCOM:=Com.PutData(sdata);//調用預定義的方法
Com:= Unassigned;
  except
Com:= Unassigned;
  end;
end;
其它類型的Port發送大同小異,在此不再贅述。到此為止,Source和Target的基本處理已經完成。一個基本的通訊功能已經建立,經過不同類型的Source和Target的自由匹配,就可以實現完全不同的通訊功能。建立多個Channel,就可以集中實現多個不同功用的通訊處理。
                          ix.              隊列處理
在上文的DataArrive方法中當數據被發送之后,Data Dispatcher會調用數據日志記錄的writeLog和隊列化處理的PutQueue方法,二者的功能類似,都是根據系統參數對數據信息進行數據庫的存儲,不是本文的重點。而隊列的Retry處理與Timer事件中按Port類型分發處理的原理類似,是依賴于Queue Timer的觸發,將緩沖的數據從數據庫中讀出,并依照Target Port ID再次調用DataSend進行數據的發送重試,如發送成功,則本次數據傳輸的事務完成,否則重新進入隊列等待下一次觸發時間進行重試,直到發送成功或達到設置的最大重試數為止。
三、            開發經驗總結
由于本文的側重點在于說明Transceiver的核心思想與設計理念,簡化和削弱了Transceiver作為后臺服務應當考慮的多線程處理、對象池化以及事務支持、更為復雜強大的Source和Target的Group管理和Channel集成、收發內存對象、數據流、二進制數據的能力、系統配置信息的讀取和其封裝類的實現、系統及數據的安全性等等,希望讀者朋友們能夠拋磚引玉,理解Transceiver的設計思想,啟發實際開發工作中的靈感火花,做出更加出色強大的軟件。
 
 
作者:火鳥 redbirdli@hotmail.com
 

上一篇:在Delphi中使用動態圖標

下一篇:Delphi例程-文件管理例程(1~15)

發表評論 共有條評論
用戶名: 密碼:
驗證碼: 匿名發表
學習交流
熱門圖片

新聞熱點

疑難解答

圖片精選

網友關注

亚洲香蕉成人av网站在线观看_欧美精品成人91久久久久久久_久久久久久久久久久亚洲_热久久视久久精品18亚洲精品_国产精自产拍久久久久久_亚洲色图国产精品_91精品国产网站_中文字幕欧美日韩精品_国产精品久久久久久亚洲调教_国产精品久久一区_性夜试看影院91社区_97在线观看视频国产_68精品久久久久久欧美_欧美精品在线观看_国产精品一区二区久久精品_欧美老女人bb
欧美日产国产成人免费图片| 人人澡人人澡人人看欧美| 久久69精品久久久久久久电影好| 欧洲日韩成人av| 97国产精品免费视频| 日韩精品视频免费在线观看| 日韩福利在线播放| 亚洲天堂视频在线观看| 欧美国产日韩免费| 亚洲第一精品电影| 91在线免费观看网站| 国产99久久精品一区二区 夜夜躁日日躁| 久久福利视频导航| 日韩免费在线免费观看| 国产精品久久久久久久久免费| www国产精品视频| 欧美日韩在线观看视频| 欧美成人小视频| 精品爽片免费看久久| 最新国产成人av网站网址麻豆| 国产精品高潮在线| 国产视频在线一区二区| 国产精品久久二区| 91精品国产自产在线老师啪| 亚洲一二在线观看| 日韩av在线一区| 国产人妖伪娘一区91| 欧美亚洲视频一区二区| 亚洲精品xxx| 成人妇女免费播放久久久| 韩曰欧美视频免费观看| 亚洲欧美999| 欧美韩国理论所午夜片917电影| 日本a级片电影一区二区| 岛国av一区二区在线在线观看| 91高清视频免费观看| 国产精品劲爆视频| 视频在线一区二区| 欧美精品videossex性护士| 亚洲自拍高清视频网站| 高清欧美一区二区三区| 亚洲国产97在线精品一区| 日韩大片免费观看视频播放| 黄网站色欧美视频| 成人www视频在线观看| 亚洲人成网站777色婷婷| 亚洲人精品午夜在线观看| 永久555www成人免费| 中文字幕日韩精品有码视频| 久久久中精品2020中文| 国内精品久久久久影院优| 中文字幕成人精品久久不卡| 日韩在线视频网站| 亚洲白拍色综合图区| 色香阁99久久精品久久久| 精品成人乱色一区二区| 亚洲韩国欧洲国产日产av| 日韩在线视频免费观看高清中文| 日韩精品视频在线观看网址| 日本精品免费一区二区三区| 欧美巨大黑人极品精男| 日韩av手机在线看| 亚洲男人天堂网| 国产亚洲精品久久久久久777| 国产一区二区三区在线免费观看| 久久精品国产久精国产一老狼| 国产精品日韩在线| 久久在精品线影院精品国产| 欧美视频精品一区| 97在线视频精品| 亚洲午夜精品久久久久久久久久久久| 国产精品视频导航| 国产日韩视频在线观看| 亚洲免费电影在线观看| 777777777亚洲妇女| 国产精品精品国产| 亚洲色图第三页| 宅男66日本亚洲欧美视频| 红桃视频成人在线观看| 国产精品中文字幕久久久| 欧美精品18videos性欧美| www国产精品视频| 91精品啪aⅴ在线观看国产| 黄色精品在线看| 亚洲欧美日韩国产中文| 国产精品久久久久高潮| 欧美成人小视频| 性欧美xxxx| 狠狠久久亚洲欧美专区| 欧美成人精品不卡视频在线观看| 7777免费精品视频| 亚洲精品xxxx| 国产精品中文久久久久久久| 综合欧美国产视频二区| 欧美大尺度电影在线观看| 欧美精品在线网站| 久久久久久久电影一区| 人妖精品videosex性欧美| 亚洲精品97久久| 国产中文字幕91| 久久久久久久久久国产精品| 亚洲国产欧美一区二区三区久久| 国产乱肥老妇国产一区二| 国产精品激情自拍| 97视频人免费观看| 欧美一级bbbbb性bbbb喷潮片| 欧美特黄级在线| 久久精品国产电影| 一本色道久久88综合亚洲精品ⅰ| 午夜精品久久久久久久99黑人| 日韩国产欧美区| 国产日本欧美一区二区三区在线| 日本高清+成人网在线观看| 在线观看精品自拍私拍| 日韩欧美在线字幕| 欧美日本国产在线| 福利视频一区二区| 欧美性色xo影院| 久久深夜福利免费观看| 俺去了亚洲欧美日韩| 日韩精品视频中文在线观看| 国产一区二区成人| 国产精品激情av电影在线观看| 伊人久久大香线蕉av一区二区| 日韩欧美在线国产| 午夜精品久久久久久99热软件| 成人在线激情视频| 亚洲视频在线观看网站| 亚洲国产精彩中文乱码av在线播放| 伊人激情综合网| 久久99精品视频一区97| 韩国日本不卡在线| 日韩av在线高清| 韩国精品美女www爽爽爽视频| 亚洲一区二区黄| 日韩有码片在线观看| 亚洲午夜色婷婷在线| 亚洲精品色婷婷福利天堂| 欧美大片大片在线播放| 日韩中文字幕国产精品| 亚洲影院色在线观看免费| 日韩中文综合网| 亚洲视频欧美视频| 亚洲人精选亚洲人成在线| 国产欧美 在线欧美| 久久伊人91精品综合网站| 国产精品高清在线观看| 成人一区二区电影| 成人啪啪免费看| 麻豆国产精品va在线观看不卡| 欧美尺度大的性做爰视频| 不卡av在线网站| 欧美—级a级欧美特级ar全黄| 最近中文字幕日韩精品| 欧美精品videossex88| 久久99精品国产99久久6尤物| 国产日本欧美一区二区三区| 欧美性猛交xxxx免费看漫画| 亚洲国产毛片完整版| 国产精品av在线| 色青青草原桃花久久综合| 亚洲国产精品va在看黑人| 精品日本高清在线播放| 成人国产精品日本在线|