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

首頁 > 學院 > 開發設計 > 正文

[連載]《C#通訊(串口和網絡)框架的設計與實現》-5.串口和網絡統一IO設計

2019-11-14 13:44:36
字體:
來源:轉載
供稿:網友

目       錄

第五章           串口和網絡統一IO設計... 2

5.1           統一IO接口... 2

5.1.1    串口IO.. 4

5.1.2    網絡IO.. 7

5.1.3    擴展應用... 12

5.2           IO管理器... 12

5.2.1    串口I O管理器... 13

5.2.2    網絡IO管理器... 15

5.2.2.1   網絡偵聽... 16

5.2.2.2   連接遠程服務器... 17

5.2.2.3   互斥操作... 18

5.3           小結... 19

 

第五章     串口和網絡統一IO設計

     作為通訊框架平臺軟件,IO是核心部分之一,涉及到與硬件設備、軟件之間的信息數據交互,主要包括兩部分:IO實例與IO管理器。IO實例負責直接對串口和網絡進行操作;IO管理器負責對IO實例進行管理。

     受應用環境的影響,IO操作過程中的確出現過一些問題,有些問題的解決也費了好長時間。并不是解決問題有多困難,而是無法確定到底是什么原因引起的。經過不斷的完善,IO部分才逐漸穩定下來。

5.1    統一IO接口

    框架平臺一大特點就是開發一套設備驅動(插件)同時支持串口和網絡兩種通訊方式,而兩種通訊方式的切換只需要改動配制文件。

    不同的設備類型和協議、不同的通訊方式,用堆代碼的方式進行開發,根本無法適應不同場景的應用,提高了代碼的維護成本,以及修改代碼可能造成潛在的BUG,是讓人很頭疼的一件事。

    在開始設計框架平臺的時候,一個核心的思想就是把變的東西要設計靈活,把不變的東西設計穩定。對于設備的協議就是變的東西,對于IO部分就是相對不變的東西,那就需要對串口IO和網絡IO進行整合。不僅在代碼層面要運行穩定;在邏輯層面,不管是串口IO還是網絡IO在框架內部是統一的接口,所有對IO的操作都會通過這個統一的接口來完成。

     統一的IO接口代碼如下:

public interface IIOChannel:IDisposable{       /// <summary>       /// 同步鎖       /// </summary>       object SyncLock { get; }       /// <summary>       /// IO關鍵字,如果是串口通訊為串口號,如:COM1;如果是網絡通訊為ip和端口,例如:127.0.0.1:1234       /// </summary>       string Key { get; }       /// <summary>       /// IO通道,可以是COM,也可以是SOCKET       /// </summary>       object IO{get;}        /// <summary>       /// 讀IO;       /// </summary>       /// <returns></returns>       byte[] ReadIO();       /// <summary>       /// 寫IO       /// </summary>       int WriteIO(byte[] data);       /// <summary>       /// 關閉       /// </summary>       void Close();       /// <summary>       /// IO類型       /// </summary>       CommunicationType IOType { get; }        /// <summary>       /// 是否被釋放了       /// </summary>       bool IsDisposed { get; }}

     串口IO和網絡IO都繼承自IIOChannel接口,完成特定的IO通訊操作。繼承關系圖如下:

 

5.1.1    串口IO

     原來串口IO操作使用是的MS自帶的SerialPort組件,但是這個組件與一些小眾工業串口卡不兼容,操作的時候出現異常"參數不正確"的提示。SerialPort組件本身是對Win32 API的封裝,所以分析應該不是這個組件本身的問題。有網友反饋,如下圖:

 

     但是,從解決問題的成本角度來考慮,從軟件著手解決是成本最低的、效率最高的?;谶@方面的考慮,使用MOXA公司的PCOMM.DLL組件進行開發,并沒有出現類似的問題。所以,在代碼重構中使用了PCOMM.DLL組件,并且運行一直很穩定。

