久久精品国产精品国产精品污,男人扒开添女人下部免费视频,一级国产69式性姿势免费视频,夜鲁夜鲁很鲁在线视频 视频,欧美丰满少妇一区二区三区,国产偷国产偷亚洲高清人乐享,中文 在线 日韩 亚洲 欧美,熟妇人妻无乱码中文字幕真矢织江,一区二区三区人妻制服国产

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

WebSocket通信原理和在Tomcat中实现源码详解(万字爆肝)

發布時間:2023/12/18 编程问答 25 豆豆
生活随笔 收集整理的這篇文章主要介紹了 WebSocket通信原理和在Tomcat中实现源码详解(万字爆肝) 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

首發CSDN:徐同學呀,原創不易,轉載請注明源鏈接。我是徐同學,用心輸出高質量文章,希望對你有所幫助。 本篇基于Tomcat10.0.6。建議收藏起來慢慢看。

文章目錄

    • 一、前言
    • 二、什么是WebSocket
      • 1、HTTP/1.1的缺陷
      • 2、WebSocket發展歷史
        • (1)背景
        • (2)歷史
      • 3、WebSocket握手和雙向通信
        • (1)定義
        • (2)握手(建立連接)
        • (3)消息幀
        • (4)揮手(關閉連接)
      • 4、WebSocket優點
    • 三、Java API for WebSocket(JSR356)
      • 1、服務端API
        • (1)注解方式@ServerEndpoint
        • (2)繼承抽象類Endpoint
      • 2、客戶端API
      • 3、上下文Session
      • 4、HandshakeRequest 和 HandshakeResponse
        • (1)HandshakeRequest
        • (2)HandshakeResponse
      • 5、WebSocketContainer
    • 四、WebSocket基于Tomcat應用
      • 1、服務器端實現
        • (1)@ServerEndpoint注解方式
        • (2)繼承抽象類Endpoint方式
        • (3)早期Tomcat7中Server端實現對比
      • 2、客戶端實現
        • (1)前端js版
        • (2)@ClientEndpoint注解方式
        • (3)繼承抽象類Endpoint方式
      • 3、基于Nginx反向代理注意事項
    • 五、WebSocket在Tomcat中的源碼實現
      • 1、WsSci初始化
        • (1)WsSci#onStartup
        • (2)WsServerContainer#addEndpoint
        • (3)PojoMethodMapping方法映射和形參解析
      • 2、協議升級(握手)
        • (1)WsFilter
        • (2)UpgradeUtil#doUpgrade
        • (3)Request#upgrade
        • (4)回調機制ActionHook#action
        • (5)ConnectionHandler#process
        • (6)WsHttpUpgradeHandler#init握手成功
      • 3、數據傳輸和解析
        • (1)接收客戶端消息
        • (2)發送消息給客戶端
    • 六、要點回顧
    • 七、參考文獻

一、前言

WebSocket是一種全雙工通信協議,即客戶端可以向服務端發送請求,服務端也可以主動向客戶端推送數據。這樣的特點,使得它在一些實時性要求比較高的場景效果斐然(比如微信朋友圈實時通知、在線協同編輯等)。主流瀏覽器以及一些常見服務端通信框架(Tomcat、netty、undertow、webLogic等)都對WebSocket進行了技術支持。那么,WebSocket具體是什么?為什么會出現WebSocket?如何做到全雙工通信?解決了什么問題?

二、什么是WebSocket

1、HTTP/1.1的缺陷

HTTP/1.1最初是為網絡中超文本資源(HTML),請求-響應傳輸而設計的,后來支持了傳輸更多類型的資源,如圖片、視頻等,但都沒有改變它單向的請求-響應模式。

隨著互聯網的日益壯大,HTTP/1.1功能使用上已體現捉襟見肘的疲態。雖然可以通過某些方式滿足需求(如Ajax、Comet),但是性能上還是局限于HTTP/1.1,那么HTTP/1.1有哪些缺陷呢:

  • 請求-響應模式,只能客戶端發送請求給服務端,服務端才可以發送響應數據給客戶端。
  • 傳輸數據為文本格式,且請求/響應頭部冗長重復。

(為了區分HTTP/1.1和HTTP/1.2,下面描述中,HTTP均代表HTTP/1.1)

2、WebSocket發展歷史

(1)背景

在WebSocket出現之前,主要通過長輪詢和HTTP長連接實現實時數據更新,這種方式有個統稱叫Comet,Tomcat8.5之前有對Comet基于流的HTTP長連接做支持,后來因為WebSocket的成熟和標準化,以及Comet自身依然是基于HTTP,在性能消耗和瓶頸上無法跳脫HTTP,就把Comet廢棄了。

還有一個SPDY技術,也對HTTP進行了改進,多路復用流、服務器推送等,后來演化成HTTP/2.0,因為適用場景和解決的問題不同,暫不對HTTP/2.0做過多解釋,不過對于HTTP/2.0和WebSocket在Tomcat實現中都是作為協議升級來處理的。

(Comet和SPDY的原理不是本篇重點,沒有展開講解,感興趣的同學可自行百度)

(2)歷史

在這種背景下,HTML5制定了WebSocket

  • 籌備階段,WebSocket被劃分為HTML5標準的一部分,2008年6月,Michael Carter進行了一系列討論,最終形成了稱為WebSocket的協議。
  • 2009年12月,Google Chrome 4是第一個提供標準支持的瀏覽器,默認情況下啟用了WebSocket。
  • 2010年2月,WebSocket協議的開發從W3C和WHATWG小組轉移到IETF(TheInternet Engineering Task Force),并在Ian Hickson的指導下進行了兩次修訂。
  • 2011年,IETF將WebSocket協議標準化為RFC 6455起,大多數Web瀏覽器都在實現支持WebSocket協議的客戶端API。此外,已經開發了許多實現WebSocket協議的Java庫。
  • 2013年,發布JSR356標準,Java API for WebSocket。

(為什么要去了解WebSocket的發展歷史和背景呢?個人認為可以更好的理解某個技術實現的演變歷程,比如Tomcat,早期有Comet沒有WebSocket時,Tomcat就對Comet做了支持,后來有WebSocket了,但是還沒出JSR356標準,Tomcat就對Websocket做了支持,自定義API,再后來有了JSR356,Tomcat立馬緊跟潮流,廢棄自定義的API,實現JSR356那一套,這就使得在Tomcat7使用WebSocket的同學,想升為Tomcat8(其實Tomcat7.0.47之后就是JSR356標準了),發現WebSocket接入方式變了,而且一些細節也變了。)

3、WebSocket握手和雙向通信

(1)定義

WebSocket全雙工通信協議,在客戶端和服務端建立連接后,可以持續雙向通信,和HTTP同屬于應用層協議,并且都依賴于傳輸層的TCP/IP協議。

雖然WebSocket有別于HTTP,是一種新協議,但是RFC 6455中規定:

it is designed to work over HTTP ports 80 and 443 as well as to support HTTP proxies and intermediaries.

  • WebSocket通過HTTP端口80和443進行工作,并支持HTTP代理和中介,從而使其與HTTP協議兼容。
  • 為了實現兼容性,WebSocket握手使用HTTP Upgrade頭從HTTP協議更改為WebSocket協議。
  • Websocket使用ws或wss的統一資源標志符(URI),分別對應明文和加密連接。

(2)握手(建立連接)

在雙向通信之前,必須通過握手建立連接。Websocket通過 HTTP/1.1 協議的101狀態碼進行握手,首先客戶端(如瀏覽器)發出帶有特殊消息頭(Upgrade、Connection)的請求到服務器,服務器判斷是否支持升級,支持則返回響應狀態碼101,表示協議升級成功,對于WebSocket就是握手成功。

客戶端請求示例:

GET /test HTTP/1.1 Host: server.example.com Upgrade: websocket Connection: Upgrade Sec-WebSocket-Key: tFGdnEL/5fXMS9yKwBjllg== Origin: http://example.com Sec-WebSocket-Protocol: v10.stomp, v11.stomp, v12.stomp Sec-WebSocket-Extensions: permessage-deflate; client_max_window_bits Sec-WebSocket-Version: 13
  • Connection必須設置Upgrade,表示客戶端希望連接升級。
  • Upgrade: websocket表明協議升級為websocket。
  • Sec-WebSocket-Key字段內記錄著握手過程中必不可少的鍵值,由客戶端(瀏覽器)生成,可以盡量避免普通HTTP請求被誤認為Websocket協議。
  • Sec-WebSocket-Version 表示支持的Websocket版本。RFC6455要求使用的版本是13。
  • Origin字段是必須的。如果缺少origin字段,WebSocket服務器需要回復HTTP 403 狀態碼(禁止訪問),通過Origin可以做安全校驗。

服務端響應示例:

HTTP/1.1 101 Switching Protocols Upgrade: websocket Connection: Upgrade Sec-WebSocket-Accept: HaA6EjhHRejpHyuO0yBnY4J4n3A= Sec-WebSocket-Extensions: permessage-deflate;client_max_window_bits=15 Sec-WebSocket-Protocol: v12.stomp

Sec-WebSocket-Accept的字段值是由握手請求中的Sec-WebSocket-Key的字段值生成的。成功握手確立WebSocket連接之后,通信時不再使用HTTP的數據幀,而采用WebSocket獨立的數據幀。

(3)消息幀

WebSocket使用二進制消息幀作為雙向通信的媒介。何為消息幀?發送方將每個應用程序消息拆分為一個或多個幀,通過網絡將它們傳輸到目的地,并重新組裝解析出一個完整消息。

有別于HTTP/1.1文本消息格式(冗長的消息頭和分隔符等),WebSocket消息幀規定一定的格式,以二進制傳輸,更加短小精悍。二者相同之處就是都是基于TCP/IP流式協議(沒有規定消息邊界)。

如下是消息幀的基本結構圖:

  • FIN: 1 bit,表示該幀是否為消息的最后一幀。1-是,0-否。
  • RSV1,RSV2,RSV3: 1 bit each,預留(3位),擴展的預留標志。一般情況為0,除非協商的擴展定義為非零值。如果接收到非零值且不為協商擴展定義,接收端必須使連接失敗。
  • Opcode: 4 bits,定義消息幀的操作類型,如果接收到一個未知Opcode,接收端必須使連接失敗。(0x0-延續幀,0x1-文本幀,0x2-二進制幀,0x8-關閉幀,0x9-PING幀,0xA-PONG幀(在接收到PING幀時,終端必須發送一個PONG幀響應,除非它已經接收到關閉幀),0x3-0x7保留給未來的非控制幀,0xB-F保留給未來的控制幀)
  • Mask: 1 bit,表示該幀是否為隱藏的,即被加密保護的。1-是,0-否。Mask=1時,必須傳一個Masking-key,用于解除隱藏(客戶端發送消息給服務器端,Mask必須為1)。
  • Payload length: 7 bits, 7+16 bits, or 7+64 bits,有效載荷數據的長度(擴展數據長度+應用數據長度,擴展數據長度可以為0)。

if 0-125, that is the payload length. If 126, the following 2 bytes interpreted as a 16-bit unsigned integer are the payload length. If 127, the following 8 bytes interpreted as a 64-bit unsigned integer (the most significant bit MUST be 0) are the payload length.

  • Masking-key: 0 or 4 bytes,用于解除幀隱藏(加密)的key,Mask=1時不為空,Mask=0時不用傳。
  • Payload data: (x+y) bytes,有效載荷數據包括擴展數據(x bytes)和應用數據(y bytes)。有效載荷數據是用戶真正要傳輸的數據。

這樣的二進制消息幀設計,與HTTP協議相比,WebSocket協議可以提供約500:1的流量減少和3:1的延遲減少。

(4)揮手(關閉連接)

揮手相對于握手要簡單很多,客戶端和服務器端任何一方都可以通過發送關閉幀來發起揮手請求。發送關閉幀的一方,之后不再發送任何數據給對方;接收到關閉幀的一方,如果之前沒有發送過關閉幀,則必須發送一個關閉幀作為響應。關閉幀中可以攜帶關閉原因。

在發送和接收一個關閉幀消息之后,就認為WebSocket連接已關閉,且必須關閉底層TCP連接。

除了通過關閉握手來關閉連接外,WebSocket連接也可能在另一方離開或底層TCP連接關閉時突然關閉。

4、WebSocket優點

  • 較少的控制開銷。在連接建立后,服務器和客戶端之間交換數據時,用于協議控制的數據包頭部相對于HTTP請求每次都要攜帶完整的頭部,顯著減少。

  • 更強的實時性。由于協議是全雙工的,所以服務器可以隨時主動給客戶端下發數據。相對于HTTP請求需要等待客戶端發起請求服務端才能響應,延遲明顯更少。

  • 保持連接狀態。與HTTP不同的是,Websocket需要先建立連接,這就使得其成為一種有狀態的協議,之后通信時可以省略部分狀態信息。而HTTP請求可能需要在每個請求都攜帶狀態信息(如身份認證等)。

  • 更好的二進制支持。Websocket定義了二進制幀,相對HTTP,可以更輕松地處理二進制內容。

  • 支持擴展。Websocket定義了擴展,用戶可以擴展協議、實現部分自定義的子協議。

  • 更好的壓縮效果。相對于HTTP壓縮,Websocket在適當的擴展支持下,可以沿用之前內容的上下文,在傳遞類似的數據時,可以顯著提高壓縮率。

三、Java API for WebSocket(JSR356)

JSR356在Java EE7時歸為Java EE標準的一部分(后來Java EE更名為Jakarta EE,世上再無Java EE,以下統一稱Jakarta EE),所有兼容Jakarta EE的應用服務器,都必須遵循JSR356標準的WebSocket協議API。

根據JSR356規定, 建立WebSocket連接的服務器端和客戶端,兩端對稱,可以互相通信,差異性較小,抽象成API,就是一個個Endpoint(端點),只不過服務器端的叫ServerEndpoint,客戶端的叫ClientEndpoint。客戶端向服務端發送WebSocket握手請求,建立連接后就創建一個ServerEndpoint對象。(這里的Endpoint和Tomcat連接器里的AbstractEndpoint名稱上有點像,但是兩個毫不相干的東西,就像周杰倫和周杰的關系。)

ServerEndpoint和ClientEndpoint在API上差異也很小,有相同的生命周期事件(OnOpen、OnClose、OnError、OnMessage),不同之處是ServerEndpoint作為服務器端點,可以指定一個URI路徑供客戶端連接,ClientEndpoint沒有。

1、服務端API

服務器端的Endpoint有兩種實現方式,一種是注解方式@ServerEndpoint,一種是繼承抽象類Endpoint。

(1)注解方式@ServerEndpoint

首先看看@ServerEndpoint有哪些要素:

  • value,可以指定一個URI路徑標識一個Endpoint。
  • subprotocols,用戶在WebSocket協議下自定義擴展一些子協議。
  • decoders,用戶可以自定義一些消息解碼器,比如通信的消息是一個對象,接收到消息可以自動解碼封裝成消息對象。
  • encoders,有解碼器就有編碼器,定義解碼器和編碼器的好處是可以規范使用層消息的傳輸。
  • configurator,ServerEndpoint配置類,主要提供ServerEndpoint對象的創建方式擴展(如果使用Tomcat的WebSocket實現,默認是反射創建ServerEndpoint對象)。

@ServerEndpoint可以注解到任何類上,但是想實現服務端的完整功能,還需要配合幾個生命周期的注解使用,這些生命周期注解只能注解在方法上:

  • @OnOpen 建立連接時觸發。
  • @OnClose 關閉連接時觸發。
  • @OnError 發生異常時觸發。
  • @OnMessage 接收到消息時觸發。

(2)繼承抽象類Endpoint

繼承抽象類Endpoint,重寫幾個生命周期方法。

怎么沒有onMessage方法,實現onMessage還需要繼承實現一個接口jakarta.websocket.MessageHandler,MessageHandler接口又分為Partial和Whole,實現的MessageHandler需要在onOpen觸發時注冊到jakarta.websocket.Session中。

繼承抽象類Endpoint的方式相對于注解方式要麻煩的多,除了繼承Endpoint和實現接口MessageHandler外,還必須實現一個jakarta.websocket.server.ServerApplicationConfig來管理Endpoint,比如給Endpoint分配URI路徑。

而encoders、decoders、configurator等配置信息由jakarta.websocket.server.ServerEndpointConfig管理,默認實現jakarta.websocket.server.DefaultServerEndpointConfig。

所以如果使用 Java 版WebSocket服務器端實現首推注解方式。

2、客戶端API

對于客戶端API,也是有注解方式和繼承抽象類Endpoint方式。

  • 注解方式,只需要將@ServerEndpoint換成@ClientEndpoint。
  • 繼承抽象類Endpoint方式,需要一個jakarta.websocket.ClientEndpointConfig來管理encoders、decoders、configurator等配置信息,默認實現jakarta.websocket.DefaultClientEndpointConfig。

3、上下文Session

WebSocket是一個有狀態的連接,建立連接后的通信都是通過jakarta.websocket.Session保持狀態,一個連接一個Session,每一個Session有一個唯一標識Id。

Session的主要職責涉及:

  • 基礎信息管理(request信息(getRequestURI、getRequestParameterMap、getPathParameters等)、協議版本getProtocolVersion、子協議getNegotiatedSubprotocol等)。
  • 連接管理(狀態判斷isOpen、接收消息的MessageHandler、發送消息的異步遠程端點RemoteEndpoint.Async和同步遠程端點RemoteEndpoint.Basic等)。

4、HandshakeRequest 和 HandshakeResponse

HandshakeRequest 和 HandshakeResponse了解即可,這兩個接口主要用于WebScoket握手升級過程中握手請求響應的封裝,如果只是單純使用WebSocket,不會接觸到這兩個接口。

(1)HandshakeRequest

(2)HandshakeResponse

Sec-WebSocket-Accept根據客戶端傳的Sec-WebSocket-Key生成,如下是Tomcat10.0.6 WebSocket源碼實現中生成Sec-WebSocket-Accept的算法:

private static String getWebSocketAccept(String key) {byte[] digest = ConcurrentMessageDigest.digestSHA1(key.getBytes(StandardCharsets.ISO_8859_1), WS_ACCEPT);return Base64.encodeBase64String(digest); }

5、WebSocketContainer

jakarta.websocket.WebSocketContainer顧名思義,就是WebSocket的容器,集大成者。其主要職責包括但不限于connectToServer,客戶端連接服務器端,基于瀏覽器的WebSocket客戶端連接服務器端,由瀏覽器支持,但是基于Java版的WebSocket客戶端就可以通過WebSocketContainer#connectToServer向服務端發起連接請求。


四、WebSocket基于Tomcat應用

(如下使用的是javax.websocket包,未使用最新的jakarta.websocket,主要是測試項目基于SpringBoot+Tomcat9.x的,Java API for WebSocket版本需要保持一致。)

1、服務器端實現

(1)@ServerEndpoint注解方式