     針對串口IO操作比較簡單,主要是實現了ReadIO和WriteIO兩個接口,代碼如下:

public class sessionCom : ISessionCom{       ......       public byte[] ReadIO()       {              if (_ReceiveBuffer != null)              {                     int num = InternalRead(_ReceiveBuffer, 0, _ReceiveBuffer.Length);                     if (num > 0)                     {                            byte[] data = new byte[num];                            Buffer.BlockCopy(_ReceiveBuffer, 0, data, 0, data.Length);                            return data;                     }                     else                     {                            return new byte[] { };                     }              }              else              {                     return new byte[] { };              }       }       public int WriteIO(byte[] data)       {              int sendBufferSize = GlobalPRoperty.GetInstance().ComSendBufferSize;              if (data.Length <= sendBufferSize)              {                     return this.InternalWrite(data);              }              else              {                     int successNum = 0;                     int num = 0;                     while (num < data.Length)                     {                            int remainLength = data.Length - num;                            int sendLength = remainLength >= sendBufferSize                                   ? sendBufferSize                                   : remainLength;                            successNum += InternalWrite(data, num, sendLength);                            num += sendLength;                     }                     return successNum;              }       }       ......}

      針對ReadIO接口函數,可以有多種操作方式,例如:讀固定長度、判斷結尾字符、一直讀到IO緩存為空等。讀固定長度,如果偶爾出現通訊干擾或丟失數據,這種方式會給后續正確讀取數據造成影響;判斷結尾字符,在框架內部的IO實現上又無法做到通用性;一直讀到IO緩存為空,如果接收數據的頻率大于從IO緩存讀取的頻率,那么會阻塞輪詢調度線程?;诙喾矫娴目紤],現場環境往往比想象的要復雜,在設置讀超時的基礎上,讀一次就返回了。

      還要考慮到現場實際的應用環境,例如:USB形式的串口容易松動,造成不穩定;9針串口損壞等情況。所以,有可能因為硬件環境改變引起無法正常對IO進行操作,這時候會通過TryOpen接口函數試著重新打開串口IO;另外,串口參數發生改變時,通過IOSettings接口函數重新配置參數。

5.1.2    網絡IO

      網絡IO通訊的本質是對Socket進行操作,框架平臺現在支持TCP方式進行通訊;工作模塊支持Server和Client兩種,也就是開發一套設備驅動可以支持Tcp Server和Tcp Client兩種數據交互方式?,F在不支持UDP通訊方式,將會在后續進行完善。

     發送和接收的代碼實現比較簡單,SessionSocket類中的ReadIO和WriteIO是用同步方式實現的;當并發通訊和自控通訊模式時,接收數據是用異步方式來完成的。當然,也可以使用完全的異步編程方式,使用SocketAsyncEventArgs操作類。SessionSocket操作代碼實現如下:

public class SessionSocket : ISessionSocket{       public byte[] ReadIO()       {              if (!this.IsDisposed)              {                     if (this.AcceptedSocket.Connected)                     {                            if (this.AcceptedSocket.Poll(10, SelectMode.SelectRead))                            {                                   if (this.AcceptedSocket.Available > this.AcceptedSocket.ReceiveBufferSize)                                   {                                          throw new Exception("接收的數據大于設置的接收緩沖區大小");                                   }                                   #region                                   int num = this.AcceptedSocket.Receive(this._ReceiveBuffer, 0, this._ReceiveBuffer.Length, SocketFlags.None);                                   if (num <= 0)                                   {                                          throw new SocketException((int)SocketError.HostDown);                                   }                                   else                                   {                                          this._NoneDataCount = 0;                                          byte[] data = new byte[num];                                          Buffer.BlockCopy(_ReceiveBuffer, 0, data, 0, data.Length);                                          return data;                                   }                                   #endregion                            }                            else                            {                                   this._NoneDataCount++;                                   if (this._NoneDataCount >= 60)                                   {                                          this._NoneDataCount = 0;                                          throw new SocketException((int)SocketError.HostDown);                                   }                                   else                                   {                                          return new byte[] { };                                   }                            }                     }                     else                     {                            throw new SocketException((int)SocketError.HostDown);                     }              }              else              {                    return new byte[] { };              }       }       public int WriteIO(byte[] data)       {              if (!this.IsDisposed)              {                     if (this.AcceptedSocket.Connected                            &&                            this.AcceptedSocket.Poll(10, SelectMode.SelectWrite))                     {                            int successNum = 0;                            int num = 0;                            while (num < data.Length)                            {                                   int remainLength = data.Length - num;                                   int sendLength = remainLength >= this.AcceptedSocket.SendBufferSize                                          ? this.AcceptedSocket.SendBufferSize                                          : remainLength;                                   SocketError error;                                   successNum += this.AcceptedSocket.Send(data, num, sendLength, SocketFlags.None, out error);                                   num += sendLength;                                   if (successNum <= 0 || error != SocketError.Success)                                   {                                          throw new SocketException((int)SocketError.HostDown);                                   }                            }                            return successNum;                     }                     else                     {                            throw new SocketException((int)SocketError.HostDown);                     }              }              else              {                     return 0;              }       }}