import javax.websocket.*; import javax.websocket.server.PathParam; import javax.websocket.server.ServerEndpoint; import java.io.IOException; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.atomic.AtomicLong;@ServerEndpoint(value = "/ws/test/{userId}", encoders = {MessageEncoder.class}, decoders = {MessageDecoder.class}, configurator = MyServerConfigurator.class) public class WebSocketServerEndpoint {private Session session;private String userId;@OnOpenpublic void OnOpen(Session session, @PathParam(value = "userId") String userId) {this.session = session;this.userId = userId;// 建立連接后,將連接存到一個map里endpointMap.put(userId, this);Message message = new Message(0, "connected, hello " + userId);sendMsg(message);}@OnClosepublic void OnClose() {// 關閉連接時觸發,從map中刪除連接endpointMap.remove(userId);System.out.println("server closed...");}@OnMessagepublic void onMessage(Message message) {System.out.println("server recive message=" + message.toString());}@OnErrorpublic void onError(Throwable t) throws Throwable {this.session.close(new CloseReason(CloseReason.CloseCodes.CLOSED_ABNORMALLY, "系統異常"));t.printStackTrace();}/*** 群發* @param data*/public void sendAllMsg(Message data) {for (WebSocketServerEndpoint value : endpointMap.values()) {value.sendMsgAsync(data);}}/*** 推送消息給指定 userId* @param data* @param userId*/public void sendMsg(Message data, String userId) {WebSocketServerEndpoint endpoint = endpointMap.get(userId);if (endpoint == null) {System.out.println("not conected to " + userId);return;}endpoint.sendMsgAsync(data);}private void sendMsg(Message data) {try {this.session.getBasicRemote().sendObject(data);} catch (IOException ioException) {ioException.printStackTrace();} catch (EncodeException e) {e.printStackTrace();}}private void sendMsgAsync(Message data) {this.session.getAsyncRemote().sendObject(data);}// 存儲建立連接的Endpointprivate static ConcurrentHashMap<String, WebSocketServerEndpoint> endpointMap = new ConcurrentHashMap<String, WebSocketServerEndpoint>(); }

每一個客戶端與服務器端建立連接后,都會生成一個WebSocketServerEndpoint,可以通過一個Map將其與userId對應存起來,為后續群發廣播和單獨推送消息給某個客戶端提供便利。

注意:@ServerEndpoint的encoders、decoders、configurator等配置信息在實際使用中可以不定義,如果項目簡單,完全可以用默認的。

如果通信消息被封裝成一個對象,如示例的Message(因為源碼過于簡單就不展示了,屬性主要有code、msg、data),就必須提供編碼器和解碼器。也可以在每次發送消息時硬編碼轉為字符串,在接收到消息時轉為Message。有了編碼器和解碼器,顯得比較規范,轉為字符串由編碼器做,字符串轉為對象由解碼器做,但也使得架構變復雜了,視項目需求而定。

Configurator的用處就是自定義Endpoint對象創建方式,默認Tomcat提供的是通過反射。WebScoket是每個連接都會創建一個Endpoint對象,如果連接比較多,很頻繁,通過反射創建,用后即毀,可能不是一個好主意,所以可以搞一個對象池,用過回收,用時先從對象池中拿,有就重置,省去實例化分配內存等消耗過程。

如果使用SpringBoot內置Tomcat、undertow、Netty等,接入WebSocket時除了加@ServerEndpoint還需要加一個@Component,再給Spring注冊一個ServerEndpointExporter類,這樣,服務端Endpoint就交由Spring去掃描注冊了。

@Configuration public class WebSocketConfig {@Beanpublic ServerEndpointExporter serverEndpointExporter() {ServerEndpointExporter serverEndpointExporter = new ServerEndpointExporter();return serverEndpointExporter;} }

外置Tomcat就不需要這么麻煩,Tomcat會默認掃描classpath下帶有@ServerEndpoint注解的類。(SpringBoot接入Websocket后續會單獨出文章講解,也挺有意思的)

(2)繼承抽象類Endpoint方式

import javax.websocket.*; import java.io.IOException; import java.util.concurrent.ConcurrentHashMap;public class WebSocketServerEndpoint extends Endpoint {private Session session;private String userId;@Overridepublic void onOpen(Session session, EndpointConfig endpointConfig) {this.session = session;this.userId = session.getPathParameters().get("userId");session.addMessageHandler(new MessageHandler());endpointMap.put(userId, this);Message message = new Message(0, "connected, hello " + userId);sendMsg(message);}@Overridepublic void onClose(Session session, CloseReason closeReason) {endpointMap.remove(userId);}@Overridepublic void onError(Session session, Throwable throwable) {throwable.printStackTrace();}/*** 群發* @param data*/public void sendAllMsg(Message data) {for (WebSocketServerEndpoint value : endpointMap.values()) {value.sendMsgAsync(data);}}/*** 推送消息給指定 userId* @param data* @param userId*/public void sendMsg(Message data, String userId) {WebSocketServerEndpoint endpoint = endpointMap.get(userId);if (endpoint == null) {System.out.println("not conected to " + userId);return;}endpoint.sendMsgAsync(data);}private void sendMsg(Message data) {try {this.session.getBasicRemote().sendObject(data);} catch (IOException ioException) {ioException.printStackTrace();} catch (EncodeException e) {e.printStackTrace();}}private void sendMsgAsync(Message data) {this.session.getAsyncRemote().sendObject(data);}private class MessageHandler implements javax.websocket.MessageHandler.Whole<Message> {@Overridepublic void onMessage(Message message) {System.out.println("server recive message=" + message.toString());}}private static ConcurrentHashMap<String, WebSocketServerEndpoint> endpointMap = new ConcurrentHashMap<String, WebSocketServerEndpoint>();}

繼承抽象類Endpoint方式比加注解@ServerEndpoint方式麻煩的很,主要是需要自己實現MessageHandler和ServerApplicationConfig。@ServerEndpoint的話都是使用默認的,原理上差不多,只是注解更自動化,更簡潔。

MessageHandler做的事情,一個@OnMessage就搞定了,ServerApplicationConfig做的URI映射、decoders、encoders,configurator等,一個@ServerEndpoint就可以了。

import javax.websocket.Decoder; import javax.websocket.Encoder; import javax.websocket.Endpoint; import javax.websocket.server.ServerApplicationConfig; import javax.websocket.server.ServerEndpointConfig; import java.util.ArrayList; import java.util.HashSet; import java.util.List; import java.util.Set;public class MyServerApplicationConfig implements ServerApplicationConfig {@Overridepublic Set<ServerEndpointConfig> getEndpointConfigs(Set<Class<? extends Endpoint>> set) {Set<ServerEndpointConfig> result = new HashSet<ServerEndpointConfig>();List<Class<? extends Decoder>> decoderList = new ArrayList<Class<? extends Decoder>>();decoderList.add(MessageDecoder.class);List<Class<? extends Encoder>> encoderList = new ArrayList<Class<? extends Encoder>>();encoderList.add(MessageEncoder.class);if (set.contains(WebSocketServerEndpoint3.class)) {ServerEndpointConfig serverEndpointConfig = ServerEndpointConfig.Builder.create(WebSocketServerEndpoint3.class, "/ws/test3").decoders(decoderList).encoders(encoderList).configurator(new MyServerConfigurator()).build();result.add(serverEndpointConfig);}return result;}@Overridepublic Set<Class<?>> getAnnotatedEndpointClasses(Set<Class<?>> set) {return set;} }

如果使用SpringBoot內置Tomcat,則不需要ServerApplicationConfig了,但是需要給Spring注冊一個ServerEndpointConfig。

@Bean public ServerEndpointConfig serverEndpointConfig() {List<Class<? extends Decoder>> decoderList = new ArrayList<Class<? extends Decoder>>();decoderList.add(MessageDecoder.class);List<Class<? extends Encoder>> encoderList = new ArrayList<Class<? extends Encoder>>();encoderList.add(MessageEncoder.class);ServerEndpointConfig serverEndpointConfig = ServerEndpointConfig.Builder.create(WebSocketServerEndpoint3.class, "/ws/test3/{userId}").decoders(decoderList).encoders(encoderList).configurator(new MyServerConfigurator()).build();return serverEndpointConfig; }

(3)早期Tomcat7中Server端實現對比

Tomcat7早期版本7.0.47之前還沒有出JSR 356時,自己搞了一套接口,其實就是一個Servlet。

和遵循JSR356標準的版本對比,有一個比較大的變化是,createWebSocketInbound創建生命周期事件處理器StreamInbound的時機是WebSocket協議升級之前,此時還可以通過用戶線程緩存(ThreadLocal等)的HttpServletRequest對象,獲取一些請求頭等信息。

而遵循JSR356標準的版本實現,創建生命周期事件處理的Endpoint是在WebSocket協議升級完成(經過HTTP握手)之后創建的,而WebSocket握手成功給客戶端響應101前,會結束銷毀HttpServletRequest對象,此時是獲取不到請求頭等信息的。

import org.apache.catalina.websocket.StreamInbound; import org.apache.catalina.websocket.WebSocketServlet;import javax.servlet.annotation.WebServlet; import javax.servlet.http.HttpServletRequest;@WebServlet(urlPatterns = "/ws/test") public class MyWeSocketServlet extends WebSocketServlet {@Overrideprotected StreamInbound createWebSocketInbound(String subProtocol, HttpServletRequest request) {MyMessageInbound messageInbound = new MyMessageInbound(subProtocol, request);return messageInbound;}} import org.apache.catalina.websocket.MessageInbound; import org.apache.catalina.websocket.WsOutbound;import javax.servlet.http.HttpServletRequest; import java.io.IOException; import java.nio.ByteBuffer; import java.nio.CharBuffer;public class MyMessageInbound extends MessageInbound {private String subProtocol;private HttpServletRequest request;public MyMessageInbound(String subProtocol, HttpServletRequest request) {this.subProtocol = subProtocol;this.request = request;}@Overrideprotected void onOpen(WsOutbound outbound) {String msg = "connected, hello";ByteBuffer byteBuffer = ByteBuffer.wrap(msg.getBytes());try {outbound.writeBinaryMessage(byteBuffer);} catch (IOException e) {e.printStackTrace();}}@Overrideprotected void onClose(int status) {}@Overrideprotected void onBinaryMessage(ByteBuffer byteBuffer) throws IOException {// 接收到客戶端信息}@Overrideprotected void onTextMessage(CharBuffer charBuffer) throws IOException {// 接收到客戶端信息} }

2、客戶端實現

(1)前端js版

js版的客戶端主要依托瀏覽器對WebScoket的支持,在生命周期事件觸發上和服務器端的差不多,這也應證了建立WebSocket連接的兩端是對等的。

編寫WebSocket客戶端需要注意以下幾點:

  • 和服務器端商議好傳輸的消息的格式,一般為json字符串,比較直觀,編碼解碼都很簡單,也可以是其他商定的格式。
  • 需要心跳檢測,定時給服務器端發送消息,保持連接正常。
  • 正常關閉連接,即關閉瀏覽器窗口前主動關閉連接,以免服務器端拋異常。
  • 如果因為異常斷開連接,支持重連。
// 對websocket進行簡單封裝 WebSocketOption.prototype = {// 創建websocket操作createWebSocket: function () {try {if('WebSocket' in window) {this.ws = new WebSocket(this.wsUrl);} else if('MozWebSocket' in window) { this.ws = new MozWebSocket(this.wsUrl);} else {alert("您的瀏覽器不支持websocket協議,建議使用新版谷歌、火狐等瀏覽器,請勿使用IE10以下瀏覽器,360瀏覽器請使用極速模式,不要使用兼容模式!"); }this.lifeEventHandle();} catch(e) {this.reconnect(this.wsUrl);console.log(e);} },// 生命周期事件操作lifeEventHandle: function() {var self = this;this.ws.onopen = function (event) {self.connectCount = 1;//心跳檢測重置if (self.heartCheck == null) {self.heartCheck = new HeartCheckObj(self.ws);}self.sendMsg(5, "")self.heartCheck.reset().start(); console.log("websocket連接成功!" + new Date().toUTCString());};this.ws.onclose = function (event) {// 全部設置為初始值self.heartCheck = null;self.reconnect(self.wsUrl); console.log("websocket連接關閉!" + new Date().toUTCString());};this.ws.onerror = function () {self.reconnect(self.wsUrl);console.log("websocket連接錯誤!");};//如果獲取到消息,心跳檢測重置this.ws.onmessage = function (event) { //心跳檢測重置if (self.heartCheck == null) {self.heartCheck = new HeartCheckObj(self.ws);}self.heartCheck.reset().start(); console.log("websocket收到消息啦:" + event.data);// 業務處理// 接收到的消息可以放到localStorage里,然后在其他地方取出來}},// 斷線重連操作reconnect: function() {var self = this;if (this.lockReconnect) return;console.log(this.lockReconnect)this.lockReconnect = true;//沒連接上會一直重連,設置延遲避免請求過多,重連時間設置按倍數增加setTimeout(function () { self.createWebSocket(self.wsUrl);self.lockReconnect = false;self.connectCount++;}, 10000 * (self.connectCount));},// 發送消息操作sendMsg: function(cmd, data) {var sendData = {"cmd": cmd, "msg": data};try {this.ws.send(JSON.stringify(sendData));} catch(err) {console.log("發送數據失敗, err=" + err)}},// 關閉websocket接口操作closeWs: function() {this.ws.close();} }/*** 封裝心跳檢測對象<p>*/ function HeartCheckObj(ws) {this.ws = ws;// 心跳時間this.timeout = 10000;// 定時事件this.timeoutObj = null;// 自動斷開事件this.serverTimeoutObj = null; } HeartCheckObj.prototype = {setWs: function(ws) {this.ws = ws;},reset: function() {clearTimeout(this.timeoutObj);clearTimeout(this.serverTimeoutObj);return this;},// 開始心跳檢測start: function() {var self = this;this.timeoutObj = setTimeout(function() {//這里發送一個心跳,后端收到后,返回一個心跳消息,//onmessage拿到返回的心跳就說明連接正常var ping = {"cmd":1, "msg": "ping"};self.ws.send(JSON.stringify(ping));//如果onmessage那里超過一定時間還沒重置,說明后端主動斷開了self.serverTimeoutObj = setTimeout(function() {//如果onclose會執行reconnect,我們執行ws.close()就行了.如果直接執行reconnect 會觸發onclose導致重連兩次self.ws.close(); }, self.timeout)}, self.timeout)} }/*** -------------------------* 創建websocket的主流程 ** -------------------------*/ var currentDomain = document.domain; var wsUrl = "ws://" + currentDomain + "/test"var webSocketOption = new WebSocketOption(wsUrl) webSocketOption.createWebSocket()// 監聽窗口關閉事件,當窗口關閉時,主動去關閉websocket連接,防止連接還沒斷開就關閉窗口,server端會拋異常。 window.onbeforeunload = function() {webSocketOption.closeWs(); }

這里推薦一個在線測試WebSocket連接和發送消息的網站easyswoole.com/wstool.html:

真的很牛逼,很方便,很簡單。還有源碼github:https://github.com/easy-swoole/wstool,感興趣可以看看。

(2)@ClientEndpoint注解方式

Java版客戶端不用多說,把@ServerEndpoint換成@ClientEndpoint就可以了,其他都一樣。@ClientEndpoint比@ServerEndpoint就少了一個value,不需要設置URI。

@ClientEndpoint(encoders = {MessageEncoder.class}, decoders = {MessageDecoder.class}) public class WebSocketClientEndpoint {private Session session;@OnOpenpublic void OnOpen(Session session) {this.session = session;Message message = new Message(0, "connecting...");sendMsg(message);}@OnClosepublic void OnClose() {Message message = new Message(0, "client closed...");sendMsg(message);System.out.println("client closed");}@OnMessagepublic void onMessage(Message message) {System.out.println("client recive message=" + message.toString());}@OnErrorpublic void onError(Throwable t) throws Throwable {t.printStackTrace();}public void sendMsg(Message data) {try {this.session.getBasicRemote().sendObject(data);} catch (IOException ioException) {ioException.printStackTrace();} catch (EncodeException e) {e.printStackTrace();}}public void sendMsgAsync(Message data) {this.session.getAsyncRemote().sendObject(data);} }

連接服務器端:

WebSocketContainer container = ContainerProvider.getWebSocketContainer(); container.connectToServer(WebSocketClientEndpoint.class,new URI("ws://localhost:8080/ws/test"));

(3)繼承抽象類Endpoint方式

繼承抽象類Endpoint方式也和服務器端的差不多,但是不需要實現ServerApplicationConfig,需要實例化一個ClientEndpointConfig。Endpoint實現類和服務器端的一樣,就省略了,如下是連接服務器端的代碼:

ClientEndpointConfig clientEndpointConfig = ClientEndpointConfig.Builder.create().build(); container.connectToServer(new WebSocketClientEndpoint(),clientEndpointConfig,new URI("ws://localhost:8080/websocket/hello"));

3、基于Nginx反向代理注意事項

一般web服務器會用Nginx做反向代理,經過Nginx反向轉發的HTTP請求不會帶上Upgrade和Connection消息頭,所以需要在Nginx配置里顯式指定需要升級為WebSocket的URI帶上這兩個頭:

location /chat/ {proxy_pass http://backend;proxy_http_version 1.1;proxy_set_header Upgrade $http_upgrade;proxy_set_header Connection "upgrade";proxy_connect_timeout 4s; proxy_read_timeout 7200s; proxy_send_timeout 12s; }

默認情況下,如果代理服務器在60秒內沒有傳輸任何數據,連接將被關閉。這個超時可以通過proxy_read_timeout指令來增加?;蛘?#xff0c;可以將代理服務器配置為定期發送WebSocket PING幀以重置超時并檢查連接是否仍然活躍。

具體可參考:http://nginx.org/en/docs/http/websocket.html

五、WebSocket在Tomcat中的源碼實現

所有兼容Java EE的應用服務器,必須遵循JSR356 WebSocket Java API標準,Tomcat也不例外。而且Tomcat也是支持WebSocket最早的Web應用服務器框架(之一),在還沒有出JSR356標準時,就已經自定義了一套WebSocket API,但是JSR356一出,不得不改弦更張。

通過前面的講解,在使用上完全沒有問題,但是有幾個問題完全是黑盒的:

  • Server Endpoint 是如何被掃描加載的?
  • WebSocket是如何借助HTTP 進行握手升級的?
  • WebSocket建立連接后如何保持連接不斷,互相通信的?

(如下源碼解析,需要對Tomcat連接器源碼有一定了解)

1、WsSci初始化

Tomcat 提供了一個org.apache.tomcat.websocket.server.WsSci類來初始化、加載WebSocket。從類名上顧名思義,利用了Sci加載機制,何為Sci加載機制?就是實現接口 jakarta.servlet.ServletContainerInitializer,在Tomcat部署裝載Web項目(org.apache.catalina.core.StandardContext#startInternal)時主動觸發ServletContainerInitializer#onStartup,做一些擴展的初始化操作。

WsSci主要做了一件事,就是掃描加載Server Endpoint,并將其加到WebSocket容器里jakarta.websocket.WebSocketContainer。

WsSci主要會掃描三種類:

  • 加了@ServerEndpoint的類。
  • Endpoint的子類。
  • ServerApplicationConfig的子類。

(1)WsSci#onStartup

@HandlesTypes({ServerEndpoint.class, ServerApplicationConfig.class,Endpoint.class}) public class WsSci implements ServletContainerInitializer {@Overridepublic void onStartup(Set<Class<?>> clazzes, ServletContext ctx)throws ServletException {WsServerContainer sc = init(ctx, true);if (clazzes == null || clazzes.size() == 0) {return;}// Group the discovered classes by typeSet<ServerApplicationConfig> serverApplicationConfigs = new HashSet<>();Set<Class<? extends Endpoint>> scannedEndpointClazzes = new HashSet<>();Set<Class<?>> scannedPojoEndpoints = new HashSet<>();try {// wsPackage is "jakarta.websocket."String wsPackage = ContainerProvider.class.getName();wsPackage = wsPackage.substring(0, wsPackage.lastIndexOf('.') + 1);for (Class<?> clazz : clazzes) {JreCompat jreCompat = JreCompat.getInstance();int modifiers = clazz.getModifiers();if (!Modifier.isPublic(modifiers) ||Modifier.isAbstract(modifiers) ||Modifier.isInterface(modifiers) ||!jreCompat.isExported(clazz)) {// Non-public, abstract, interface or not in an exported// package (Java 9+) - skip it.continue;}// Protect against scanning the WebSocket API JARs// 防止掃描WebSocket API jarif (clazz.getName().startsWith(wsPackage)) {continue;}if (ServerApplicationConfig.class.isAssignableFrom(clazz)) {// 1、clazz是ServerApplicationConfig子類serverApplicationConfigs.add((ServerApplicationConfig) clazz.getConstructor().newInstance());}if (Endpoint.class.isAssignableFrom(clazz)) {// 2、clazz是Endpoint子類@SuppressWarnings("unchecked")Class<? extends Endpoint> endpoint =(Class<? extends Endpoint>) clazz;scannedEndpointClazzes.add(endpoint);}if (clazz.isAnnotationPresent(ServerEndpoint.class)) {// 3、clazz是加了注解ServerEndpoint的類scannedPojoEndpoints.add(clazz);}}} catch (ReflectiveOperationException e) {throw new ServletException(e);}// Filter the resultsSet<ServerEndpointConfig> filteredEndpointConfigs = new HashSet<>();Set<Class<?>> filteredPojoEndpoints = new HashSet<>();if (serverApplicationConfigs.isEmpty()) {// 從這里看出@ServerEndpoint的服務器端是可以不用ServerApplicationConfig的filteredPojoEndpoints.addAll(scannedPojoEndpoints);} else {// serverApplicationConfigs不為空,for (ServerApplicationConfig config : serverApplicationConfigs) {Set<ServerEndpointConfig> configFilteredEndpoints =config.getEndpointConfigs(scannedEndpointClazzes);if (configFilteredEndpoints != null) {filteredEndpointConfigs.addAll(configFilteredEndpoints);}// getAnnotatedEndpointClasses 對于 scannedPojoEndpoints起到一個過濾作用// 不滿足條件的后面不加到WsServerContainer里Set<Class<?>> configFilteredPojos =config.getAnnotatedEndpointClasses(scannedPojoEndpoints);if (configFilteredPojos != null) {filteredPojoEndpoints.addAll(configFilteredPojos);}}}try {// 繼承抽象類Endpoint的需要使用者手動封裝成ServerEndpointConfig// 而加了注解@ServerEndpoint的類 Tomcat會自動封裝成ServerEndpointConfig// Deploy endpointsfor (ServerEndpointConfig config : filteredEndpointConfigs) {sc.addEndpoint(config);}// Deploy POJOsfor (Class<?> clazz : filteredPojoEndpoints) {sc.addEndpoint(clazz, true);}} catch (DeploymentException e) {throw new ServletException(e);}}static WsServerContainer init(ServletContext servletContext,boolean initBySciMechanism) {WsServerContainer sc = new WsServerContainer(servletContext);servletContext.setAttribute(Constants.SERVER_CONTAINER_SERVLET_CONTEXT_ATTRIBUTE, sc);// 注冊監聽器WsSessionListener給servletContext,// 在http session銷毀時觸發 ws session的關閉銷毀servletContext.addListener(new WsSessionListener(sc));// Can't register the ContextListener again if the ContextListener is// calling this methodif (initBySciMechanism) {// 注冊監聽器WsContextListener給servletContext,// 在 servletContext初始化時觸發WsSci.init// 在 servletContext銷毀時觸發WsServerContainer的銷毀// 不過呢,只在WsSci.onStartup時注冊一次servletContext.addListener(new WsContextListener());}return sc;} }

從上述源碼中可以看出ServerApplicationConfig起到一個過濾的作用:

  • 當沒有ServerApplicationConfig時,加了@ServerEndpoint的類會默認全部加到一個Set集合(filteredPojoEndpoints),所以加了@ServerEndpoint的類可以不需要自定義實現ServerApplicationConfig。
  • 當有ServerApplicationConfig時,ServerApplicationConfig#getEndpointConfigs用來過濾Endpoint子類,并且Endpoint子類必須封裝成一個ServerEndpointConfig。
  • ServerApplicationConfig#getAnnotatedEndpointClasses用來過濾加了注解@ServerEndpoint的類,一般空實現就行了(如果不想某個類被加到WsServerContainer里,那不加@ServerEndpoint不就可以了)。

過濾之后的Endpoint子類和加了注解@ServerEndpoint的類會分別調用不同形參的WsServerContainer#addEndpoint,將其加到WsServerContainer里。

(2)WsServerContainer#addEndpoint

  • 將Endpoint子類加到WsServerContainer里,調用的是形參為ServerEndpointConfig的addEndpoint:
public void addEndpoint(ServerEndpointConfig sec) throws DeploymentException {addEndpoint(sec, false); }

因為Endpoint子類需要使用者封裝成ServerEndpointConfig,不需要Tomcat來封裝。

  • 將加了注解@ServerEndpoint的類加到WsServerContainer,調用的是形參為Class<?>的addEndpoint(fromAnnotatedPojo參數暫時在這個方法里沒什么用處):

該方法主要職責就是解析@ServerEndpoint,獲取path、decoders、encoders、configurator等構建一個ServerEndpointConfig對象

最終調用的都是如下這個比較復雜的方法,fromAnnotatedPojo表示是否是加了@ServerEndpoint的類。主要做了兩件事:

  • 對加了@ServerEndpoint類的生命周期方法(@OnOpen、@OnClose、@OnError、@OnMessage)的掃描和映射封裝。

  • 對path的有效性檢查和path param解析。

(3)PojoMethodMapping方法映射和形參解析

PojoMethodMapping構造函數比較長,主要是對加了@OnOpen、@OnClose、@OnError、@OnMessage的方法進行校驗和映射,以及對每個方法的形參進行解析和校驗,主要邏輯總結如下:

  • 對當前類以及其父類中的方法進行掃描。
  • 當前類中不能存在多個相同注解的方法,否則會拋出Duplicate annotation異常。
  • 父類和子類中存在相同注解的方法,子類必須重寫該方法,否則會拋出Duplicate annotation異常。
  • 對于@OnMessage,可以有多個,但是接收消息的類型必須不同,消息類型大概分為三種:PongMessage心跳消息、字節型、字符型。
  • 如果掃描到對的注解都是父類的方法,子類重寫了該方法,但是沒有加響應的注解,則會被清除。
  • 形參解析。
public PojoMethodMapping(Class<?> clazzPojo, List<Class<? extends Decoder>> decoderClazzes, String wsPath,InstanceManager instanceManager) throws DeploymentException {this.wsPath = wsPath;List<DecoderEntry> decoders = Util.getDecoders(decoderClazzes, instanceManager);Method open = null;Method close = null;Method error = null;Method[] clazzPojoMethods = null;Class<?> currentClazz = clazzPojo;while (!currentClazz.equals(Object.class)) {Method[] currentClazzMethods = currentClazz.getDeclaredMethods();if (currentClazz == clazzPojo) {clazzPojoMethods = currentClazzMethods;}for (Method method : currentClazzMethods) {if (method.isSynthetic()) {// Skip all synthetic methods.// They may have copies of annotations from methods we are// interested in and they will use the wrong parameter type// (they always use Object) so we can't used them here.continue;}if (method.getAnnotation(OnOpen.class) != null) {checkPublic(method);if (open == null) {open = method;} else {if (currentClazz == clazzPojo ||!isMethodOverride(open, method)) {// Duplicate annotation// 拋出Duplicate annotation異常的兩種情況:// 1. 當前的類有多個相同注解的方法,如有兩個@OnOpen// 2. 當前類時父類,有相同注解的方法,但是其子類沒有重寫這個方法// 即 父類和子類有多個相同注解的方法,且沒有重寫關系throw new DeploymentException(sm.getString("pojoMethodMapping.duplicateAnnotation",OnOpen.class, currentClazz));}}} else if (method.getAnnotation(OnClose.class) != null) {checkPublic(method);if (close == null) {close = method;} else {if (currentClazz == clazzPojo ||!isMethodOverride(close, method)) {// Duplicate annotationthrow new DeploymentException(sm.getString("pojoMethodMapping.duplicateAnnotation",OnClose.class, currentClazz));}}} else if (method.getAnnotation(OnError.class) != null) {checkPublic(method);if (error == null) {error = method;} else {if (currentClazz == clazzPojo ||!isMethodOverride(error, method)) {// Duplicate annotationthrow new DeploymentException(sm.getString("pojoMethodMapping.duplicateAnnotation",OnError.class, currentClazz));}}} else if (method.getAnnotation(OnMessage.class) != null) {checkPublic(method);MessageHandlerInfo messageHandler = new MessageHandlerInfo(method, decoders);boolean found = false;// 第一次掃描OnMessage時,onMessage為空,不會走下面的for,然后就把messageHandler加到onMessage里// 如果非首次掃描到這里,即向上掃描父類,允許有多個接收消息類型完全不同的onmessagefor (MessageHandlerInfo otherMessageHandler : onMessage) {// 如果多個onmessage接收的消息類型有相同的,則可能會拋出Duplicate annotation// 1. 同一個類中多個onmessage有接收相同類型的消息// 2. 父子類中多個onmessage有接收相同類型的消息,但不是重寫關系if (messageHandler.targetsSameWebSocketMessageType(otherMessageHandler)) {found = true;if (currentClazz == clazzPojo ||!isMethodOverride(messageHandler.m, otherMessageHandler.m)) {// Duplicate annotationthrow new DeploymentException(sm.getString("pojoMethodMapping.duplicateAnnotation",OnMessage.class, currentClazz));}}}if (!found) {onMessage.add(messageHandler);}} else {// Method not annotated}}currentClazz = currentClazz.getSuperclass();}// If the methods are not on clazzPojo and they are overridden// by a non annotated method in clazzPojo, they should be ignoredif (open != null && open.getDeclaringClass() != clazzPojo) {// open 有可能是父類的,子類即clazzPojo有重寫該方法,但是沒有加OnOpen注解// 則 open置為nullif (isOverridenWithoutAnnotation(clazzPojoMethods, open, OnOpen.class)) {open = null;}}if (close != null && close.getDeclaringClass() != clazzPojo) {if (isOverridenWithoutAnnotation(clazzPojoMethods, close, OnClose.class)) {close = null;}}if (error != null && error.getDeclaringClass() != clazzPojo) {if (isOverridenWithoutAnnotation(clazzPojoMethods, error, OnError.class)) {error = null;}}List<MessageHandlerInfo> overriddenOnMessage = new ArrayList<>();for (MessageHandlerInfo messageHandler : onMessage) {if (messageHandler.m.getDeclaringClass() != clazzPojo&& isOverridenWithoutAnnotation(clazzPojoMethods, messageHandler.m, OnMessage.class)) {overriddenOnMessage.add(messageHandler);}}// 子類重寫了的onmessage方法,但沒有加OnMessage注解的需要從onMessage list 中刪除for (MessageHandlerInfo messageHandler : overriddenOnMessage) {onMessage.remove(messageHandler);}this.onOpen = open;this.onClose = close;this.onError = error;// 參數解析onOpenParams = getPathParams(onOpen, MethodType.ON_OPEN);onCloseParams = getPathParams(onClose, MethodType.ON_CLOSE);onErrorParams = getPathParams(onError, MethodType.ON_ERROR); }

雖然方法名可以隨意,但是形參卻有著強制限制:

  • @onOpen方法,可以有的參數Session、EndpointConfig、@PathParam,不能有其他參數。
  • @onError方法,可以有的參數Session、@PathParam, 必須有Throwable,不能有其他參數。
  • @onClose方法,可以有的參數Session, CloseReason, @PathParam,不能有其他參數。

2、協議升級(握手)

Tomcat中WebSocket是通過UpgradeToken機制實現的,其具體的升級處理器為WsHttpUpgradeHandler。WebSocket協議升級的過程比較曲折,首先要通過過濾器WsFilter進行升級判斷,然后調用org.apache.catalina.connector.Request#upgrade進行UpgradeToken的構建,最后通過org.apache.catalina.connector.Request#coyoteRequest回調函數action將UpgradeToken回傳給連接器為后續升級處理做準備。

(1)WsFilter

WebSocket協議升級的過程比較曲折。帶有WebSocket握手的請求會平安經過Tomcat的Connector,被轉發到Servlet容器中,在業務處理之前經過過濾器WsFilter判斷是否需要升級(WsFilter 在 org.apache.catalina.core.ApplicationFilterChain過濾鏈中觸發):

  • 首先判斷WsServerContainer是否有進行Endpoint的掃描和注冊以及請頭中是否有Upgrade: websocket。
  • 獲取請求path即uri在WsServerContainer中找對應的ServerEndpointConfig。
  • 調用UpgradeUtil.doUpgrade進行升級。

(2)UpgradeUtil#doUpgrade

UpgradeUtil#doUpgrade主要做了如下幾件事情:

  • 檢查HttpServletRequest的一些請求頭的有效性,如Connection: upgrade、Sec-WebSocket-Version:13、Sec-WebSocket-Key等。
  • 給HttpServletResponse設置一些響應頭,如Upgrade:websocket、Connection: upgrade、根據Sec-WebSocket-Key的值生成響應頭Sec-WebSocket-Accept的值。
  • 封裝WsHandshakeRequest和WsHandshakeResponse。
  • 調用HttpServletRequest#upgrade進行升級,并獲取WsHttpUpgradeHandler(具體的升級流程處理器)。
// org.apache.tomcat.websocket.server.UpgradeUtil#doUpgrade public static void doUpgrade(WsServerContainer sc, HttpServletRequest req,HttpServletResponse resp, ServerEndpointConfig sec,Map<String,String> pathParams)throws ServletException, IOException {// Validate the rest of the headers and reject the request if that// validation failsString key;String subProtocol = null;// 檢查請求頭中是否有 Connection: upgradeif (!headerContainsToken(req, Constants.CONNECTION_HEADER_NAME,Constants.CONNECTION_HEADER_VALUE)) {resp.sendError(HttpServletResponse.SC_BAD_REQUEST);return;}// 檢查請求頭中的 Sec-WebSocket-Version:13if (!headerContainsToken(req, Constants.WS_VERSION_HEADER_NAME,Constants.WS_VERSION_HEADER_VALUE)) {resp.setStatus(426);resp.setHeader(Constants.WS_VERSION_HEADER_NAME,Constants.WS_VERSION_HEADER_VALUE);return;}// 獲取 Sec-WebSocket-Keykey = req.getHeader(Constants.WS_KEY_HEADER_NAME);if (key == null) {resp.sendError(HttpServletResponse.SC_BAD_REQUEST);return;}// Origin check,校驗 Origin 是否有權限String origin = req.getHeader(Constants.ORIGIN_HEADER_NAME);if (!sec.getConfigurator().checkOrigin(origin)) {resp.sendError(HttpServletResponse.SC_FORBIDDEN);return;}// Sub-protocolsList<String> subProtocols = getTokensFromHeader(req,Constants.WS_PROTOCOL_HEADER_NAME);subProtocol = sec.getConfigurator().getNegotiatedSubprotocol(sec.getSubprotocols(), subProtocols);// Extensions// Should normally only be one header but handle the case of multiple// headersList<Extension> extensionsRequested = new ArrayList<>();Enumeration<String> extHeaders = req.getHeaders(Constants.WS_EXTENSIONS_HEADER_NAME);while (extHeaders.hasMoreElements()) {Util.parseExtensionHeader(extensionsRequested, extHeaders.nextElement());}// Negotiation phase 1. By default this simply filters out the// extensions that the server does not support but applications could// use a custom configurator to do more than this.List<Extension> installedExtensions = null;if (sec.getExtensions().size() == 0) {installedExtensions = Constants.INSTALLED_EXTENSIONS;} else {installedExtensions = new ArrayList<>();installedExtensions.addAll(sec.getExtensions());installedExtensions.addAll(Constants.INSTALLED_EXTENSIONS);}List<Extension> negotiatedExtensionsPhase1 = sec.getConfigurator().getNegotiatedExtensions(installedExtensions, extensionsRequested);// Negotiation phase 2. Create the Transformations that will be applied// to this connection. Note than an extension may be dropped at this// point if the client has requested a configuration that the server is// unable to support.List<Transformation> transformations = createTransformations(negotiatedExtensionsPhase1);List<Extension> negotiatedExtensionsPhase2;if (transformations.isEmpty()) {negotiatedExtensionsPhase2 = Collections.emptyList();} else {negotiatedExtensionsPhase2 = new ArrayList<>(transformations.size());for (Transformation t : transformations) {negotiatedExtensionsPhase2.add(t.getExtensionResponse());}}// Build the transformation pipelineTransformation transformation = null;StringBuilder responseHeaderExtensions = new StringBuilder();boolean first = true;for (Transformation t : transformations) {if (first) {first = false;} else {responseHeaderExtensions.append(',');}append(responseHeaderExtensions, t.getExtensionResponse());if (transformation == null) {transformation = t;} else {transformation.setNext(t);}}// Now we have the full pipeline, validate the use of the RSV bits.if (transformation != null && !transformation.validateRsvBits(0)) {throw new ServletException(sm.getString("upgradeUtil.incompatibleRsv"));}// 設置resp的響應頭Upgrade:websocket、 Connection: upgrade 、Sec-WebSocket-Accept:// If we got this far, all is good. Accept the connection.resp.setHeader(Constants.UPGRADE_HEADER_NAME,Constants.UPGRADE_HEADER_VALUE);resp.setHeader(Constants.CONNECTION_HEADER_NAME,Constants.CONNECTION_HEADER_VALUE);// 通過Sec-WebSocket-Key生成Sec-WebSocket-Accept的值resp.setHeader(HandshakeResponse.SEC_WEBSOCKET_ACCEPT,getWebSocketAccept(key));if (subProtocol != null && subProtocol.length() > 0) {// RFC6455 4.2.2 explicitly states "" is not valid hereresp.setHeader(Constants.WS_PROTOCOL_HEADER_NAME, subProtocol);}if (!transformations.isEmpty()) {resp.setHeader(Constants.WS_EXTENSIONS_HEADER_NAME, responseHeaderExtensions.toString());}WsHandshakeRequest wsRequest = new WsHandshakeRequest(req, pathParams);WsHandshakeResponse wsResponse = new WsHandshakeResponse();WsPerSessionServerEndpointConfig perSessionServerEndpointConfig =new WsPerSessionServerEndpointConfig(sec);sec.getConfigurator().modifyHandshake(perSessionServerEndpointConfig,wsRequest, wsResponse);wsRequest.finished();// Add any additional headersfor (Entry<String,List<String>> entry :wsResponse.getHeaders().entrySet()) {for (String headerValue: entry.getValue()) {resp.addHeader(entry.getKey(), headerValue);}}// 調用 request.upgrade 進行升級WsHttpUpgradeHandler wsHandler =req.upgrade(WsHttpUpgradeHandler.class);wsHandler.preInit(perSessionServerEndpointConfig, sc, wsRequest,negotiatedExtensionsPhase2, subProtocol, transformation, pathParams,req.isSecure());}

(3)Request#upgrade

Request#upgrade主要做了三件事:

  • 實例化WsHttpUpgradeHandler并構建UpgradeToken。
  • 回調coyoteRequest.action,將UpgradeToken回傳給連接器。
  • 設置響應碼101。
// org.apache.catalina.connector.Request#upgrade public <T extends HttpUpgradeHandler> T upgrade(Class<T> httpUpgradeHandlerClass) throws java.io.IOException, ServletException {T handler;InstanceManager instanceManager = null;try {// Do not go through the instance manager for internal Tomcat classes since they don't// need injectionif (InternalHttpUpgradeHandler.class.isAssignableFrom(httpUpgradeHandlerClass)) {handler = httpUpgradeHandlerClass.getConstructor().newInstance();} else {instanceManager = getContext().getInstanceManager();handler = (T) instanceManager.newInstance(httpUpgradeHandlerClass);}} catch (ReflectiveOperationException | NamingException | IllegalArgumentException |SecurityException e) {throw new ServletException(e);}// 構建 UpgradeToken,UpgradeToken主要包含WsHttpUpgradeHandler、context、協議名稱protocolUpgradeToken upgradeToken = new UpgradeToken(handler, getContext(), instanceManager,getUpgradeProtocolName(httpUpgradeHandlerClass));// 回調action 進行升級coyoteRequest.action(ActionCode.UPGRADE, upgradeToken);// Output required by RFC2616. Protocol specific headers should have// already been set.// 設置響應101response.setStatus(HttpServletResponse.SC_SWITCHING_PROTOCOLS);return handler; }

(4)回調機制ActionHook#action

一些發生在Servlet容器的動作可能需要回傳給連接器做處理,比如WebSocket的握手升級,所以連接器就給org.apache.coyote.Request設置了一個動作鉤子``ActionHook#action。一些動作表示定義在枚舉類ActionCode中,ActionCode.UPGRADE就代表協議升級動作。org.apache.coyote.AbstractProcessor實現了ActionHook接口,ActionCode.UPGRADE動作會調用org.apache.coyote.http11.Http11Processor#doHttpUpgrade,只是簡單將upgradeToken設置給Http11Processor`。

(5)ConnectionHandler#process

Tomcat連接器是同步調用容器業務處理,容器中的業務處理結束后還是回到連接器繼續往下執行。

連接器將請求轉發給容器處理是在適配器里完成的,容器中流程處理結束返回到org.apache.catalina.connector.CoyoteAdapter#service,繼續往下執行,最終結束并回收HttpServletrequest、HttpServletreponse對象。

org.apache.catalina.connector.CoyoteAdapter#service是在org.apache.coyote.http11.Http11Processor#service中調用的,

Http11Processor#service是HTTP請求處理主流程,通過upgradeToken != null來判斷是否為升級操作,s是則返回SocketState.UPGRADING。

最后來到org.apache.coyote.AbstractProtocol.ConnectionHandler#process一個連接處理的主流程,根據Http11Processor#service返回SocketState.UPGRADING來進行升級操作,如下只截取了和WebSocket協議升級相關流程的代碼:

  • 獲取UpgradeToken,從中取出HttpUpgradeHandler,對于WebSocket來說是WsHttpUpgradeHandler。
  • 調用WsHttpUpgradeHandler#init啟動協議升級處理。

(6)WsHttpUpgradeHandler#init握手成功

走到這里,基本上就是握手成功了,接下來就是創建WsSession和觸發onOpen。

WsSession的構建中會實例化Endpoint,如果實例化出來的對象不是Endpoint類型,即加了@ServerEndpoint的實例對象,則用一個PojoEndpointServer進行包裝,而PojoEndpointServer是繼承了抽象類Endpoint的。

觸發onOpen時會將WsSession傳進去,對于加PojoEndpointServer,因為用戶自定義的方法名和形參不確定,所以通過反射調用用戶自定義的onopen形式的方法,并且會將通過@onMessage解析出的MessageHandler設置給WsSession。

3、數據傳輸和解析

握手成功之后就建立了雙向通信的連接,該連接有別于HTTP/1.1長連接(應用服務器中工作線程循環占用),而是占用一條TCP連接。在連接建立是進行TCP三次握手,之后全雙工互相通信,將不需要再進行耗時的TCP的三次握手和四次揮手,一方需要關閉WebSocket連接時,發送關閉幀,另一方接收到關閉幀之后,也發送個關閉幀作為響應,之后就認為WebSocket連接關閉了,并且關閉底層TCP連接(四次揮手)。

實則WebSocket全雙工是建立在TCP的長鏈接上的,TCP長鏈接長時間沒有消息通信,會定時?;?#xff0c;一般WebSocket會通過代理如nginx等進行連接通信,nginx有一個連接超時沒有任何信息傳輸時,會斷開,所以需要WebSocket一端定時發送心跳保活。

(1)接收客戶端消息

客戶端來了消息,由連接器的Poller輪詢監測socket底層是否有數據到來,有數據可讀,則封裝成一個SocketProcessor扔到線程池里處理,org.apache.coyote.http11.upgrade.UpgradeProcessorInternal#dispatch具有處理升級協議連接,org.apache.tomcat.websocket.server.WsHttpUpgradeHandler#upgradeDispatch是專門處理WebSocket連接的處理器。

org.apache.tomcat.websocket.server.WsFrameServer是對服務器端消息幀處理的封裝,包括讀取底層數據,按消息幀格式解析、拼裝出有效載荷數據,觸發onMessage。

因為源碼篇幅較多,只展示具體源碼調用流程:

(2)發送消息給客戶端

一般,客戶端發送WebSocket握手請求,和服務器端建立連接后,服務器端需要將連接(Endpoint+WsSession)保存起來,為后續主動推送消息給客戶端提供方便。

Tomcat提供了可以發送三種數據類型(文本、二進制、Object對象)和兩種發送方式(同步、異步)的發送消息的方法。

  • org.apache.tomcat.websocket.WsRemoteEndpointAsync異步發送。
  • org.apache.tomcat.websocket.WsRemoteEndpointBasic 同步發送。

發送消息也同樣需要按消息幀格式封裝,然后通過socket寫到網絡里即可。

六、要點回顧

WebSocket的出現不是空穴來風,起初在HTTP/1.1基礎上通過輪詢和長連接達到信息實時同步的功能,但是這并沒有跳出HTTP/1.1自身的缺陷。HTTP/1.1明顯的兩個缺陷:消息頭冗長且為文本傳輸,請求響應模式。為此,WebSocket誕生了,跳出HTTP/1.1,建立一個新的真正全雙工通信協議。

不僅僅要會在項目中使用WebSocket,還要知道其通信原理和在應用服務器中的實現原理,很多注意事項都是在查閱了官方資源和源碼之后恍然大悟的。

  • 在Tomcat中使用WebSocket不可以在Endpoint里獲取緩存的HttpServletRequest對象,因為在WebSocket握手之前,HTTP/1.1請求就算結束了(HttpServletRequest對象被回收),建立連接之后就更是獨立于HTTP/1.1了。
  • 建立連接的WebSocket,會生成新的Endpoint和WsSession。
  • 使用內置Tomcat需要注意,WsSci做的事情交給了Spring做。
  • WebSocket全雙工是建立在TCP長連接的基礎之上。
  • … …

七、參考文獻

  • https://datatracker.ietf.org/doc/html/rfc6455(可能需要翻墻)
  • https://www.oracle.com/technical-resources/articles/java/jsr356.html
  • https://medium.com/swlh/websockets-with-spring-part-1-http-and-websocket-36c69df1c2ee(可能需要翻墻)
  • http://nginx.org/en/docs/http/websocket.html
  • https://zh.wikipedia.org/wiki/WebSocket
  • 書籍:《Tomcat架構解析》劉光瑞(Tomcat8.5)11.3.4 Tomcat的WebSocket實現
  • 書籍:《Tomcat內核設計剖析》汪建(Tomcat7)10.6 WebSocket協議的支持
  • 書籍:《圖解HTTP》9.3 使用瀏覽器進行全雙工通信的WebSocket
  • 極客時間:《深入拆解Tomcat & Jetty》李號雙(Tomcat9.x)18.新特性:Tomcat如何支持WebSocket?
  • Tomcat注釋源碼:https://gitee.com/stefanpy/tomcat-source-code-learning
  • 如若文章有錯誤理解,歡迎批評指正,同時非常期待你的留言和點贊。如果覺得有用,不妨點個在看,讓更多人受益。

    總結

    以上是生活随笔為你收集整理的WebSocket通信原理和在Tomcat中实现源码详解(万字爆肝)的全部內容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。

    九一九色国产 | 国产成人精品视频ⅴa片软件竹菊 | 久久国产劲爆∧v内射 | 欧美日本日韩 | 欧美性生交xxxxx久久久 | 夜精品a片一区二区三区无码白浆 | 日日鲁鲁鲁夜夜爽爽狠狠 | 亚洲成在人网站无码天堂 | 一本久久伊人热热精品中文字幕 | 国产乱人无码伦av在线a | 亚洲中文字幕乱码av波多ji | 小鲜肉自慰网站xnxx | 欧美丰满老熟妇xxxxx性 | 欧美黑人巨大xxxxx | 亚洲精品一区二区三区大桥未久 | 国产午夜无码精品免费看 | 日韩欧美中文字幕在线三区 | 九一九色国产 | 亚洲人亚洲人成电影网站色 | 久久无码专区国产精品s | 麻豆av传媒蜜桃天美传媒 | 精品国产青草久久久久福利 | 亚洲中文字幕成人无码 | 大地资源中文第3页 | 日日摸夜夜摸狠狠摸婷婷 | 丰满少妇弄高潮了www | 国产口爆吞精在线视频 | 国产成人精品无码播放 | 欧美喷潮久久久xxxxx | 又大又硬又黄的免费视频 | 欧美人与牲动交xxxx | 黄网在线观看免费网站 | 人妻熟女一区 | 亚洲欧美综合区丁香五月小说 | 无套内谢的新婚少妇国语播放 | www国产精品内射老师 | 日韩精品一区二区av在线 | 成人精品视频一区二区 | 久久久久人妻一区精品色欧美 | 国产成人无码午夜视频在线观看 | 一个人看的www免费视频在线观看 | 色爱情人网站 | 国产精品无套呻吟在线 | 欧美丰满熟妇xxxx性ppx人交 | 亚洲国产精品一区二区第一页 | 亚洲综合伊人久久大杳蕉 | 国产香蕉97碰碰久久人人 | 亚洲国精产品一二二线 | 成人欧美一区二区三区黑人免费 | 久热国产vs视频在线观看 | 亚洲一区av无码专区在线观看 | 精品aⅴ一区二区三区 | 亚洲欧洲日本无在线码 | 永久黄网站色视频免费直播 | 国产精品久久久久9999小说 | 久久久久久av无码免费看大片 | 国内精品人妻无码久久久影院 | 久久久av男人的天堂 | 丰满肥臀大屁股熟妇激情视频 | 纯爱无遮挡h肉动漫在线播放 | 免费看男女做好爽好硬视频 | 国产真实伦对白全集 | 男女超爽视频免费播放 | 麻豆精品国产精华精华液好用吗 | 曰本女人与公拘交酡免费视频 | 国产性生交xxxxx无码 | 欧美freesex黑人又粗又大 | 2020久久香蕉国产线看观看 | 四虎4hu永久免费 | 又大又紧又粉嫩18p少妇 | 亚洲综合另类小说色区 | 波多野结衣一区二区三区av免费 | 精品无码国产自产拍在线观看蜜 | 2019午夜福利不卡片在线 | 四十如虎的丰满熟妇啪啪 | 亚洲码国产精品高潮在线 | 偷窥日本少妇撒尿chinese | 99久久亚洲精品无码毛片 | 欧美日本日韩 | 麻豆果冻传媒2021精品传媒一区下载 | 无码av岛国片在线播放 | 国产熟女一区二区三区四区五区 | 国产99久久精品一区二区 | 国内精品人妻无码久久久影院蜜桃 | 九九久久精品国产免费看小说 | 九九综合va免费看 | 少妇人妻偷人精品无码视频 | 久久综合九色综合97网 | 一个人免费观看的www视频 | 国产一区二区三区精品视频 | 性色欲情网站iwww九文堂 | 狠狠色丁香久久婷婷综合五月 | 国产亚洲精品久久久久久国模美 | 欧洲精品码一区二区三区免费看 | 精品成在人线av无码免费看 | 99久久婷婷国产综合精品青草免费 | 亚洲成在人网站无码天堂 | 国产av人人夜夜澡人人爽麻豆 | 亚洲国产一区二区三区在线观看 | 99国产欧美久久久精品 | 在线a亚洲视频播放在线观看 | 亚洲日韩av一区二区三区四区 | 人人妻人人澡人人爽精品欧美 | 狂野欧美性猛xxxx乱大交 | 三级4级全黄60分钟 | 日本在线高清不卡免费播放 | 国产在线一区二区三区四区五区 | 国产亚洲人成a在线v网站 | 国精产品一品二品国精品69xx | 18精品久久久无码午夜福利 | 亚洲春色在线视频 | v一区无码内射国产 | 人妻少妇精品视频专区 | 精品乱码久久久久久久 | 一本色道久久综合狠狠躁 | 人妻少妇精品无码专区动漫 | 欧美一区二区三区视频在线观看 | 在线观看国产一区二区三区 | 99久久久国产精品无码免费 | 亚洲色大成网站www | 国内丰满熟女出轨videos | 国产成人午夜福利在线播放 | 欧美真人作爱免费视频 | 日韩人妻无码中文字幕视频 | 国产香蕉尹人视频在线 | 俄罗斯老熟妇色xxxx | 一本久久a久久精品vr综合 | 人妻与老人中文字幕 | 欧美精品无码一区二区三区 | 亚洲 a v无 码免 费 成 人 a v | 国产精品久久国产精品99 | 自拍偷自拍亚洲精品被多人伦好爽 | 精品无码成人片一区二区98 | 成人亚洲精品久久久久 | 男女爱爱好爽视频免费看 | 丁香花在线影院观看在线播放 | 久久99精品久久久久久 | 久9re热视频这里只有精品 | 久久综合香蕉国产蜜臀av | 精品无人国产偷自产在线 | 国产超碰人人爽人人做人人添 | 久久人妻内射无码一区三区 | 久久久av男人的天堂 | 无码人妻久久一区二区三区不卡 | 18禁止看的免费污网站 | 亚洲精品欧美二区三区中文字幕 | 精品一二三区久久aaa片 | 亚洲男女内射在线播放 | 少妇性俱乐部纵欲狂欢电影 | 男人的天堂2018无码 | 国产亚洲精品久久久久久久久动漫 | 激情亚洲一区国产精品 | 国产亚洲精品久久久ai换 | 波多野结衣av在线观看 | 2020最新国产自产精品 | 中文字幕无码人妻少妇免费 | 久久99久久99精品中文字幕 | 成人欧美一区二区三区黑人免费 | 精品水蜜桃久久久久久久 | 欧美午夜特黄aaaaaa片 | 久久午夜无码鲁丝片 | 亚洲人成人无码网www国产 | 亚洲一区二区三区在线观看网站 | 亚洲精品综合一区二区三区在线 | 成人免费视频视频在线观看 免费 | 天天爽夜夜爽夜夜爽 | 精品无人区无码乱码毛片国产 | 久久亚洲中文字幕精品一区 | 久久99精品久久久久婷婷 | 无码av免费一区二区三区试看 | 人人妻人人澡人人爽精品欧美 | 欧美亚洲日韩国产人成在线播放 | 国产成人一区二区三区别 | 色综合视频一区二区三区 | 性色av无码免费一区二区三区 | 人妻与老人中文字幕 | 野狼第一精品社区 | 一本久道久久综合狠狠爱 | a片免费视频在线观看 | 精品国产福利一区二区 | 久久伊人色av天堂九九小黄鸭 | 亚洲区欧美区综合区自拍区 | 国产精品对白交换视频 | 国产女主播喷水视频在线观看 | 国产真实乱对白精彩久久 | 婷婷丁香六月激情综合啪 | 精品人人妻人人澡人人爽人人 | 天堂久久天堂av色综合 | 婷婷六月久久综合丁香 | 99精品国产综合久久久久五月天 | 久久久av男人的天堂 | 亚洲日韩一区二区三区 | 欧美黑人性暴力猛交喷水 | 无码人妻丰满熟妇区五十路百度 | 少妇一晚三次一区二区三区 | 女人高潮内射99精品 | 红桃av一区二区三区在线无码av | 精品无人国产偷自产在线 | 亚洲日韩av片在线观看 | 永久免费观看美女裸体的网站 | 综合人妻久久一区二区精品 | 亚洲精品www久久久 | 国产精品-区区久久久狼 | 国产亲子乱弄免费视频 | 精品久久8x国产免费观看 | 亚洲欧洲日本综合aⅴ在线 | 日本丰满护士爆乳xxxx | 免费无码av一区二区 | 特黄特色大片免费播放器图片 | 欧美大屁股xxxxhd黑色 | 波多野结衣aⅴ在线 | 无码人妻丰满熟妇区五十路百度 | 中文久久乱码一区二区 | 国产精品无码mv在线观看 | 全黄性性激高免费视频 | 在教室伦流澡到高潮hnp视频 | 色欲av亚洲一区无码少妇 | 国产卡一卡二卡三 | 性史性农村dvd毛片 | 免费网站看v片在线18禁无码 | 亚洲成av人综合在线观看 | 亚洲日韩乱码中文无码蜜桃臀网站 | 日韩精品无码一区二区中文字幕 | 国内揄拍国内精品少妇国语 | 国产精品高潮呻吟av久久 | 欧美日韩一区二区三区自拍 | 亚洲中文字幕无码中字 | 丰满妇女强制高潮18xxxx | 国产av人人夜夜澡人人爽麻豆 | 高清国产亚洲精品自在久久 | 亚洲中文无码av永久不收费 | 中国女人内谢69xxxxxa片 | 男女猛烈xx00免费视频试看 | 亚洲精品久久久久avwww潮水 | 最新版天堂资源中文官网 | 一个人看的视频www在线 | 欧美真人作爱免费视频 | 99视频精品全部免费免费观看 | 午夜时刻免费入口 | 麻豆果冻传媒2021精品传媒一区下载 | 午夜精品久久久内射近拍高清 | 国产特级毛片aaaaaaa高清 | 成人动漫在线观看 | а√资源新版在线天堂 | 国产精品第一区揄拍无码 | 一本久久a久久精品vr综合 | 亚洲中文字幕久久无码 | 性生交大片免费看l | 成人无码视频在线观看网站 | 又色又爽又黄的美女裸体网站 | 天天躁夜夜躁狠狠是什么心态 | 精品国产麻豆免费人成网站 | 欧美亚洲日韩国产人成在线播放 | 成人片黄网站色大片免费观看 | 在线a亚洲视频播放在线观看 | 国语自产偷拍精品视频偷 | 亚洲日本一区二区三区在线 | 成人三级无码视频在线观看 | 熟妇人妻无乱码中文字幕 | 国内综合精品午夜久久资源 | 少妇太爽了在线观看 | 国产午夜无码视频在线观看 | 88国产精品欧美一区二区三区 | 久久人人爽人人爽人人片av高清 | 狠狠色欧美亚洲狠狠色www | 免费国产黄网站在线观看 | 久久精品人妻少妇一区二区三区 | 亚洲国产日韩a在线播放 | 最新国产麻豆aⅴ精品无码 | 任你躁在线精品免费 | 免费男性肉肉影院 | 亚洲国产午夜精品理论片 | 亚洲另类伦春色综合小说 | 人妻少妇被猛烈进入中文字幕 | 两性色午夜免费视频 | 国产熟妇高潮叫床视频播放 | 国产农村妇女高潮大叫 | 欧美性生交xxxxx久久久 | 国产精品久久久久久亚洲毛片 | 小sao货水好多真紧h无码视频 | 最新国产麻豆aⅴ精品无码 | 四虎4hu永久免费 | 久久99精品久久久久婷婷 | 一本无码人妻在中文字幕免费 | 国产精品久久精品三级 | 成人综合网亚洲伊人 | 久久午夜无码鲁丝片 | 人妻天天爽夜夜爽一区二区 | аⅴ资源天堂资源库在线 | 欧美成人家庭影院 | 午夜精品久久久内射近拍高清 | 99久久精品日本一区二区免费 | 少妇性俱乐部纵欲狂欢电影 | 日本成熟视频免费视频 | 亚洲高清偷拍一区二区三区 | 蜜桃无码一区二区三区 | 日本熟妇人妻xxxxx人hd | 在线 国产 欧美 亚洲 天堂 | 国内精品人妻无码久久久影院 | 亚洲无人区午夜福利码高清完整版 | 十八禁视频网站在线观看 | 亚洲成av人综合在线观看 | av无码久久久久不卡免费网站 | 亚洲国产精品久久人人爱 | 久久亚洲国产成人精品性色 | 久久99久久99精品中文字幕 | 国产精品国产三级国产专播 | 无码播放一区二区三区 | 国产乱人伦偷精品视频 | 欧美变态另类xxxx | 成人片黄网站色大片免费观看 | 对白脏话肉麻粗话av | 精品乱码久久久久久久 | 国产亚洲tv在线观看 | ass日本丰满熟妇pics | 国产精品99爱免费视频 | 亚洲日韩精品欧美一区二区 | 国产莉萝无码av在线播放 | 亚洲成av人在线观看网址 | 少妇愉情理伦片bd | 丝袜足控一区二区三区 | 国产精品第一国产精品 | 人妻少妇精品无码专区动漫 | 国产精品无码一区二区三区不卡 | 永久黄网站色视频免费直播 | 强伦人妻一区二区三区视频18 | 亚洲人成人无码网www国产 | 日本成熟视频免费视频 | 国产精品办公室沙发 | 国产成人精品视频ⅴa片软件竹菊 | 亚洲成av人在线观看网址 | 国产在线精品一区二区高清不卡 | 中文精品无码中文字幕无码专区 | 日本一本二本三区免费 | 色一情一乱一伦一区二区三欧美 | 男女猛烈xx00免费视频试看 | 少妇性l交大片 | 国产精品怡红院永久免费 | 久久精品人妻少妇一区二区三区 | 日本丰满熟妇videos | 少妇人妻大乳在线视频 | 国产综合久久久久鬼色 | 亚洲精品无码国产 | 久久精品成人欧美大片 | 无码人妻出轨黑人中文字幕 | 成人欧美一区二区三区 | 无码人中文字幕 | 国产超级va在线观看视频 | 四虎影视成人永久免费观看视频 | 亚洲成在人网站无码天堂 | 成人无码精品一区二区三区 | 日本熟妇大屁股人妻 | 亚洲一区二区三区香蕉 | 无遮挡啪啪摇乳动态图 | 国产精品视频免费播放 | 97资源共享在线视频 | 天天av天天av天天透 | 久久精品中文闷骚内射 | 成人av无码一区二区三区 | 女高中生第一次破苞av | а天堂中文在线官网 | a国产一区二区免费入口 | 久久久精品欧美一区二区免费 | 久久久久久亚洲精品a片成人 | 国产在线精品一区二区高清不卡 | 欧美三级不卡在线观看 | 日本在线高清不卡免费播放 | 无遮挡啪啪摇乳动态图 | 国产成人综合在线女婷五月99播放 | 福利一区二区三区视频在线观看 | 欧美激情综合亚洲一二区 | 欧美熟妇另类久久久久久多毛 | 人人澡人摸人人添 | 国产成人无码av在线影院 | 男女猛烈xx00免费视频试看 | 少妇被粗大的猛进出69影院 | 男女爱爱好爽视频免费看 | 女人被男人爽到呻吟的视频 | 亚洲日韩精品欧美一区二区 | 99久久精品午夜一区二区 | 内射爽无广熟女亚洲 | 亚洲精品欧美二区三区中文字幕 | 最近免费中文字幕中文高清百度 | 激情内射亚州一区二区三区爱妻 | 中文字幕+乱码+中文字幕一区 | 久久天天躁夜夜躁狠狠 | 精品无码国产一区二区三区av | 欧美高清在线精品一区 | 俄罗斯老熟妇色xxxx | 大地资源网第二页免费观看 | 国产sm调教视频在线观看 | 国产精品久久久久9999小说 | 永久黄网站色视频免费直播 | 亚洲精品久久久久久一区二区 | а天堂中文在线官网 | 两性色午夜免费视频 | 日韩av无码一区二区三区不卡 | 亚洲国产精品久久久天堂 | 久久亚洲a片com人成 | 领导边摸边吃奶边做爽在线观看 | www成人国产高清内射 | 中文字幕乱码中文乱码51精品 | 99久久精品午夜一区二区 | 狂野欧美性猛xxxx乱大交 | 激情综合激情五月俺也去 | 久久97精品久久久久久久不卡 | 国产精品久久国产三级国 | 一本久久伊人热热精品中文字幕 | 高潮毛片无遮挡高清免费 | 国产三级精品三级男人的天堂 | 亚洲天堂2017无码中文 | 亚洲精品国产精品乱码视色 | 亚洲精品成人av在线 | 精品乱码久久久久久久 | 国产精品久久久久久无码 | 国产精品-区区久久久狼 | 国产办公室秘书无码精品99 | 精品国产乱码久久久久乱码 | av小次郎收藏 | 思思久久99热只有频精品66 | 一二三四社区在线中文视频 | 久久人人97超碰a片精品 | 欧美xxxx黑人又粗又长 | 精品国精品国产自在久国产87 | 成人综合网亚洲伊人 | 亚洲啪av永久无码精品放毛片 | 久久久久亚洲精品男人的天堂 | 国产99久久精品一区二区 | 国精品人妻无码一区二区三区蜜柚 | 国产黄在线观看免费观看不卡 | 丰满肥臀大屁股熟妇激情视频 | 极品尤物被啪到呻吟喷水 | 国产无遮挡吃胸膜奶免费看 | 波多野结衣一区二区三区av免费 | 欧美激情综合亚洲一二区 | 亚洲狠狠色丁香婷婷综合 | 国产艳妇av在线观看果冻传媒 | a在线亚洲男人的天堂 | 日本饥渴人妻欲求不满 | 性欧美大战久久久久久久 | 一本久道久久综合狠狠爱 | 欧美日韩一区二区综合 | 蜜桃臀无码内射一区二区三区 | 午夜肉伦伦影院 | 无码av岛国片在线播放 | 国产精品高潮呻吟av久久 | 国内综合精品午夜久久资源 | 中文字幕无码日韩专区 | 久久精品国产99久久6动漫 | 高清国产亚洲精品自在久久 | 一本色道久久综合狠狠躁 | 亚洲娇小与黑人巨大交 | 日韩视频 中文字幕 视频一区 | 精品乱子伦一区二区三区 | 日本精品高清一区二区 | 国产两女互慰高潮视频在线观看 | 国产成人无码av片在线观看不卡 | 欧美35页视频在线观看 | 欧美喷潮久久久xxxxx | 国精产品一区二区三区 | 亚洲色欲色欲欲www在线 | 日本www一道久久久免费榴莲 | 76少妇精品导航 | 在线看片无码永久免费视频 | 成年女人永久免费看片 | 一个人看的视频www在线 | 无码av岛国片在线播放 | 一本大道伊人av久久综合 | 荫蒂被男人添的好舒服爽免费视频 | 日本精品少妇一区二区三区 | 欧美精品国产综合久久 | 一本大道伊人av久久综合 | 亚洲欧美精品伊人久久 | 欧美午夜特黄aaaaaa片 | 中文字幕无码免费久久9一区9 | 人妻尝试又大又粗久久 | 无码任你躁久久久久久久 | 亚洲中文字幕成人无码 | 成人亚洲精品久久久久 | 麻豆果冻传媒2021精品传媒一区下载 | 在线精品国产一区二区三区 | 亚洲精品综合一区二区三区在线 | 亚洲の无码国产の无码步美 | 中文字幕无码av激情不卡 | 久久精品一区二区三区四区 | 欧美野外疯狂做受xxxx高潮 | 国产两女互慰高潮视频在线观看 | 狠狠色噜噜狠狠狠7777奇米 | 日本一区二区三区免费播放 | 成 人 网 站国产免费观看 | 国产精品18久久久久久麻辣 | 午夜不卡av免费 一本久久a久久精品vr综合 | 国产无av码在线观看 | 精品成人av一区二区三区 | 领导边摸边吃奶边做爽在线观看 | 精品欧洲av无码一区二区三区 | 久久久久久国产精品无码下载 | 欧洲熟妇精品视频 | 性欧美疯狂xxxxbbbb | 中文字幕人成乱码熟女app | 亚洲热妇无码av在线播放 | 国产精品99爱免费视频 | 日欧一片内射va在线影院 | 中文字幕av日韩精品一区二区 | 清纯唯美经典一区二区 | 久久亚洲精品成人无码 | 亚洲一区二区三区 | 亚洲国产精品一区二区美利坚 | 亚洲精品一区二区三区在线 | 又湿又紧又大又爽a视频国产 | 午夜肉伦伦影院 | 偷窥日本少妇撒尿chinese | 国产超碰人人爽人人做人人添 | 国产精品无码一区二区三区不卡 | 2020久久超碰国产精品最新 | 日韩无码专区 | 亚洲成av人片天堂网无码】 | 久久国产精品精品国产色婷婷 | 中文无码成人免费视频在线观看 | 久久综合给久久狠狠97色 | 亚洲精品国偷拍自产在线观看蜜桃 | 亚洲熟熟妇xxxx | 亚洲欧美色中文字幕在线 | 永久免费精品精品永久-夜色 | 日本一卡二卡不卡视频查询 | 天堂久久天堂av色综合 | 欧美性生交xxxxx久久久 | 中文字幕日产无线码一区 | 性生交大片免费看女人按摩摩 | 成年美女黄网站色大免费全看 | 国产一精品一av一免费 | 中文字幕乱码人妻无码久久 | 亚洲精品国偷拍自产在线观看蜜桃 | 色窝窝无码一区二区三区色欲 | 欧美精品无码一区二区三区 | 亚洲国产欧美国产综合一区 | 无码人妻精品一区二区三区不卡 | 狠狠色丁香久久婷婷综合五月 | 99久久精品午夜一区二区 | 无码人妻精品一区二区三区不卡 | 国产电影无码午夜在线播放 | 玩弄中年熟妇正在播放 | 欧美日韩久久久精品a片 | 久久人人爽人人人人片 | 荫蒂被男人添的好舒服爽免费视频 | 永久免费观看国产裸体美女 | 欧美怡红院免费全部视频 | 国产av一区二区三区最新精品 | 国产麻豆精品一区二区三区v视界 | 撕开奶罩揉吮奶头视频 | 亚洲 日韩 欧美 成人 在线观看 | 男人和女人高潮免费网站 | 性生交片免费无码看人 | 奇米综合四色77777久久 东京无码熟妇人妻av在线网址 | 国产精品久久久久久亚洲影视内衣 | 日本精品人妻无码77777 天堂一区人妻无码 | 中文字幕av日韩精品一区二区 | 真人与拘做受免费视频一 | 成人影院yy111111在线观看 | 人人爽人人澡人人高潮 | 无码成人精品区在线观看 | 自拍偷自拍亚洲精品10p | 日韩av无码一区二区三区不卡 | 最近免费中文字幕中文高清百度 | 激情综合激情五月俺也去 | 欧美丰满熟妇xxxx性ppx人交 | 久久99精品久久久久久动态图 | 青青青手机频在线观看 | 97无码免费人妻超级碰碰夜夜 | 久久99久久99精品中文字幕 | 精品人妻中文字幕有码在线 | 久久综合久久自在自线精品自 | 麻豆精品国产精华精华液好用吗 | 青春草在线视频免费观看 | 人人妻人人澡人人爽欧美一区九九 | 女人和拘做爰正片视频 | 久久久久人妻一区精品色欧美 | 天堂一区人妻无码 | 内射白嫩少妇超碰 | 国内精品久久久久久中文字幕 | 人妻插b视频一区二区三区 | www国产精品内射老师 | 亚洲男女内射在线播放 | 中文精品无码中文字幕无码专区 | 永久黄网站色视频免费直播 | 国产成人综合在线女婷五月99播放 | 欧美一区二区三区 | 四虎国产精品免费久久 | 国产精品亚洲五月天高清 | 99久久人妻精品免费一区 | 亚洲精品久久久久中文第一幕 | 亚欧洲精品在线视频免费观看 | 亚洲一区二区三区无码久久 | 亲嘴扒胸摸屁股激烈网站 | 亚洲中文字幕乱码av波多ji | 欧美日本精品一区二区三区 | 漂亮人妻洗澡被公强 日日躁 | 国产极品视觉盛宴 | 国产两女互慰高潮视频在线观看 | 精品久久久久香蕉网 | 丁香啪啪综合成人亚洲 | 装睡被陌生人摸出水好爽 | 欧美xxxx黑人又粗又长 | 日本精品人妻无码免费大全 | 女人高潮内射99精品 | 亚洲熟妇自偷自拍另类 | 欧美野外疯狂做受xxxx高潮 | 成在人线av无码免观看麻豆 | 好男人www社区 | 精品国产精品久久一区免费式 | 桃花色综合影院 | 国产在线无码精品电影网 | 国产一区二区三区日韩精品 | 国产精品无码mv在线观看 | 特级做a爰片毛片免费69 | 人人妻人人澡人人爽人人精品浪潮 | 亚洲日韩乱码中文无码蜜桃臀网站 | a国产一区二区免费入口 | 国产人妻精品一区二区三区 | 99久久久无码国产精品免费 | 欧美三级不卡在线观看 | 四虎永久在线精品免费网址 | 无码一区二区三区在线观看 | 久久亚洲精品成人无码 | 亚洲欧美日韩成人高清在线一区 | 麻豆国产97在线 | 欧洲 | 精品亚洲成av人在线观看 | 亚洲综合色区中文字幕 | 鲁大师影院在线观看 | 国产高清av在线播放 | 夜夜影院未满十八勿进 | 伊人久久大香线焦av综合影院 | 久久伊人色av天堂九九小黄鸭 | a国产一区二区免费入口 | 亚洲欧洲日本无在线码 | 中文字幕无码av波多野吉衣 | 中文字幕久久久久人妻 | 55夜色66夜色国产精品视频 | 国产一区二区三区四区五区加勒比 | 精品国产青草久久久久福利 | 荡女精品导航 | 丰满诱人的人妻3 | 在线a亚洲视频播放在线观看 | 人妻天天爽夜夜爽一区二区 | 久久 国产 尿 小便 嘘嘘 | 国语自产偷拍精品视频偷 | 色一情一乱一伦 | 欧美精品无码一区二区三区 | 内射巨臀欧美在线视频 | 美女极度色诱视频国产 | 亚洲欧洲中文日韩av乱码 | 亚洲精品成人av在线 | 国产亚洲精品久久久久久久 | 老子影院午夜精品无码 | 色噜噜亚洲男人的天堂 | 噜噜噜亚洲色成人网站 | 18禁止看的免费污网站 | 领导边摸边吃奶边做爽在线观看 | 日日摸夜夜摸狠狠摸婷婷 | 精品无码一区二区三区爱欲 | 精品乱码久久久久久久 | 国产免费无码一区二区视频 | 精品国产aⅴ无码一区二区 | 国产内射爽爽大片视频社区在线 | 鲁一鲁av2019在线 | 亚洲国产日韩a在线播放 | 久久久精品国产sm最大网站 | 丝袜美腿亚洲一区二区 | 亚洲精品一区二区三区在线 | 无码人中文字幕 | 免费国产成人高清在线观看网站 | 中文字幕人成乱码熟女app | 国产疯狂伦交大片 | 娇妻被黑人粗大高潮白浆 | 国精产品一区二区三区 | 300部国产真实乱 | 亚洲综合久久一区二区 | 亚洲人成无码网www | 欧美变态另类xxxx | 国产精品久久久久久久影院 | 成人三级无码视频在线观看 | 亚洲欧洲中文日韩av乱码 | 日日橹狠狠爱欧美视频 | 女高中生第一次破苞av | 国产va免费精品观看 | 日本熟妇乱子伦xxxx | 捆绑白丝粉色jk震动捧喷白浆 | 精品国产福利一区二区 | 欧美熟妇另类久久久久久不卡 | 亚洲人成无码网www | 欧美高清在线精品一区 | 中文字幕av伊人av无码av | 免费无码肉片在线观看 | 久久无码中文字幕免费影院蜜桃 | 激情内射亚州一区二区三区爱妻 | 无码av中文字幕免费放 | 日韩欧美群交p片內射中文 | 精品久久久中文字幕人妻 | 正在播放东北夫妻内射 | 亚洲国产精品一区二区第一页 | 日韩精品无码免费一区二区三区 | 国产一区二区不卡老阿姨 | 少妇性l交大片欧洲热妇乱xxx | 国产精品久久福利网站 | 国产午夜亚洲精品不卡 | 亚无码乱人伦一区二区 | 天干天干啦夜天干天2017 | 国产口爆吞精在线视频 | 亚洲精品无码国产 | 日韩人妻系列无码专区 | √8天堂资源地址中文在线 | 亚洲乱亚洲乱妇50p | 性生交大片免费看女人按摩摩 | 少妇人妻偷人精品无码视频 | 日韩亚洲欧美中文高清在线 | 免费无码av一区二区 | 欧美xxxx黑人又粗又长 | 国产婷婷色一区二区三区在线 | 秋霞特色aa大片 | 国产精品igao视频网 | 激情内射亚州一区二区三区爱妻 | 国产精品内射视频免费 | 性欧美牲交xxxxx视频 | 国产一精品一av一免费 | 国产精品igao视频网 | 国产两女互慰高潮视频在线观看 | 亚洲爆乳大丰满无码专区 | 欧美性生交活xxxxxdddd | 国内少妇偷人精品视频免费 | 丝袜美腿亚洲一区二区 | 久久综合久久自在自线精品自 | 大肉大捧一进一出好爽视频 | 中文字幕 亚洲精品 第1页 | 国产色精品久久人妻 | 国产一区二区三区四区五区加勒比 | 玩弄人妻少妇500系列视频 | 超碰97人人做人人爱少妇 | 丰满岳乱妇在线观看中字无码 | 欧美freesex黑人又粗又大 | 午夜成人1000部免费视频 | 永久黄网站色视频免费直播 | 国产麻豆精品一区二区三区v视界 | 亚洲色大成网站www国产 | 亚洲国产成人a精品不卡在线 | 自拍偷自拍亚洲精品被多人伦好爽 | 日本一本二本三区免费 | 久久久久免费看成人影片 | 亚洲综合色区中文字幕 | 狠狠色丁香久久婷婷综合五月 | 自拍偷自拍亚洲精品10p | 久久精品国产一区二区三区肥胖 | 国产舌乚八伦偷品w中 | 狠狠色丁香久久婷婷综合五月 | 欧美精品国产综合久久 | 亚洲性无码av中文字幕 | 日本乱偷人妻中文字幕 | 97久久精品无码一区二区 | 久久久久成人片免费观看蜜芽 | 两性色午夜免费视频 | av人摸人人人澡人人超碰下载 | 九九综合va免费看 | 精品国产一区二区三区四区在线看 | 欧美人与动性行为视频 | 一二三四社区在线中文视频 | 国产成人无码av在线影院 | 岛国片人妻三上悠亚 | 澳门永久av免费网站 | 小泽玛莉亚一区二区视频在线 | 欧美人与禽zoz0性伦交 | 最新版天堂资源中文官网 | √天堂资源地址中文在线 | 久久成人a毛片免费观看网站 | 图片区 小说区 区 亚洲五月 | 久久综合九色综合欧美狠狠 | 国产办公室秘书无码精品99 | 久久久久免费精品国产 | 久久国产精品精品国产色婷婷 | 精品少妇爆乳无码av无码专区 | 国内揄拍国内精品少妇国语 | 久久天天躁狠狠躁夜夜免费观看 | 国产成人精品一区二区在线小狼 | 亚洲中文字幕无码中字 | 国语自产偷拍精品视频偷 | 成人精品天堂一区二区三区 | 亚洲综合无码一区二区三区 | 波多野结衣乳巨码无在线观看 | 国产xxx69麻豆国语对白 | 在线欧美精品一区二区三区 | 欧洲vodafone精品性 | 亚洲a无码综合a国产av中文 | 国产成人人人97超碰超爽8 | 中文字幕亚洲情99在线 | 中文字幕色婷婷在线视频 | 亚洲色欲久久久综合网东京热 | 樱花草在线播放免费中文 | 日日夜夜撸啊撸 | 欧美 日韩 亚洲 在线 | 日韩精品成人一区二区三区 | 欧美第一黄网免费网站 | 久青草影院在线观看国产 | 国产 浪潮av性色四虎 | 在教室伦流澡到高潮hnp视频 | 无码人妻久久一区二区三区不卡 | 人妻少妇精品视频专区 | 玩弄人妻少妇500系列视频 | 强辱丰满人妻hd中文字幕 | 亚洲午夜无码久久 | 日韩av无码一区二区三区不卡 | 国产精品亚洲综合色区韩国 | 伊人久久大香线焦av综合影院 | 亚洲日韩av一区二区三区四区 | 少妇久久久久久人妻无码 | 国产精品自产拍在线观看 | 久久亚洲日韩精品一区二区三区 | 日日天日日夜日日摸 | 波多野结衣乳巨码无在线观看 | 久久精品国产99精品亚洲 | 粉嫩少妇内射浓精videos | 精品国精品国产自在久国产87 | 国内少妇偷人精品视频免费 | 亚洲 欧美 激情 小说 另类 | 999久久久国产精品消防器材 | 精品国产成人一区二区三区 | 97夜夜澡人人双人人人喊 | 丁香啪啪综合成人亚洲 | 成 人 免费观看网站 | 亚洲伊人久久精品影院 | 国产精品久久久久久久影院 | 大地资源网第二页免费观看 | 玩弄中年熟妇正在播放 | 无码一区二区三区在线观看 | 国产亚av手机在线观看 | 亚洲欧洲日本综合aⅴ在线 | 成年女人永久免费看片 | 国产亚洲精品久久久闺蜜 | 国产农村妇女高潮大叫 | 久久aⅴ免费观看 | 啦啦啦www在线观看免费视频 | 日本成熟视频免费视频 | 内射欧美老妇wbb | 中文字幕精品av一区二区五区 | 在线播放免费人成毛片乱码 | 日本大香伊一区二区三区 | 国产精品理论片在线观看 | 蜜臀aⅴ国产精品久久久国产老师 | 久久久久久国产精品无码下载 | 精品国产一区av天美传媒 | 亚洲色欲久久久综合网东京热 | 国产精品igao视频网 | av无码电影一区二区三区 | 中文字幕乱码中文乱码51精品 | 性啪啪chinese东北女人 | 国产成人一区二区三区别 | 天堂亚洲免费视频 | 欧美日韩久久久精品a片 | 国内精品人妻无码久久久影院蜜桃 | 精品无人区无码乱码毛片国产 | 欧洲vodafone精品性 | 国产精品久久国产三级国 | 精品无码国产自产拍在线观看蜜 | 99久久久无码国产aaa精品 | 久久五月精品中文字幕 | 欧美熟妇另类久久久久久多毛 | 天海翼激烈高潮到腰振不止 | 久久人人爽人人爽人人片ⅴ | 永久免费观看国产裸体美女 | 六十路熟妇乱子伦 | 欧美激情综合亚洲一二区 | 人人爽人人澡人人高潮 | 日韩欧美群交p片內射中文 | 久久久久亚洲精品中文字幕 | 在线观看国产一区二区三区 | 日韩av无码一区二区三区 | 国产尤物精品视频 | 无码免费一区二区三区 | 玩弄少妇高潮ⅹxxxyw | 国内少妇偷人精品视频免费 | 亚洲国产精华液网站w | 免费人成在线视频无码 | 久久久精品国产sm最大网站 | 日本高清一区免费中文视频 | 2020久久香蕉国产线看观看 | 真人与拘做受免费视频一 | 亚洲成熟女人毛毛耸耸多 | 亚洲精品一区二区三区在线 | 久久久久成人精品免费播放动漫 | 久久久久久九九精品久 | 一本久久a久久精品vr综合 | 久久国产36精品色熟妇 | 日本一区二区三区免费播放 | 永久免费观看美女裸体的网站 | 亚洲一区二区三区香蕉 | 乱人伦中文视频在线观看 | 国产精品久久国产三级国 | 国产人妻人伦精品 | 人人妻人人澡人人爽欧美一区九九 | 中文无码成人免费视频在线观看 | 亚洲熟妇色xxxxx亚洲 | 国产精品福利视频导航 | 黄网在线观看免费网站 | 日日摸天天摸爽爽狠狠97 | 国产人妻大战黑人第1集 | 中文字幕乱妇无码av在线 | 人妻插b视频一区二区三区 | 蜜桃臀无码内射一区二区三区 | 欧美激情内射喷水高潮 | 无码人妻精品一区二区三区下载 | 亚洲精品久久久久中文第一幕 | 日本精品少妇一区二区三区 | 最新国产乱人伦偷精品免费网站 | 日韩av无码一区二区三区不卡 | 亚洲精品欧美二区三区中文字幕 | 日本护士毛茸茸高潮 | 成人女人看片免费视频放人 | 亚洲另类伦春色综合小说 | 久久久久久久人妻无码中文字幕爆 | 少妇邻居内射在线 | 亚洲精品国偷拍自产在线观看蜜桃 | 欧美一区二区三区视频在线观看 | 精品无码一区二区三区爱欲 | 久久久久av无码免费网 | 最近的中文字幕在线看视频 | 任你躁国产自任一区二区三区 | 国产精品18久久久久久麻辣 | 亚洲热妇无码av在线播放 | 国产成人无码区免费内射一片色欲 | 亚洲高清偷拍一区二区三区 | 亚洲自偷自偷在线制服 | 熟女少妇在线视频播放 | 无码人妻av免费一区二区三区 | 丰满护士巨好爽好大乳 | 色婷婷av一区二区三区之红樱桃 | 国产乱人偷精品人妻a片 | 精品国产乱码久久久久乱码 | 国产人妖乱国产精品人妖 | 亚洲春色在线视频 | 极品嫩模高潮叫床 | 3d动漫精品啪啪一区二区中 | www国产精品内射老师 | 国产农村乱对白刺激视频 | 久久午夜夜伦鲁鲁片无码免费 | 成 人影片 免费观看 | 精品欧美一区二区三区久久久 | 午夜精品一区二区三区在线观看 | 亚洲精品欧美二区三区中文字幕 | 久久午夜无码鲁丝片秋霞 | 国产美女极度色诱视频www | 国产偷自视频区视频 | 精品国产精品久久一区免费式 | 在线播放亚洲第一字幕 | 女人被男人爽到呻吟的视频 | 日韩亚洲欧美精品综合 | 国产精品无码一区二区三区不卡 | 一本久久a久久精品亚洲 | 亚洲国产av美女网站 | 377p欧洲日本亚洲大胆 | 天堂а√在线中文在线 | 亚洲综合精品香蕉久久网 | 精品国产麻豆免费人成网站 | 性做久久久久久久久 | 人妻中文无码久热丝袜 | 中文久久乱码一区二区 | 奇米影视7777久久精品人人爽 | 天天做天天爱天天爽综合网 | 中文字幕乱码亚洲无线三区 | 成人无码精品一区二区三区 | 伊人久久大香线蕉av一区二区 | 丰满人妻翻云覆雨呻吟视频 | 俺去俺来也www色官网 | 国产色xx群视频射精 | 亚洲aⅴ无码成人网站国产app | 领导边摸边吃奶边做爽在线观看 | 99久久久无码国产aaa精品 | 久久久久免费精品国产 | 强辱丰满人妻hd中文字幕 | 亚洲无人区一区二区三区 | 色婷婷欧美在线播放内射 | 东北女人啪啪对白 | 国产乱人无码伦av在线a | 亚洲日韩精品欧美一区二区 | 人妻天天爽夜夜爽一区二区 | 国产偷国产偷精品高清尤物 | 欧美人与禽猛交狂配 | 中文无码成人免费视频在线观看 | 强辱丰满人妻hd中文字幕 | 伊人色综合久久天天小片 | 在教室伦流澡到高潮hnp视频 | 99精品久久毛片a片 | 18禁黄网站男男禁片免费观看 | 国产激情无码一区二区 | 国内精品一区二区三区不卡 | 2019午夜福利不卡片在线 | 欧美丰满老熟妇xxxxx性 | 中文久久乱码一区二区 | 国产亚洲人成在线播放 | 午夜精品久久久久久久久 | 婷婷丁香六月激情综合啪 | 无码纯肉视频在线观看 | 无码福利日韩神码福利片 | 中国女人内谢69xxxx | 无码播放一区二区三区 | 在线а√天堂中文官网 | 成人精品视频一区二区三区尤物 | 丰满肥臀大屁股熟妇激情视频 | 在线观看国产午夜福利片 | 久久午夜无码鲁丝片午夜精品 | 中文毛片无遮挡高清免费 | 日韩精品乱码av一区二区 | www国产精品内射老师 | 亚洲一区二区三区无码久久 | 亚洲码国产精品高潮在线 | 亚洲成av人影院在线观看 | 无码av免费一区二区三区试看 | 亚洲精品综合一区二区三区在线 | 日本免费一区二区三区最新 | 久久综合激激的五月天 | 人人妻在人人 | 亚洲另类伦春色综合小说 | 色五月丁香五月综合五月 | 国产精品久久久久影院嫩草 | 国产精品无码久久av | 丰满妇女强制高潮18xxxx | 秋霞成人午夜鲁丝一区二区三区 | 精品一区二区三区无码免费视频 | 永久黄网站色视频免费直播 | 粉嫩少妇内射浓精videos | 荡女精品导航 | 久久国语露脸国产精品电影 | 久久国产36精品色熟妇 | 精品无码一区二区三区爱欲 | 人妻少妇被猛烈进入中文字幕 | 综合网日日天干夜夜久久 | 超碰97人人射妻 | 中文字幕 亚洲精品 第1页 | 又大又紧又粉嫩18p少妇 | 亚洲第一无码av无码专区 | 日日碰狠狠丁香久燥 | 亚洲精品国产精品乱码视色 | 少妇高潮喷潮久久久影院 | 国内综合精品午夜久久资源 | 色欲av亚洲一区无码少妇 | 中文字幕亚洲情99在线 | 欧美 丝袜 自拍 制服 另类 | 国内丰满熟女出轨videos | 亚洲精品久久久久久久久久久 | 国产电影无码午夜在线播放 | 狠狠色色综合网站 | 性欧美牲交xxxxx视频 | 无码播放一区二区三区 | 伊人久久大香线蕉午夜 | аⅴ资源天堂资源库在线 | 成年美女黄网站色大免费全看 | 在线亚洲高清揄拍自拍一品区 | 97久久精品无码一区二区 | 天天综合网天天综合色 | 久久zyz资源站无码中文动漫 | 欧美人与善在线com | 久久亚洲精品成人无码 | 97se亚洲精品一区 | 亚洲综合色区中文字幕 | 理论片87福利理论电影 | 在线观看国产一区二区三区 | 1000部夫妻午夜免费 | 免费无码一区二区三区蜜桃大 | 日日摸日日碰夜夜爽av | 少妇厨房愉情理9仑片视频 | 蜜桃视频韩日免费播放 | 综合网日日天干夜夜久久 | 亚洲狠狠色丁香婷婷综合 | 亚洲七七久久桃花影院 | 国产亚洲精品精品国产亚洲综合 | 国产真人无遮挡作爱免费视频 | 国产免费久久久久久无码 | 搡女人真爽免费视频大全 | 国产精品丝袜黑色高跟鞋 | 水蜜桃色314在线观看 | 少妇性l交大片 | 国产成人精品优优av | 国产莉萝无码av在线播放 | 国精产品一品二品国精品69xx | 欧美 日韩 人妻 高清 中文 | 亚洲欧洲中文日韩av乱码 | 玩弄中年熟妇正在播放 | 亚洲男人av香蕉爽爽爽爽 | 国产精品永久免费视频 | 久久综合给久久狠狠97色 | 亚洲码国产精品高潮在线 | 欧美成人家庭影院 | 无码精品人妻一区二区三区av | 亚洲成av人影院在线观看 | 乱码av麻豆丝袜熟女系列 | 漂亮人妻洗澡被公强 日日躁 | 久久久久人妻一区精品色欧美 | 好男人www社区 | 亚洲欧美日韩综合久久久 | 纯爱无遮挡h肉动漫在线播放 | 亚洲狠狠婷婷综合久久 | 国产熟妇高潮叫床视频播放 | 亚洲啪av永久无码精品放毛片 | 人妻有码中文字幕在线 | 国产精品亚洲一区二区三区喷水 | 久久婷婷五月综合色国产香蕉 | 久久国产36精品色熟妇 | 免费观看黄网站 | 捆绑白丝粉色jk震动捧喷白浆 | 午夜福利试看120秒体验区 | 国产无套粉嫩白浆在线 | 色一情一乱一伦一区二区三欧美 | 国产精品-区区久久久狼 | 国产真实伦对白全集 | 国产精品久久久久久久9999 | 国产精品无码久久av | 妺妺窝人体色www在线小说 | 一本久道久久综合婷婷五月 | 国产极品视觉盛宴 | 精品一二三区久久aaa片 | 伊人久久婷婷五月综合97色 | 国产精品第一区揄拍无码 | 日日摸夜夜摸狠狠摸婷婷 | 国产精品无套呻吟在线 | 日日天干夜夜狠狠爱 | 人妻熟女一区 | 欧洲vodafone精品性 | 激情国产av做激情国产爱 | 精品久久综合1区2区3区激情 | 亚洲一区二区三区在线观看网站 | 高中生自慰www网站 | 成人精品天堂一区二区三区 | 未满成年国产在线观看 | 亚洲色无码一区二区三区 | 色欲综合久久中文字幕网 | 久久久久国色av免费观看性色 | 国产人成高清在线视频99最全资源 | 欧美 日韩 亚洲 在线 | 国产sm调教视频在线观看 | 国产sm调教视频在线观看 | 亚洲第一网站男人都懂 | 激情亚洲一区国产精品 | 国产69精品久久久久app下载 | 色欲av亚洲一区无码少妇 | 日本一本二本三区免费 | 国精产品一区二区三区 | 欧美日韩一区二区免费视频 | 麻豆md0077饥渴少妇 | 男人的天堂av网站 | 国产成人综合色在线观看网站 | 18黄暴禁片在线观看 | 无码午夜成人1000部免费视频 | 久久国产自偷自偷免费一区调 | 国产另类ts人妖一区二区 | 亚洲精品国产品国语在线观看 | 美女毛片一区二区三区四区 | 国内少妇偷人精品视频免费 | 欧美 日韩 亚洲 在线 | 美女极度色诱视频国产 | 7777奇米四色成人眼影 | 欧美人与牲动交xxxx | 国产成人综合美国十次 | 伊人久久婷婷五月综合97色 | 亚洲精品成a人在线观看 | 18精品久久久无码午夜福利 | 亚洲欧美日韩综合久久久 | 亚洲区小说区激情区图片区 | 大地资源网第二页免费观看 | 久久亚洲a片com人成 | 丰满少妇高潮惨叫视频 | av在线亚洲欧洲日产一区二区 | 国产精品va在线观看无码 | 亚洲欧美色中文字幕在线 | 理论片87福利理论电影 | 欧美大屁股xxxxhd黑色 | 中文字幕乱妇无码av在线 | 国产舌乚八伦偷品w中 | 精品一二三区久久aaa片 | 黑人玩弄人妻中文在线 | 日韩人妻无码一区二区三区久久99 | 熟妇人妻无码xxx视频 | www国产精品内射老师 | 亚洲中文字幕乱码av波多ji | 国产精品亚洲а∨无码播放麻豆 | 伊人久久大香线蕉av一区二区 | 久久久久99精品国产片 | 天干天干啦夜天干天2017 | 欧美黑人性暴力猛交喷水 | 香港三级日本三级妇三级 | 亚洲中文字幕在线无码一区二区 | 国产精品亚洲综合色区韩国 | 久久久无码中文字幕久... | 日本熟妇大屁股人妻 | 久久综合香蕉国产蜜臀av | 少妇愉情理伦片bd | 国产va免费精品观看 | 高潮毛片无遮挡高清免费视频 | 国内精品一区二区三区不卡 | 熟妇人妻无码xxx视频 | 正在播放老肥熟妇露脸 | 亚洲aⅴ无码成人网站国产app | 日韩av无码中文无码电影 | 亚洲综合伊人久久大杳蕉 | 亚洲精品中文字幕乱码 | 国产精品igao视频网 | 亚洲国产精品一区二区美利坚 | 国产精品丝袜黑色高跟鞋 | 午夜福利一区二区三区在线观看 | 十八禁真人啪啪免费网站 | 日本大乳高潮视频在线观看 | 精品国产一区av天美传媒 | 97无码免费人妻超级碰碰夜夜 | 丰满护士巨好爽好大乳 | 天天摸天天碰天天添 | 99久久人妻精品免费一区 | 色婷婷综合激情综在线播放 | 激情内射亚州一区二区三区爱妻 | 中文亚洲成a人片在线观看 | 夜先锋av资源网站 | 老司机亚洲精品影院 | 无码毛片视频一区二区本码 | 国产精品无码成人午夜电影 | 亚洲精品国产精品乱码不卡 | 又紧又大又爽精品一区二区 | 乱人伦人妻中文字幕无码久久网 | 领导边摸边吃奶边做爽在线观看 | 日韩精品无码一本二本三本色 | 免费中文字幕日韩欧美 | 成人影院yy111111在线观看 | 最新国产乱人伦偷精品免费网站 | 丰满少妇人妻久久久久久 | 国内精品人妻无码久久久影院蜜桃 | 一本久久a久久精品vr综合 | 亚洲国精产品一二二线 | √天堂资源地址中文在线 | 国产精品久久久久久久9999 | 最近中文2019字幕第二页 | 久久亚洲精品成人无码 | 久久熟妇人妻午夜寂寞影院 | 一本久久a久久精品vr综合 | 午夜无码人妻av大片色欲 | 毛片内射-百度 | 国产色在线 | 国产 | 亚洲精品午夜无码电影网 | 爽爽影院免费观看 | 亚洲一区二区三区四区 | √8天堂资源地址中文在线 | 国产精品-区区久久久狼 | 亚洲中文字幕久久无码 | 国产亚洲精品精品国产亚洲综合 | 男女超爽视频免费播放 | 无码一区二区三区在线观看 | 综合激情五月综合激情五月激情1 | 欧美老妇与禽交 | 亚洲欧美日韩综合久久久 | 亚洲一区av无码专区在线观看 | 国产女主播喷水视频在线观看 | 国产熟女一区二区三区四区五区 | 亚洲gv猛男gv无码男同 | 久久综合激激的五月天 | 成人综合网亚洲伊人 | 老司机亚洲精品影院无码 | 成在人线av无码免观看麻豆 | 夜夜夜高潮夜夜爽夜夜爰爰 | 狠狠综合久久久久综合网 | 欧美激情内射喷水高潮 | 中文字幕+乱码+中文字幕一区 | 在线观看国产一区二区三区 | 女人被爽到呻吟gif动态图视看 | 国产免费无码一区二区视频 | 色一情一乱一伦 | 精品无码国产自产拍在线观看蜜 | 少妇被粗大的猛进出69影院 | 精品无码一区二区三区的天堂 | 97久久国产亚洲精品超碰热 | 骚片av蜜桃精品一区 | 粉嫩少妇内射浓精videos | 亚洲综合精品香蕉久久网 | 亚洲精品综合一区二区三区在线 | 人妻少妇精品久久 | 麻豆果冻传媒2021精品传媒一区下载 | a片免费视频在线观看 | 色婷婷综合激情综在线播放 | 在教室伦流澡到高潮hnp视频 | 亚洲熟熟妇xxxx | 国产精品鲁鲁鲁 | 好男人www社区 | 中国女人内谢69xxxxxa片 | 兔费看少妇性l交大片免费 | 亚洲s码欧洲m码国产av | 成年美女黄网站色大免费视频 | 国产口爆吞精在线视频 | 亚洲 另类 在线 欧美 制服 | 丰满少妇女裸体bbw | 欧美丰满熟妇xxxx | 国产凸凹视频一区二区 | 日本大乳高潮视频在线观看 | 国产无套内射久久久国产 | 四虎国产精品一区二区 | 99精品国产综合久久久久五月天 | 亚洲欧美国产精品久久 | 亚洲一区av无码专区在线观看 | 久久精品国产99久久6动漫 | 一本大道伊人av久久综合 | 久久精品视频在线看15 | 色噜噜亚洲男人的天堂 | 荫蒂添的好舒服视频囗交 | 亚洲国产一区二区三区在线观看 | 亚洲精品成人福利网站 | 欧美怡红院免费全部视频 | 人妻少妇精品无码专区动漫 | 色欲久久久天天天综合网精品 | 成人精品天堂一区二区三区 | 少妇性l交大片欧洲热妇乱xxx | 精品国产乱码久久久久乱码 | 性生交大片免费看l | 国产精品高潮呻吟av久久 | 国产精品美女久久久 | 亚洲精品久久久久avwww潮水 | 少妇无套内谢久久久久 | 日韩欧美中文字幕公布 | 国色天香社区在线视频 | 欧美肥老太牲交大战 | 97人妻精品一区二区三区 | 中国大陆精品视频xxxx | 三上悠亚人妻中文字幕在线 | 国产av剧情md精品麻豆 | 日韩成人一区二区三区在线观看 | 99久久婷婷国产综合精品青草免费 | 亚洲日韩av片在线观看 | 男女作爱免费网站 | 亚洲国精产品一二二线 | 亚洲色大成网站www | 最近免费中文字幕中文高清百度 | 呦交小u女精品视频 | 欧美日韩在线亚洲综合国产人 | 国产成人无码av片在线观看不卡 | 熟妇女人妻丰满少妇中文字幕 | 少妇无码av无码专区在线观看 | 奇米综合四色77777久久 东京无码熟妇人妻av在线网址 | 日韩无套无码精品 | 国产农村乱对白刺激视频 | 久久久精品国产sm最大网站 | 亚洲 欧美 激情 小说 另类 | 久久久成人毛片无码 | 国产精品久久久一区二区三区 | 真人与拘做受免费视频一 | 西西人体www44rt大胆高清 | 无码午夜成人1000部免费视频 | 成人片黄网站色大片免费观看 | 99久久久无码国产精品免费 | 97夜夜澡人人双人人人喊 | 国产精品a成v人在线播放 | 在线精品亚洲一区二区 | 真人与拘做受免费视频 | 精品国产青草久久久久福利 | 国产乱人伦app精品久久 国产在线无码精品电影网 国产国产精品人在线视 | 国产成人av免费观看 | 妺妺窝人体色www在线小说 | 色情久久久av熟女人妻网站 | 一本无码人妻在中文字幕免费 | 国产午夜福利亚洲第一 | 国产黑色丝袜在线播放 | 成人三级无码视频在线观看 | 亚洲精品www久久久 | 性生交大片免费看l | 无码吃奶揉捏奶头高潮视频 | 无码毛片视频一区二区本码 | 未满成年国产在线观看 | 国产av久久久久精东av | 中文字幕无码免费久久99 | 在教室伦流澡到高潮hnp视频 | 国产成人无码av一区二区 | 国产成人无码av在线影院 | 天天做天天爱天天爽综合网 | 国产午夜无码视频在线观看 | 国产午夜手机精彩视频 | 中文字幕人成乱码熟女app | 麻豆精产国品 | 亚洲欧洲日本综合aⅴ在线 | 欧美人与善在线com | 国产精品毛多多水多 | 国产成人久久精品流白浆 | 精品无码国产自产拍在线观看蜜 | 精品无人国产偷自产在线 | 天天做天天爱天天爽综合网 | 日韩精品一区二区av在线 | 牲欲强的熟妇农村老妇女视频 | 国产真实乱对白精彩久久 | 伊人久久大香线焦av综合影院 | 中文字幕 亚洲精品 第1页 | 老熟妇乱子伦牲交视频 | 2019nv天堂香蕉在线观看 | 全球成人中文在线 | 台湾无码一区二区 | 99久久婷婷国产综合精品青草免费 | 欧美日本免费一区二区三区 | 国内精品人妻无码久久久影院蜜桃 | 亚洲午夜久久久影院 | 亚洲国产欧美在线成人 | 国产成人无码区免费内射一片色欲 | 久久综合香蕉国产蜜臀av | 国产精品怡红院永久免费 | 图片区 小说区 区 亚洲五月 | 国产又爽又黄又刺激的视频 | www国产亚洲精品久久久日本 | 成人精品视频一区二区三区尤物 | 国产sm调教视频在线观看 | 无遮挡国产高潮视频免费观看 | 麻豆精产国品 | 久久午夜无码鲁丝片秋霞 | 国产成人综合在线女婷五月99播放 | 国产午夜福利亚洲第一 | 狠狠色丁香久久婷婷综合五月 | 六月丁香婷婷色狠狠久久 | 中文无码成人免费视频在线观看 | 国产成人精品视频ⅴa片软件竹菊 | 国产激情综合五月久久 | 少妇性l交大片欧洲热妇乱xxx | 亚洲熟妇色xxxxx欧美老妇 | 97久久国产亚洲精品超碰热 | 麻豆国产人妻欲求不满谁演的 | 国产亚洲精品久久久久久久久动漫 | 国产精品久久久久久亚洲影视内衣 | 欧美喷潮久久久xxxxx | 久久久久久久女国产乱让韩 | 樱花草在线播放免费中文 | 300部国产真实乱 | 久久久久国色av免费观看性色 | 狠狠综合久久久久综合网 | 亚洲国产av精品一区二区蜜芽 | 日韩亚洲欧美精品综合 | 四虎4hu永久免费 | 亚洲熟妇色xxxxx亚洲 | 欧美真人作爱免费视频 | 国产亚洲视频中文字幕97精品 | 亚欧洲精品在线视频免费观看 | 亚洲精品中文字幕乱码 | 老子影院午夜精品无码 | 国产亚洲tv在线观看 | 久久aⅴ免费观看 | 色欲人妻aaaaaaa无码 | 国产做国产爱免费视频 | 国产成人亚洲综合无码 | 国产午夜精品一区二区三区嫩草 | www国产精品内射老师 | 久久精品一区二区三区四区 | 亚洲国产欧美国产综合一区 | 亚洲精品美女久久久久久久 | 红桃av一区二区三区在线无码av | 人妻中文无码久热丝袜 | 人人澡人人妻人人爽人人蜜桃 | 性生交大片免费看l | 国内精品一区二区三区不卡 | 亚洲精品久久久久avwww潮水 | 亚洲午夜无码久久 | 77777熟女视频在线观看 а天堂中文在线官网 | 内射后入在线观看一区 | 色欲人妻aaaaaaa无码 | 色一情一乱一伦 | 无遮挡国产高潮视频免费观看 | 熟妇人妻无码xxx视频 | 色婷婷香蕉在线一区二区 | 亚洲成av人片在线观看无码不卡 | 国产乱子伦视频在线播放 | 日韩精品a片一区二区三区妖精 | 亚洲精品国产精品乱码不卡 | 人人妻人人澡人人爽精品欧美 | 免费观看又污又黄的网站 | 国产精品久久精品三级 | 久久午夜无码鲁丝片 | 99久久婷婷国产综合精品青草免费 | 日本乱偷人妻中文字幕 | 一本一道久久综合久久 | 少妇久久久久久人妻无码 | 在线а√天堂中文官网 | 日韩欧美群交p片內射中文 | 男女性色大片免费网站 | 成人试看120秒体验区 | 色综合天天综合狠狠爱 | 欧美激情一区二区三区成人 | 国产亚洲人成在线播放 | 999久久久国产精品消防器材 | 欧美人与牲动交xxxx | 中文字幕无码av波多野吉衣 | 色欲久久久天天天综合网精品 | 99久久久无码国产精品免费 | 色偷偷人人澡人人爽人人模 | 99久久99久久免费精品蜜桃 | 欧美 日韩 人妻 高清 中文 | 国产成人综合美国十次 | 在教室伦流澡到高潮hnp视频 | 亚洲精品国产第一综合99久久 | 亚洲成av人综合在线观看 | 婷婷五月综合缴情在线视频 | 国产av一区二区三区最新精品 | 日本免费一区二区三区最新 | 色 综合 欧美 亚洲 国产 | 人人妻人人澡人人爽人人精品浪潮 | 免费观看激色视频网站 | 亚洲欧洲日本综合aⅴ在线 | 亚洲日韩精品欧美一区二区 | 在线欧美精品一区二区三区 | 国产极品视觉盛宴 | 少妇人妻偷人精品无码视频 | 蜜桃av抽搐高潮一区二区 | 国产成人精品三级麻豆 | 男人扒开女人内裤强吻桶进去 | 国产在线精品一区二区三区直播 | 久久亚洲a片com人成 | 欧美猛少妇色xxxxx | 亚洲综合精品香蕉久久网 | 久久天天躁夜夜躁狠狠 | 无码人妻久久一区二区三区不卡 | 国产精品第一国产精品 | 欧美大屁股xxxxhd黑色 | 老司机亚洲精品影院无码 | 无码乱肉视频免费大全合集 | 久久亚洲a片com人成 | 亚洲区小说区激情区图片区 | 永久免费精品精品永久-夜色 | 伊人色综合久久天天小片 | 在线播放免费人成毛片乱码 | 巨爆乳无码视频在线观看 | 亚洲中文字幕乱码av波多ji | 成人无码精品一区二区三区 | 亚洲中文字幕无码中文字在线 | 亚洲精品一区二区三区在线 | 欧美 亚洲 国产 另类 | 国产精品久久久久9999小说 | 爆乳一区二区三区无码 | 国产精品香蕉在线观看 | 亚洲乱码国产乱码精品精 | 天堂久久天堂av色综合 | 67194成是人免费无码 | 亚洲一区二区三区在线观看网站 | 色综合视频一区二区三区 | 成人欧美一区二区三区黑人 | 亚洲成在人网站无码天堂 | 久久精品国产99精品亚洲 | 亚洲欧洲日本无在线码 | 国产在热线精品视频 | 秋霞特色aa大片 | 999久久久国产精品消防器材 | 国产综合在线观看 | 日本丰满熟妇videos | 狂野欧美激情性xxxx | 亚洲成av人片在线观看无码不卡 | 亚洲成a人一区二区三区 | 色综合天天综合狠狠爱 | 日日摸日日碰夜夜爽av | 亚洲精品中文字幕久久久久 | 久久99热只有频精品8 | 国产真实乱对白精彩久久 | а天堂中文在线官网 | 亚洲日韩一区二区 | 日韩精品a片一区二区三区妖精 | 日韩人妻无码一区二区三区久久99 | 任你躁国产自任一区二区三区 | 福利一区二区三区视频在线观看 | 午夜福利电影 | 亚洲啪av永久无码精品放毛片 | 窝窝午夜理论片影院 | 欧美人与物videos另类 | 精品久久综合1区2区3区激情 | 亚洲熟女一区二区三区 | 中文字幕无码av激情不卡 | 亚洲精品午夜无码电影网 | 日韩精品无码免费一区二区三区 | 亚洲中文字幕无码中字 | 少妇人妻偷人精品无码视频 | 欧美黑人巨大xxxxx | 青青青手机频在线观看 | 国产美女极度色诱视频www | 国产熟妇高潮叫床视频播放 | 日日摸夜夜摸狠狠摸婷婷 | 国语精品一区二区三区 | 亚洲精品午夜国产va久久成人 | 日日橹狠狠爱欧美视频 | 亚洲欧美中文字幕5发布 | 大肉大捧一进一出视频出来呀 | 综合网日日天干夜夜久久 | 在线观看国产午夜福利片 | 欧美性猛交xxxx富婆 | 午夜时刻免费入口 | 国产亚洲精品久久久ai换 | 亚洲区欧美区综合区自拍区 | 国产精品-区区久久久狼 | 少妇无码av无码专区在线观看 | 国产真实乱对白精彩久久 | 欧美35页视频在线观看 | 国产午夜亚洲精品不卡下载 | 亚洲精品www久久久 | 国产两女互慰高潮视频在线观看 | 国产在线精品一区二区三区直播 | 午夜丰满少妇性开放视频 | 伊人久久大香线蕉av一区二区 | 中文字幕无码日韩欧毛 | 精品少妇爆乳无码av无码专区 | 亚洲男女内射在线播放 | 国产尤物精品视频 | 捆绑白丝粉色jk震动捧喷白浆 | 全黄性性激高免费视频 | 西西人体www44rt大胆高清 | 国产成人无码av片在线观看不卡 | 亚洲国产成人a精品不卡在线 | 国产色视频一区二区三区 | 色综合久久久无码中文字幕 | 亚洲国产精品久久人人爱 | 亚洲爆乳大丰满无码专区 | 水蜜桃av无码 | 岛国片人妻三上悠亚 | 精品久久久久久亚洲精品 | 18黄暴禁片在线观看 | 宝宝好涨水快流出来免费视频 | 欧美阿v高清资源不卡在线播放 | 国产在线无码精品电影网 | 日本精品高清一区二区 | 东京热一精品无码av | 东京一本一道一二三区 | 成人亚洲精品久久久久 | 成人三级无码视频在线观看 | 日本精品久久久久中文字幕 | 免费无码肉片在线观看 | 1000部夫妻午夜免费 | 国产网红无码精品视频 | 国产亚洲精品久久久久久国模美 | 国语精品一区二区三区 | 久久国产精品精品国产色婷婷 | 内射白嫩少妇超碰 | 日日碰狠狠丁香久燥 | 亚洲一区二区三区四区 | 久久 国产 尿 小便 嘘嘘 | 伊在人天堂亚洲香蕉精品区 | 精品乱子伦一区二区三区 | 麻豆md0077饥渴少妇 | 一区二区三区高清视频一 | 久久久久久国产精品无码下载 | 无码免费一区二区三区 | 日韩无码专区 | 欧美三级不卡在线观看 | 国产真实乱对白精彩久久 | 无码福利日韩神码福利片 | 久久久久av无码免费网 | 波多野结衣乳巨码无在线观看 | 欧洲vodafone精品性 | 波多野结衣av一区二区全免费观看 | 亚洲色大成网站www国产 | 亚洲成色www久久网站 | 日本一卡2卡3卡四卡精品网站 | av无码不卡在线观看免费 | 亚洲成av人片在线观看无码不卡 | 亚洲经典千人经典日产 | 国产乱码精品一品二品 | 国产又爽又猛又粗的视频a片 | 秋霞成人午夜鲁丝一区二区三区 | 欧美 亚洲 国产 另类 | 国产午夜福利100集发布 | 久久精品99久久香蕉国产色戒 | 久久精品国产一区二区三区肥胖 | 亚洲区小说区激情区图片区 | 亚洲日韩av片在线观看 | 日日噜噜噜噜夜夜爽亚洲精品 | 国产精品嫩草久久久久 | 国产精品香蕉在线观看 | 兔费看少妇性l交大片免费 | 国产精品人人妻人人爽 | 国产成人无码a区在线观看视频app | 51国偷自产一区二区三区 | 啦啦啦www在线观看免费视频 | 亚洲精品久久久久avwww潮水 | 久久亚洲精品成人无码 | 国产亚洲精品精品国产亚洲综合 | 中文精品久久久久人妻不卡 | 无遮挡啪啪摇乳动态图 | 精品夜夜澡人妻无码av蜜桃 | 亚洲伊人久久精品影院 | 免费无码午夜福利片69 | 天堂亚洲2017在线观看 | 少妇人妻av毛片在线看 |