     ReadIO和WriteIO在操作過程中發生Socket失敗后會拋出SocketException異常,框架平臺捕捉異常后會對IO實例進行資源銷毀。重新被動偵聽或主動連接獲得Socket實例。

考慮到硬件,由PC機的網卡引起的網絡IO操作異常的可能比較??;但是,要考慮到連接到框架平臺的各類終端(客戶端)硬件設備,例如:DTU、無線路由、網絡轉換模塊等;還涉及到通訊鏈路,例如:GPRS、2G/3G/4G等;不同的硬件特性、不同的通訊鏈路,多種原因可能會造成通訊鏈路失效,例如:另外一端的程序不穩定、無法釋放資源等原因導致數據無法正常發送和接收;線路接頭虛接導致鏈路時好時壞導致發送和接收數據不穩定;網絡本身的原因出現Socket“假”連接的現象導致顯示發送數據成功,而另一端卻沒有收到等等。

     針對Socket通訊,原來在線程里定時輪詢IO實例,通過IO實例向另一端發送心跳檢測數據,如果發送失敗,立即釋放IO資源,這種操作方式的缺點是另一端會接收到一些冗余數據信息。重構時改變為另一種方式,對底層進行心跳在線檢測,當進行異步發送和接收數據的時候,如果鏈路出現問題,異步函數會立即返回,并返回結果顯示發送和接收0個數,對此進行判斷而銷毀IO實例資源。在初始化IO實例的時候,增加了對底層心跳檢測功能,代碼如下:

public SessionSocket(Socket socket){       uint dummy = 0;       _KeepAliveOptionValues = new byte[Marshal.SizeOf(dummy) * 3];       _KeepAliveOptionOutValues = new byte[_KeepAliveOptionValues.Length];       BitConverter.GetBytes((uint)1).CopyTo(_KeepAliveOptionValues, 0);       BitConverter.GetBytes((uint)(2000)).CopyTo(_KeepAliveOptionValues, Marshal.SizeOf(dummy));BitConverter.GetBytes((uint)(GlobalProperty.GetInstance().HeartPacketInterval)).CopyTo(_KeepAliveOptionValues, Marshal.SizeOf(dummy) * 2);       socket.IOControl(IOControlCode.KeepAliveValues, _KeepAliveOptionValues, _KeepAliveOptionOutValues);       socket.NoDelay = true;       socket.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.DontLinger, true);       ......}

     通過發送、接收拋出異常和底層心跳檢測兩種方式對Socket IO實例有效性進行檢測。對于正常通訊情況下的發送和接收操作很簡單,但是也要通過技術手段防止各種意外情況,從而影響框架平臺運行的穩定性。

對于通訊說簡單也簡單,說難也難,因應用場景和環境的原因難易程度不一樣。在網絡世界發展如火如荼的今天,網絡任務調度、分布式消息、大數據處理等無不涉及到多點與多點之間的信息交互,所以在通訊基礎上又發展出來各種協議、各種算法以及數據校驗等。

5.1.3    擴展應用

     把IO設計穩定,但是不代表沒有擴展的余地。在《3.設備驅動的設計》的“3.7 IO數據交互設計”中介紹了具體的應用。在調用IRunDevice設備驅動的Send和Receive接口時會把IO實例以參數的形式傳遞進來,在二次開發過程中可以重寫這兩個函數,開發特定的發送和接收業務。

    有網友問:串口通訊時,硬件設備一直在向軟件發送數據,軟件分析接收到的數據后進行數據處理,用SuperIO應該怎么實現?

    這種單向通訊方式也是存在的,框架設計前已經考慮到這類情況,具體實現步驟如下:

  1. 重寫IRunDevice設備驅動中的Send接口函數,直接return返回,不進行發送數據。
  2. 重寫IRunDevice設備驅動中的Receive接口函數,把接收上來的數據入到緩存里。
  3. 啟動IRunDevice設備驅動中的IsStartTimer 的定時器,在DeviceTimer中定時分析緩存里的數據并處理數據。
  4. 查到可用的數據,調用RunIODevice(byte[])驅動函數,其他的代碼不需要改動。

5.2    IO管理器

   

     IO管理器是對串口IO和網絡IO實例進行管理,他們都繼承自IIOChannelManager接口,但是各自的IO管理器的職能又有很大不同,網絡IO管理器更復雜一些。繼承關系結構圖如下:

5.2.1    串口I O管理器

     相對簡單的多,因為串口IO動態改變的幾率比較小,只是創建IO和關閉IO時通過事件反饋到串口監視窗體,主要代碼如下:

public class SessionComManager : IOChannelManager,ISessionComManager<string, IIOChannel>{       ......       /// <summary>       /// 建立并打開串口IO       /// </summary>       /// <param name="port"></param>       /// <param name="baud"></param>       /// <returns></returns>       public ISessionCom BuildAndOpenComIO(int port, int baud)       {              ISessionCom com = new SessionCom(port, baud);              com.TryOpen();              if (COMOpen != null)              {                     bool openSuccess = false;                     if (com.IsOpen)                     {                            openSuccess = true;                     }                     else                     {                            openSuccess = false;                     }                     COMOpenArgs args = new COMOpenArgs(port, baud, openSuccess);                     this.COMOpen(com, args);              }              return com;       }       /// <summary>       /// 閉關IO       /// </summary>       /// <param name="key"></param>       public override void CloseIO(string key)       {              ISessionCom com = (ISessionCom)this.GetIO(key);              base.CloseIO(key);              if (COMClose != null)              {                     bool closeSuccess = false;                     if (com.IsOpen)                     {                            closeSuccess = false;                     }                     else                     {                            closeSuccess = true;                     }                     COMCloseArgs args = new COMCloseArgs(com.Port, com.Baud, closeSuccess);                     this.COMClose(com, args);              }       }       ......}

5.2.2    網絡IO管理器

     網絡IO管理器相對復雜一些,涉及到Socket的動態連接和斷開,以及根據設備驅動設置的工作模式(Server或Client)切換對連接的處理方式。原來的時候,還負責通過線程定時對所有網絡IO實例進行心跳檢測,現在這部分被底層心跳檢測所替代。

5.2.2.1     網絡偵聽

       當偵聽并接收到遠程的連接實例后,會做兩件事:

  1. 判斷該連接實例的IP在設備管理器中的設備驅動是否設置為Client工作模式,如果是的話,那么則銷毀該資源實例,并退出當前事務。設備驅動設置的IP參數和客戶端的IP參數一致,但是兩端的工作模式又都為Client模式。也就是說在一個網絡內存在兩個相同的IP和相同的Client工作模式,又要讓他們之間進行通訊,這不符合C/S通訊的基本原理。所以,果斷拒絕這樣的連接并銷毀資源。

      2.判斷當前IO管理器是否存在相同的IP實例對象,如果存在,那么則銷毀該IP實例對象。因為有可能這個實例對象已失效,至少認為遠程的客戶端認為當前的連接已經失效。所以,既然這樣,我們雙方達成共識,果斷銷毀這樣的IP實例對象,接收新的IP連接實例。

   接收連接實例對象的代碼如下:

private void Monitor_SocketHanler(object source, AcceptSocketArgs e){       IRunDevice[] devs = DeviceManager.GetInstance().GetDevices(e.RemoteIP, WorkMode.TcpClient);       if (devs.Length > 0)       {              DeviceMonitorLog.WriteLog(String.Format("有設備設置{0}為Tcp Client模式,此IP不支持遠程主動連接", e.RemoteIP));              SessionSocket.CloseSocket(e.Socket);              return;       }       CheckSameSessionSocket(e.RemoteIP);       _ManualEvent.WaitOne(); //如果正在結束SOCKET操作,等待完成后再執行邊接操作        ISessionSocket socket = new SessionSocket(e.Socket);       SessionSocketConnect(socket);}

5.2.2.2     連接遠程服務器

     單獨開辟一個線程,獲得所有工作模式為Client的設備驅動,并檢測每一個設備驅動的通訊參數在IO管理器中是否存在相應的IO實例,如果不存在,那么則主動連接遠程的服務器,連接成功后把連接的IO實例入到IO管理器。

     實現的代碼如下:

private void ConnectTarget(){       while (true)       {              if (!_ConnectThreadRun)              {                     break;              }              IRunDevice[] devList = DeviceManager.GetInstance().GetDevices(WorkMode.TcpClient);              for (int i = 0; i < devList.Length; i++)              {                     try                     {                            if (!this.ContainIO(devList[i].DeviceParameter.NET.RemoteIP))                            {                                   ConnectServer(devList[i].DeviceParameter.NET.RemoteIP, devList[i].DeviceParameter.NET.RemotePort);                            }                     }                     catch (Exception ex)                     {                            devList[i].OnDeviceRuningLogHandler(ex.Message);                     }              }              System.Threading.Thread.Sleep(2000);       }}

5.2.2.3     互斥操作

    當有新的連接,在檢測是否有相同IP實例存在的時候,如果有相同IP實例存在,在銷毀資源未結束之前,不能把新連接的IP實例放到IO管理器。因為相同IP的兩個實例,一個在銷毀資源、一個在創建資源,有可能把新連接的IP實例一起銷毀掉。

    防止這種情況的出現,使用ManualResetEvent信號互斥進行狀態控制和改變,示意代碼如下:

public class SessionSocketManager : IOChannelManager, ISessionSocketManager<string, IIOChannel>{             /// <summary>       /// 初始狀態為終止狀態       /// </summary>       private ManualResetEvent _ManualEvent = new ManualResetEvent(true);       private void Monitor_SocketHanler(object source, AcceptSocketArgs e)       {              SessionSocketClose(e.RemoteIP);              _ManualEvent.WaitOne(); //如果正在結束SOCKET操作,等待完成后再執行邊接操作               ISessionSocket socket = new SessionSocket(e.Socket);              SessionSocketConnect(socket);       }        private void SessionSocketClose(string key)       {              this._ManualEvent.Reset(); //為非終止狀態              SessionSocket io = (SessionSocket)GetIO(key);              if (io != null)              {                     CloseIO(key);              }              this._ManualEvent.Set();//為終止狀態       }       private void SessionSocketConnect(ISessionSocket socket)       {              if (!this.ContainIO(socket.Key.ToString()))              {                     this.AddIO(socket.Key.ToString(), (IIOChannel)socket);              }       }}

5.3    小結

     IO這塊的設計的思想是一個負責執行一個負責管理,IO實例是具體通道操作,IO管理器負責對IO進行管理,并協調設備和IO之間的關系和工作。

 

作者:唯笑志在

Email:504547114@QQ.com

QQ:504547114

.NET開發技術聯盟:54256083

文檔下載:http://pan.baidu.com/s/1pJ7lZWf

官方網址:http://www.bmpj.net


發表評論 共有條評論
用戶名: 密碼:
驗證碼: 匿名發表
亚洲香蕉成人av网站在线观看_欧美精品成人91久久久久久久_久久久久久久久久久亚洲_热久久视久久精品18亚洲精品_国产精自产拍久久久久久_亚洲色图国产精品_91精品国产网站_中文字幕欧美日韩精品_国产精品久久久久久亚洲调教_国产精品久久一区_性夜试看影院91社区_97在线观看视频国产_68精品久久久久久欧美_欧美精品在线观看_国产精品一区二区久久精品_欧美老女人bb
97色在线观看| 亚洲最新av在线| 一色桃子一区二区| 欧美裸体视频网站| 日韩精品免费在线| 亚洲黄色av网站| 91精品国产自产在线观看永久| x99av成人免费| 91视频国产精品| 日韩精品中文字幕在线| 久久久久久美女| 福利视频第一区| 国产精品免费久久久久影院| 96精品久久久久中文字幕| 神马久久桃色视频| 91精品视频免费看| 成人免费看黄网站| 亚洲高清福利视频| 日韩中文字幕精品视频| 久热精品视频在线免费观看| 久久精品国产亚洲精品2020| 国产精品99久久久久久人| 91av网站在线播放| 在线视频欧美日韩| 日韩在线视频免费观看高清中文| 在线视频精品一| 国产日韩在线一区| 亚洲伦理中文字幕| 国产在线观看一区二区三区| 亚洲二区在线播放视频| 欧美性受xxxx白人性爽| 91免费看视频.| 久久久久久久爱| 久久大大胆人体| 大伊人狠狠躁夜夜躁av一区| 亚洲精品中文字幕女同| 亚洲午夜未满十八勿入免费观看全集| 久久伊人91精品综合网站| 国产成人综合一区二区三区| 日韩av片电影专区| 久久久久久久香蕉网| 欧美孕妇性xx| 色与欲影视天天看综合网| 91精品国产色综合久久不卡98| 成人欧美一区二区三区在线湿哒哒| 97久久久久久| 日韩一级裸体免费视频| 日韩av在线直播| 久久97久久97精品免视看| 欧美激情综合亚洲一二区| 91国偷自产一区二区三区的观看方式| 51色欧美片视频在线观看| 亚洲欧洲日本专区| 中文字幕最新精品| 色综合久综合久久综合久鬼88| 日韩在线视频一区| 久久精品国产清自在天天线| 亚洲跨种族黑人xxx| 91精品国产777在线观看| 日韩免费在线播放| 欧洲成人性视频| 久久久久久久成人| 97香蕉久久夜色精品国产| 亚洲三级黄色在线观看| 日韩av理论片| 久久中文字幕在线| 情事1991在线| 国产成人精品999| 国产精品久久久久秋霞鲁丝| 精品国产欧美一区二区三区成人| 亚洲精品女av网站| 欧美成人精品一区二区三区| 亚洲精品欧美日韩| 成人午夜两性视频| 午夜精品久久久久久久99热| 91精品久久久久久久| 日本精品性网站在线观看| 色综合影院在线| 亚洲激情中文字幕| 欧美大尺度电影在线观看| 欧美激情精品在线| 国产精品普通话| 亚洲xxxxx性| 久久6免费高清热精品| 亚洲va欧美va国产综合久久| 成人免费福利在线| 亚洲国产精品久久久| 欧美激情精品久久久久久久变态| 国产精品麻豆va在线播放| 91最新国产视频| 久久久久久九九九| 色综合色综合网色综合| 欧美激情va永久在线播放| 日韩在线免费视频| 成人免费直播live| 国产精品亚洲片夜色在线| 国产精品扒开腿做爽爽爽视频| 成人午夜一级二级三级| 欧美福利视频在线| 精品视频在线播放| 欧美另类精品xxxx孕妇| 在线电影欧美日韩一区二区私密| 精品美女久久久久久免费| 精品久久久久久亚洲精品| 欧美午夜精品久久久久久浪潮| 精品国产一区二区三区在线观看| 国产成人亚洲综合| 日本国产高清不卡| 国产精品69精品一区二区三区| 国产精品三级久久久久久电影| 国产成人极品视频| 亚洲美女激情视频| 欧美精品制服第一页| 亚洲视频在线观看免费| 91免费看片网站| 久久天天躁夜夜躁狠狠躁2022| 精品国产1区2区| 国产亚洲精品成人av久久ww| 丝袜亚洲另类欧美重口| 成人福利网站在线观看| 亚洲美女www午夜| 亚洲激情在线视频| 亚洲一区二区三区香蕉| 日本sm极度另类视频| 欧美一级高清免费| 韩国三级电影久久久久久| 91精品国产成人www| 欧美在线一区二区三区四| 亚洲伦理中文字幕| 日韩欧美中文字幕在线观看| 国产精品久久久久久婷婷天堂| 大荫蒂欧美视频另类xxxx| 色综合久综合久久综合久鬼88| 精品无码久久久久久国产| 狠狠干狠狠久久| 国产成人在线精品| 亚洲第一综合天堂另类专| 成人久久一区二区| 欧美另类xxx| 日韩精品视频免费专区在线播放| 亚洲精品97久久| 亚洲国产天堂久久综合| 欧美黑人狂野猛交老妇| 色悠悠久久88| 亚洲free性xxxx护士hd| 一本久久综合亚洲鲁鲁| 亚洲国产欧美日韩精品| 亚洲性视频网址| 欧美色视频日本版| 国产欧美一区二区三区视频| 免费99精品国产自在在线| 久久免费精品视频| 国产精品99久久久久久白浆小说| 国产亚洲精品久久久久久牛牛| 狠狠躁夜夜躁久久躁别揉| 日韩精品免费一线在线观看| 国产精品久久在线观看| 91久久久久久久久久久| 亚洲天堂视频在线观看| 亚洲成人激情视频| 欧美视频国产精品| 久久噜噜噜精品国产亚洲综合| 国产亚洲免费的视频看| 疯狂做受xxxx欧美肥白少妇|