久久福利_99r_国产日韩在线视频_直接看av的网站_中文欧美日韩_久久一

您的位置:首頁(yè)技術(shù)文章
文章詳情頁(yè)

SpringBoot + WebSocket 實(shí)現(xiàn)答題對(duì)戰(zhàn)匹配機(jī)制案例詳解

瀏覽:4日期:2023-03-08 16:14:22
概要設(shè)計(jì)

類似競(jìng)技問(wèn)答游戲:用戶隨機(jī)匹配一名對(duì)手,雙方同時(shí)開(kāi)始答題,直到雙方都完成答題,對(duì)局結(jié)束?;镜倪壿嬀褪沁@樣,如果有其他需求,可以在其基礎(chǔ)上進(jìn)行擴(kuò)展

明確了這一點(diǎn),下面介紹開(kāi)發(fā)思路。為每個(gè)用戶擬定四種在線狀態(tài),分別是:待匹配、匹配中、游戲中、游戲結(jié)束。下面是流程圖,用戶的流程是被規(guī)則約束的,狀態(tài)也隨流程而變化

SpringBoot + WebSocket 實(shí)現(xiàn)答題對(duì)戰(zhàn)匹配機(jī)制案例詳解

對(duì)流程再補(bǔ)充如下:

用戶進(jìn)入匹配大廳(具體效果如何由客戶端體現(xiàn)),將用戶的狀態(tài)設(shè)置為待匹配 用戶開(kāi)始匹配,將用戶的狀態(tài)設(shè)置為匹配中,系統(tǒng)搜索其他同樣處于匹配中的用戶,在這個(gè)過(guò)程中,用戶可以取消匹配,返回匹配大廳,此時(shí)用戶狀態(tài)重新設(shè)置為待匹配。匹配成功,保存匹配信息,將用戶狀態(tài)設(shè)置為游戲中 根據(jù)已保存的匹配信息,用戶可以獲得對(duì)手的信息。答題是時(shí),每次用戶分?jǐn)?shù)更新,也會(huì)向?qū)κ滞扑透潞蟮姆謹(jǐn)?shù) 用戶完成答題,則等待對(duì)手也完成答題。雙方都完成答題,用戶狀態(tài)設(shè)置為游戲結(jié)束,展示對(duì)局結(jié)果詳細(xì)設(shè)計(jì)

針對(duì)概要設(shè)計(jì)提出的思路,我們需要思考以下幾個(gè)問(wèn)題:

如何保持客戶端與服務(wù)器的連接? 如何設(shè)計(jì)客戶端與服務(wù)端的消息交互? 如何保存以及改變用戶狀態(tài)? 如何匹配用戶?

下面我們一個(gè)一個(gè)來(lái)解決

1. 如何保持用戶與服務(wù)器的連接?

以往我們使用 Http 請(qǐng)求服務(wù)器,并獲取響應(yīng)信息。然而 Http 有個(gè)缺陷,就是通信只能由客戶端發(fā)起,無(wú)法做到服務(wù)端主動(dòng)向客戶端推送信息。根據(jù)概要設(shè)計(jì)我們知道,服務(wù)端需要向客戶端推送對(duì)手的實(shí)時(shí)分?jǐn)?shù),因此這里不適合使用 Http,而選擇了 WebSocket。WebSocket 最大的特點(diǎn)就是服務(wù)端可以主動(dòng)向客戶端推送信息,客戶端也可以主動(dòng)向服務(wù)端發(fā)送信息,是真正的雙向平等對(duì)話

有關(guān) SpringBoot 集成 WebSocket 可參考這篇博客:https://www.jb51.net/article/208279.htm

2. 如何設(shè)計(jì)客戶端與服務(wù)端的消息交互?

按照匹配機(jī)制要求,把消息劃分為 ADD_USER(用戶加入)、MATCH_USER(匹配對(duì)手)、CANCEL_MATCH(取消匹配)、PLAY_GAME(游戲開(kāi)始)、GAME_OVER(游戲結(jié)束)

public enum MessageTypeEnum { /** * 用戶加入 */ ADD_USER, /** * 匹配對(duì)手 */ MATCH_USER, /** * 取消匹配 */ CANCEL_MATCH, /** * 游戲開(kāi)始 */ PLAY_GAME, /** * 游戲結(jié)束 */ GAME_OVER,}

使用 WebSocket 客戶端可以向服務(wù)端發(fā)送消息,服務(wù)端也能向客戶端發(fā)送消息。把消息按照需求劃分成不同的類型,客戶端發(fā)送某一類型的消息,服務(wù)端接收后判斷,并按照類型分別處理,最后返回向客戶端推送處理結(jié)果。區(qū)別客戶端 WebSocket 連接的是從客戶端傳來(lái)的 userId,用 HashMap 保存

@Component@Slf4j@ServerEndpoint(value = '/game/match/{userId}')public class ChatWebsocket { private Session session; private String userId; static QuestionSev questionSev; static MatchCacheUtil matchCacheUtil; static Lock lock = new ReentrantLock(); static Condition matchCond = lock.newCondition(); @Autowired public void setMatchCacheUtil(MatchCacheUtil matchCacheUtil) {ChatWebsocket.matchCacheUtil = matchCacheUtil; } @Autowired public void setQuestionSev(QuestionSev questionSev) {ChatWebsocket.questionSev = questionSev; } @OnOpen public void onOpen(@PathParam('userId') String userId, Session session) {log.info('ChatWebsocket open 有新連接加入 userId: {}', userId);this.userId = userId;this.session = session;matchCacheUtil.addClient(userId, this);log.info('ChatWebsocket open 連接建立完成 userId: {}', userId); } @OnError public void onError(Session session, Throwable error) {log.error('ChatWebsocket onError 發(fā)生了錯(cuò)誤 userId: {}, errorMessage: {}', userId, error.getMessage());matchCacheUtil.removeClinet(userId);matchCacheUtil.removeUserOnlineStatus(userId);matchCacheUtil.removeUserFromRoom(userId);matchCacheUtil.removeUserMatchInfo(userId);log.info('ChatWebsocket onError 連接斷開(kāi)完成 userId: {}', userId); } @OnClose public void onClose() {log.info('ChatWebsocket onClose 連接斷開(kāi) userId: {}', userId);matchCacheUtil.removeClinet(userId);matchCacheUtil.removeUserOnlineStatus(userId);matchCacheUtil.removeUserFromRoom(userId);matchCacheUtil.removeUserMatchInfo(userId);log.info('ChatWebsocket onClose 連接斷開(kāi)完成 userId: {}', userId); } @OnMessage public void onMessage(String message, Session session) {log.info('ChatWebsocket onMessage userId: {}, 來(lái)自客戶端的消息 message: {}', userId, message);JSONObject jsonObject = JSON.parseObject(message);MessageTypeEnum type = jsonObject.getObject('type', MessageTypeEnum.class);log.info('ChatWebsocket onMessage userId: {}, 來(lái)自客戶端的消息類型 type: {}', userId, type);if (type == MessageTypeEnum.ADD_USER) { addUser(jsonObject);} else if (type == MessageTypeEnum.MATCH_USER) { matchUser(jsonObject);} else if (type == MessageTypeEnum.CANCEL_MATCH) { cancelMatch(jsonObject);} else if (type == MessageTypeEnum.PLAY_GAME) { toPlay(jsonObject);} else if (type == MessageTypeEnum.GAME_OVER) { gameover(jsonObject);} else { throw new GameServerException(GameServerError.WEBSOCKET_ADD_USER_FAILED);}log.info('ChatWebsocket onMessage userId: {} 消息接收結(jié)束', userId); } /** * 群發(fā)消息 */ private void sendMessageAll(MessageReply<?> messageReply) {log.info('ChatWebsocket sendMessageAll 消息群發(fā)開(kāi)始 userId: {}, messageReply: {}', userId, JSON.toJSONString(messageReply));Set<String> receivers = messageReply.getChatMessage().getReceivers();for (String receiver : receivers) { ChatWebsocket client = matchCacheUtil.getClient(receiver); client.session.getAsyncRemote().sendText(JSON.toJSONString(messageReply));}log.info('ChatWebsocket sendMessageAll 消息群發(fā)結(jié)束 userId: {}', userId); } // 出于減少篇幅的目的,業(yè)務(wù)處理方法暫不貼出...}

3. 如何保存以及改變用戶狀態(tài)?

創(chuàng)建一個(gè)枚舉類,定義用戶的狀態(tài)

/** * 用戶狀態(tài) * @author yeeq */public enum StatusEnum { /** * 待匹配 */ IDLE, /** * 匹配中 */ IN_MATCH, /** * 游戲中 */ IN_GAME, /** * 游戲結(jié)束 */ GAME_OVER, ; public static StatusEnum getStatusEnum(String status) {switch (status) { case 'IDLE':return IDLE; case 'IN_MATCH':return IN_MATCH; case 'IN_GAME':return IN_GAME; case 'GAME_OVER':return GAME_OVER; default:throw new GameServerException(GameServerError.MESSAGE_TYPE_ERROR);} } public String getValue() {return this.name(); }}

選擇 Redis 保存用戶狀態(tài),還是創(chuàng)建一個(gè)枚舉類,Redis 中存儲(chǔ)數(shù)據(jù)都有唯一的 Key 做標(biāo)識(shí),因此在這里定義 Redis 中的 Key,分別介紹如下:

USER_STATUS:存儲(chǔ)用戶狀態(tài)的 Key,存儲(chǔ)類型是 Map<String, String>,其中用戶 userId 為 key,用戶在線狀態(tài) 為 value USER_MATCH_INFO:當(dāng)用戶處于游戲中時(shí),我們需要記錄用戶的信息,比如分?jǐn)?shù)等。這些信息不需要記錄到數(shù)據(jù)庫(kù),而且隨時(shí)會(huì)更新,放入緩存方便獲取 ROOM:可以理解為匹配的兩名用戶創(chuàng)建一個(gè)房間,具體實(shí)現(xiàn)是以鍵值對(duì)方式存儲(chǔ),比如用戶 A 和用戶 B 匹配,用戶 A 的 userId 是 A,用戶 B 的 userId 是 B,則在 Redis 中記錄為 {A -- B},{B -- A}

public enum EnumRedisKey { /** * userOnline 在線狀態(tài) */ USER_STATUS, /** * userOnline 對(duì)局信息 */ USER_IN_PLAY, /** * userOnline 匹配信息 */ USER_MATCH_INFO, /** * 房間 */ ROOM; public String getKey() {return this.name(); }}

創(chuàng)建一個(gè)工具類,用于操作 Redis 中的數(shù)據(jù)。

@Componentpublic class MatchCacheUtil { /** * 用戶 userId 為 key,ChatWebsocket 為 value */ private static final Map<String, ChatWebsocket> CLIENTS = new HashMap<>(); /** * key 是標(biāo)識(shí)存儲(chǔ)用戶在線狀態(tài)的 EnumRedisKey,value 為 map 類型,其中用戶 userId 為 key,用戶在線狀態(tài) 為 value */ @Resource private RedisTemplate<String, Map<String, String>> redisTemplate; /** * 添加客戶端 */ public void addClient(String userId, ChatWebsocket websocket) {CLIENTS.put(userId, websocket); } /** * 移除客戶端 */ public void removeClinet(String userId) {CLIENTS.remove(userId); } /** * 獲取客戶端 */ public ChatWebsocket getClient(String userId) {return CLIENTS.get(userId); } /** * 移除用戶在線狀態(tài) */ public void removeUserOnlineStatus(String userId) {redisTemplate.opsForHash().delete(EnumRedisKey.USER_STATUS.getKey(), userId); } /** * 獲取用戶在線狀態(tài) */ public StatusEnum getUserOnlineStatus(String userId) {Object status = redisTemplate.opsForHash().get(EnumRedisKey.USER_STATUS.getKey(), userId);if (status == null) { return null;}return StatusEnum.getStatusEnum(status.toString()); } /** * 設(shè)置用戶為 IDLE 狀態(tài) */ public void setUserIDLE(String userId) {removeUserOnlineStatus(userId);redisTemplate.opsForHash().put(EnumRedisKey.USER_STATUS.getKey(), userId, StatusEnum.IDLE.getValue()); } /** * 設(shè)置用戶為 IN_MATCH 狀態(tài) */ public void setUserInMatch(String userId) {removeUserOnlineStatus(userId);redisTemplate.opsForHash().put(EnumRedisKey.USER_STATUS.getKey(), userId, StatusEnum.IN_MATCH.getValue()); } /** * 隨機(jī)獲取處于匹配狀態(tài)的用戶(除了指定用戶外) */ public String getUserInMatchRandom(String userId) {Optional<Map.Entry<Object, Object>> any = redisTemplate.opsForHash().entries(EnumRedisKey.USER_STATUS.getKey()).entrySet().stream().filter(entry -> entry.getValue().equals(StatusEnum.IN_MATCH.getValue()) && !entry.getKey().equals(userId)).findAny();return any.map(entry -> entry.getKey().toString()).orElse(null); } /** * 設(shè)置用戶為 IN_GAME 狀態(tài) */ public void setUserInGame(String userId) {removeUserOnlineStatus(userId);redisTemplate.opsForHash().put(EnumRedisKey.USER_STATUS.getKey(), userId, StatusEnum.IN_GAME.getValue()); } /** * 設(shè)置處于游戲中的用戶在同一房間 */ public void setUserInRoom(String userId1, String userId2) {redisTemplate.opsForHash().put(EnumRedisKey.ROOM.getKey(), userId1, userId2);redisTemplate.opsForHash().put(EnumRedisKey.ROOM.getKey(), userId2, userId1); } /** * 從房間中移除用戶 */ public void removeUserFromRoom(String userId) {redisTemplate.opsForHash().delete(EnumRedisKey.ROOM.getKey(), userId); } /** * 從房間中獲取用戶 */ public String getUserFromRoom(String userId) {return redisTemplate.opsForHash().get(EnumRedisKey.ROOM.getKey(), userId).toString(); } /** * 設(shè)置處于游戲中的用戶的對(duì)戰(zhàn)信息 */ public void setUserMatchInfo(String userId, String userMatchInfo) {redisTemplate.opsForHash().put(EnumRedisKey.USER_MATCH_INFO.getKey(), userId, userMatchInfo); } /** * 移除處于游戲中的用戶的對(duì)戰(zhàn)信息 */ public void removeUserMatchInfo(String userId) {redisTemplate.opsForHash().delete(EnumRedisKey.USER_MATCH_INFO.getKey(), userId); } /** * 設(shè)置處于游戲中的用戶的對(duì)戰(zhàn)信息 */ public String getUserMatchInfo(String userId) {return redisTemplate.opsForHash().get(EnumRedisKey.USER_MATCH_INFO.getKey(), userId).toString(); } /** * 設(shè)置用戶為游戲結(jié)束狀態(tài) */ public synchronized void setUserGameover(String userId) {removeUserOnlineStatus(userId);redisTemplate.opsForHash().put(EnumRedisKey.USER_STATUS.getKey(), userId, StatusEnum.GAME_OVER.getValue()); }}

4. 如何匹配用戶?

匹配用戶的思路之前已經(jīng)提到過(guò),為了不阻塞客戶端與服務(wù)端的 WebSocket 連接,創(chuàng)建一個(gè)線程專門用來(lái)匹配用戶,如果匹配成功就向客戶端推送消息

用戶匹配對(duì)手時(shí)遵循這么一個(gè)原則:用戶 A 找到用戶 B,由用戶 A 負(fù)責(zé)一切工作,既由用戶 A 完成創(chuàng)建匹配數(shù)據(jù)并保存到緩存的全部操作。值得注意的一點(diǎn)是,在匹配時(shí)要注意保證狀態(tài)的變化:

當(dāng)前用戶在匹配對(duì)手的同時(shí),被其他用戶匹配,那么當(dāng)前用戶應(yīng)當(dāng)停止匹配操作 當(dāng)前用戶匹配到對(duì)手,但對(duì)手被其他用戶匹配了,那么當(dāng)前用戶應(yīng)該重新尋找新的對(duì)手

用戶匹配對(duì)手的過(guò)程應(yīng)該保證原子性,使用 Java 鎖來(lái)保證

/** * 用戶隨機(jī)匹配對(duì)手 */@SneakyThrowsprivate void matchUser(JSONObject jsonObject) { log.info('ChatWebsocket matchUser 用戶隨機(jī)匹配對(duì)手開(kāi)始 message: {}, userId: {}', jsonObject.toJSONString(), userId); MessageReply<GameMatchInfo> messageReply = new MessageReply<>(); ChatMessage<GameMatchInfo> result = new ChatMessage<>(); result.setSender(userId); result.setType(MessageTypeEnum.MATCH_USER); lock.lock(); try {// 設(shè)置用戶狀態(tài)為匹配中matchCacheUtil.setUserInMatch(userId);matchCond.signal(); } finally {lock.unlock(); } // 創(chuàng)建一個(gè)異步線程任務(wù),負(fù)責(zé)匹配其他同樣處于匹配狀態(tài)的其他用戶 Thread matchThread = new Thread(() -> {boolean flag = true;String receiver = null;while (flag) { // 獲取除自己以外的其他待匹配用戶 lock.lock(); try {// 當(dāng)前用戶不處于待匹配狀態(tài)if (matchCacheUtil.getUserOnlineStatus(userId).compareTo(StatusEnum.IN_GAME) == 0 || matchCacheUtil.getUserOnlineStatus(userId).compareTo(StatusEnum.GAME_OVER) == 0) { log.info('ChatWebsocket matchUser 當(dāng)前用戶 {} 已退出匹配', userId); return;}// 當(dāng)前用戶取消匹配狀態(tài)if (matchCacheUtil.getUserOnlineStatus(userId).compareTo(StatusEnum.IDLE) == 0) { // 當(dāng)前用戶取消匹配 messageReply.setCode(MessageCode.CANCEL_MATCH_ERROR.getCode()); messageReply.setDesc(MessageCode.CANCEL_MATCH_ERROR.getDesc()); Set<String> set = new HashSet<>(); set.add(userId); result.setReceivers(set); result.setType(MessageTypeEnum.CANCEL_MATCH); messageReply.setChatMessage(result); log.info('ChatWebsocket matchUser 當(dāng)前用戶 {} 已退出匹配', userId); sendMessageAll(messageReply); return;}receiver = matchCacheUtil.getUserInMatchRandom(userId);if (receiver != null) { // 對(duì)手不處于待匹配狀態(tài) if (matchCacheUtil.getUserOnlineStatus(receiver).compareTo(StatusEnum.IN_MATCH) != 0) {log.info('ChatWebsocket matchUser 當(dāng)前用戶 {}, 匹配對(duì)手 {} 已退出匹配狀態(tài)', userId, receiver); } else {matchCacheUtil.setUserInGame(userId);matchCacheUtil.setUserInGame(receiver);matchCacheUtil.setUserInRoom(userId, receiver);flag = false; }} else { // 如果當(dāng)前沒(méi)有待匹配用戶,進(jìn)入等待隊(duì)列 try {log.info('ChatWebsocket matchUser 當(dāng)前用戶 {} 無(wú)對(duì)手可匹配', userId);matchCond.await(); } catch (InterruptedException e) {log.error('ChatWebsocket matchUser 匹配線程 {} 發(fā)生異常: {}', Thread.currentThread().getName(), e.getMessage()); }} } finally {lock.unlock(); }}UserMatchInfo senderInfo = new UserMatchInfo();UserMatchInfo receiverInfo = new UserMatchInfo();senderInfo.setUserId(userId);senderInfo.setScore(0);receiverInfo.setUserId(receiver);receiverInfo.setScore(0);matchCacheUtil.setUserMatchInfo(userId, JSON.toJSONString(senderInfo));matchCacheUtil.setUserMatchInfo(receiver, JSON.toJSONString(receiverInfo));GameMatchInfo gameMatchInfo = new GameMatchInfo();List<Question> questions = questionSev.getAllQuestion();gameMatchInfo.setQuestions(questions);gameMatchInfo.setSelfInfo(senderInfo);gameMatchInfo.setOpponentInfo(receiverInfo);messageReply.setCode(MessageCode.SUCCESS.getCode());messageReply.setDesc(MessageCode.SUCCESS.getDesc());result.setData(gameMatchInfo);Set<String> set = new HashSet<>();set.add(userId);result.setReceivers(set);result.setType(MessageTypeEnum.MATCH_USER);messageReply.setChatMessage(result);sendMessageAll(messageReply);gameMatchInfo.setSelfInfo(receiverInfo);gameMatchInfo.setOpponentInfo(senderInfo);result.setData(gameMatchInfo);set.clear();set.add(receiver);result.setReceivers(set);messageReply.setChatMessage(result);sendMessageAll(messageReply);log.info('ChatWebsocket matchUser 用戶隨機(jī)匹配對(duì)手結(jié)束 messageReply: {}', JSON.toJSONString(messageReply)); }, CommonField.MATCH_TASK_NAME_PREFIX + userId); matchThread.start();}項(xiàng)目展示

項(xiàng)目代碼如下:https://github.com/Yee-Q/match-project

跑起來(lái)后,使用 websocket-client 可以進(jìn)行測(cè)試。在瀏覽器打開(kāi),在控制臺(tái)查看消息。

在連接輸入框隨便輸入一個(gè)數(shù)字作為 userId,點(diǎn)擊連接,此時(shí)客戶端就和服務(wù)端建立 WebSocket 連接了

SpringBoot + WebSocket 實(shí)現(xiàn)答題對(duì)戰(zhàn)匹配機(jī)制案例詳解

點(diǎn)擊加入用戶按鈕,用戶“進(jìn)入匹配大廳”

SpringBoot + WebSocket 實(shí)現(xiàn)答題對(duì)戰(zhàn)匹配機(jī)制案例詳解

點(diǎn)擊隨機(jī)匹配按鈕,開(kāi)始匹配,再取消匹配

SpringBoot + WebSocket 實(shí)現(xiàn)答題對(duì)戰(zhàn)匹配機(jī)制案例詳解

按照之前的步驟再建立一個(gè)用戶連接,都點(diǎn)擊隨機(jī)匹配按鈕,匹配成功,服務(wù)端返回響應(yīng)信息

SpringBoot + WebSocket 實(shí)現(xiàn)答題對(duì)戰(zhàn)匹配機(jī)制案例詳解

用戶分?jǐn)?shù)更新時(shí),在輸入框輸入新的分?jǐn)?shù),比如 6,點(diǎn)擊實(shí)時(shí)更新按鈕,對(duì)手將受到最新的分?jǐn)?shù)消息

SpringBoot + WebSocket 實(shí)現(xiàn)答題對(duì)戰(zhàn)匹配機(jī)制案例詳解

當(dāng)雙方都點(diǎn)擊游戲結(jié)束按鈕,則游戲結(jié)束

SpringBoot + WebSocket 實(shí)現(xiàn)答題對(duì)戰(zhàn)匹配機(jī)制案例詳解

以上就是SpringBoot + WebSocket 實(shí)現(xiàn)答題對(duì)戰(zhàn)匹配機(jī)制案例詳解的詳細(xì)內(nèi)容,更多關(guān)于SpringBoot WebSocket答題對(duì)戰(zhàn)的資料請(qǐng)關(guān)注好吧啦網(wǎng)其它相關(guān)文章!

標(biāo)簽: Spring
相關(guān)文章:
主站蜘蛛池模板: 色婷婷国产精品久久包臀 | 精品久| 中文字幕在线观看精品视频 | 在线黄av | 成人高清视频在线观看 | 久久99国产一区二区三区 | 日日夜夜爽 | 久久久久久久久久久久久久久久久久久 | 国产成人午夜精品影院游乐网 | 中文字幕国产视频 | 午夜爽爽爽 | 久久人人爽人人爽 | 欧洲另类在线1 | 久久韩国 | 91精品国产综合久久久久久 | 一区二区三区四区在线播放 | 久久国产综合 | www伊人 | 国模一区二区三区 | 色婷婷综合久久久久中文一区二区 | 欧美精品综合 | 91污在线观看 | 国产精品久久久一区 | 91.com在线观看| 国产日韩欧美综合 | 狠狠干狠狠干 | 亚洲免费视频网 | 免费一级欧美在线观看视频 | 在线观看亚洲专区 | 一区二区三区视频在线播放 | 久久精品国产精品 | 日韩欧美在线一区二区 | 国产一区二区三区四区在线观看 | av片免费 | 欧美一级裸体视频 | 91精品国产乱码久久久久久久久 | 青青草久 | 亚洲一区二区三区四区 | 成人精品久久久 | 999久久久国产精品 免费视频一区 | 不卡成人| 久久久极品 | 中文字幕 在线观看 | 国产成人一区 | 一区久久| 亚洲国产精品99久久久久久久久 | 亚洲视频在线免费观看 | www.久久| 日本爽快片毛片 | 欧美日韩在线成人 | 日韩高清国产一区在线 | 久久国产亚洲精品 | 国产美女精品 | 欧美精品一区二区三区在线四季 | 日韩理论在线 | 99精品久久精品一区二区爱城 | 天天操天天舔 | 久久久久久国产精品美女 | 91亚洲国产成人久久精品网站 | 国产高清视频一区二区 | 国内成人免费视频 | 日本高清无卡码一区二区久久 | 久久久久中文字幕 | 在线亚洲精品 | 中文字幕在线视频第一页 | 精品视频一区在线观看 | 日韩欧美成人一区二区三区 | 国产精品成人一区二区三区 | 国产美女网站视频 | 国产精品毛片无码 | 国产精品夜间视频香蕉 | 99这里只有精品视频 | 精品在线播放 | 综合国产 | 成人看片在线 | 亚洲成人国产精品 | 黑人精品| 国产精品污www在线观看 | 久久国产午夜 | 欧美一区二区视频 | 黑色丝袜脚足j国产在线看68 | 国产成人av在线 | 宅男伊人 | 亚洲 中文 欧美 日韩 在线观看 | www.国产精品| 女人夜夜春高潮爽a∨片传媒 | 国产精品视频 | 久久亚洲视频 | 伊人网站| 久草日本| 国产色网站 | 成人自拍视频 | 欧美精品在线一区 | 天天拍拍天天干 | 欧美中文在线 | 国产精品美女久久久久久免费 | 国产成人免费视频网站视频社区 | 91在线精品视频 | 色一色网站 | 精品在线一区二区 | 亚洲一区二区三区四区五区中文 | 伊人网综合在线 | 国产最新网址 | 97精品久久| 色综合二区 | 日本 国产 欧美 | a欧美| 永久91嫩草亚洲精品人人 | 国产中文字幕在线观看 | 国产精品久久久久久久浪潮网站 | 国产区最新 | 亚洲国产成人精品女人 | 91精品电影| 久久精品手机视频 | 欧美日韩精品一区 | 久久精品国产精品青草 | 午夜成人在线视频 | 欧美日韩精品一区二区 | 狠狠干av| 一区二区三区四区精品 | 中文字幕 国产精品 | 久久a国产 | 国产精品美女久久久久久免费 | av在线免费观看网站 | 亚洲精品久久久一区二区三区 | 亚洲日韩欧美一区二区在线 | 精品欧美一区二区三区久久久 | 成人免费视频观看视频 | 免费国产一区二区 | 日韩一级二级三级 | 网站一区二区三区 | 国产区在线观看 | 精品亚洲国产成av人片传媒 | 一级全毛片 | 国产激情网站 | 中文字幕日韩欧美 | 亚洲免费视频一区二区 | 四虎com| 特级黄一级播放 | av大片| 久久精品视频网 | 亚洲精品视频在线播放 | 中文字幕在线第一页 | 秋霞av电影 | 久久精品国产一区二区三区不卡 | 香蕉成人啪国产精品视频综合网 | 中文字字幕在线 | 欧美成人一区二区三区片免费 | 欧美一级黄带 | 精品成人久久 | 玖玖在线免费视频 | 欧美一区二区三区在线视频观看 | 亚洲日韩欧美一区二区在线 | 欧美一区二区三区xxxx监狱 | 久色| 欧美成人综合 | 中文字幕在线第一页 | 殴美一区| 亚洲国产精品视频 | 亚洲视频中文字幕 | 日韩成人精品 | 国产乱码精品一区二区三区忘忧草 | 在线欧美亚洲 | 久久国产精品免费一区二区三区 | 国产精品久久久久久亚洲调教 | 中文字幕在线电影观看 | 国产性色 | 91久久精品国产91久久 | 夜夜视频| 国产欧美日韩综合精品一区二区 | 亚洲乱码国产乱码精品精98午夜 | 国产日韩91 | 精品视频一区在线观看 | 国产午夜精品久久久久免费视高清 | 久久久精品国产 | 欧美日韩国产一区二区 | 天堂中文字幕 | 91精品午夜| 免费99视频| 精品久久久免费视频 | 一区二区三区国产 | 国产私拍视频 | 国产精品视频入口 | 欧美第一页 | 国产精品综合视频 | 日韩中文字幕电影在线观看 | 欧美日韩视频在线播放 | 精品久久久久久久久久久久久久 | 国产精品久久久久久久久久久久 | 先锋av资源网 | 欧美中文日韩 | 色5月婷婷丁香六月 | 欧美一区不卡 | 国产精品一区二区三区在线 | 亚洲欧美中文字幕 | 成年人免费看片 | 黑人性dh | 国产精品精品 | 国产一区二区视频精品 | www.麻豆视频| 国产精品久久久久久一区二区三区 | 7777奇米影视| 欧美精品www | 日韩欧美一区二区在线观看 | 欧美一区二区大片 | 伊人网页 | 精久久久| 国产精品美女久久久久久久久久久 | 亚洲国产成人久久综合一区,久久久国产99 | 亚洲精品久久久久久久久久久久久 | 久久精品久久综合 | 久久久精品高清 | www.色综合 | 99精品欧美一区二区三区 | 在线中文字幕视频 | 国产一区二区影院 | a毛片国产| 青青草在线免费视频 | 亚洲欧美中文日韩v在线观看 | av中文字幕第一页 | 精品一区二区av | 中文字幕一区二区三区日韩精品 | xxxx网| 精品视频在线观看一区二区三区 | 精品在线视频观看 | 国产一区二区视频在线 | 久久99精品视频 | 日韩一区二 | 国产剧情一区二区 | 亚洲一级在线观看 | 91精品久久久久久久久中文字幕 | 国产成人看片 | 男女深夜网站 | 97精品国产| 日本在线播放 | 国产农村妇女精品 | 在线一区观看 | 欧美一级裸体视频 | 色吧av| 成人作爱视频 | 久久久久久久99精品免费观看 | av观看免费 | 精品99免费 | 后人极品翘臀美女在线播放 | 狠狠操狠狠摸 | 99综合| 一区二区免费在线观看 | 国产精品国产三级国产aⅴ入口 | 久久性色| 亚洲小视频 | 国产乱人伦av在线a 天天碰天天操 | 成人黄色电影小说 | 蜜桃av网址 | 中文字幕日韩一区二区不卡 | 国产精品三级久久久久久电影 | 91精品国产乱码久久久久久久久 | 国产日韩欧美一区二区在线观看 | 91欧美| 亚洲www.| 午夜www| 在线观看视频一区 | 97在线观看| 亚洲成人精品在线 | 91夜夜蜜桃臀一区二区三区 | 99精品国产一区二区 | 欧美激情欧美激情在线五月 | 色欧美日韩 | 久久久久一区二区 | 日夜夜精品视频 | 亚洲高清久久 | 国产精品久久久久久久久久久久久 | 日韩欧美国产一区二区 | 一区二区三区四区国产 | 亚洲精品一二三区 | 国产精品久久久久久久久久 | 国产美女精品视频免费观看 | 99久久精品一区二区 | 免费黄看片 | 在线看片网站 | 欧美日韩在线免费观看 | 日韩精品在线一区 | 日韩一区在线视频 | 国产精品久久久久久中文字 | www久| 欧美日韩精品亚洲 | 国产精品高清在线观看 | 亚洲国产精久久久久久久 | 国产视频综合 | 天堂精品一区二区三区 | 99国产精品视频免费观看一公开 | 亚洲aaa在线观看 | 精品久久影院 | 一级一级国产片 | 欧美一区2区三区4区公司二百 | 欧美日韩精 | 超级碰在线视频 | 精品一区二区久久久久久久网站 | 精品国产一区二区三区在线观看 | 免费黄色看片 | 在线国产一区二区 | 亚洲91精品 | a级黄色毛片免费观看 | 杨门女将寡妇一级裸片看 | 久久九 | 久久久亚洲精品中文字幕 | 一区二区精品视频在线观看 | 亚洲aaaaaa特级 | 中文字幕在线视频网站 | 国产精品久久久久久亚洲调教 | 久久成人免费观看 | 日本不卡一区二区三区在线观看 | 国产成人影视 | 久草免费在线视频 | 免费观看一区二区三区 | 国产精品1区2区3区 中文字幕一区二区三区四区 | 午夜国产一级 | 福利电影在线 | 中国一级毛片 | 亚洲精品中文字幕在线观看 | 国产乡下妇女做爰视频 | 精品视频在线观看 | 日韩精品免费在线视频 | 一二三区字幕免费观看av | 成人1区2区 | 不卡一区二区三区视频 | 国产精品一区二区在线 | 狠狠久久综合 | 欧美亚洲一区 | 夜夜精品视频 | 亚洲国产精品一区二区第一页 | 精品久久一二三区 | 日本精品一区二区三区在线观看视频 | 一区二区在线看 | 日本亚洲精品一区二区三区 | 免费毛片网站 | 伊人久久国产 | 国产精品久久久久久久久久久久 | 天天澡天天狠天天天做 | 五月婷婷导航 | 成人在线观 | 国产精品久久久久久一区二区三区 | 欧美精品一区二区三区四区 | 91在线观看网站 | 青青草精品 | 欧美福利电影在线观看 | av黄色在线播放 | 日本久久久久久久久 | 成av在线| 激情欧美一区二区三区中文字幕 | 国产片久久| 亚洲网站免费看 | 国产精品久久在线观看 | 狠狠操夜夜操 | 欧美日一区 | 色啪网站 | 一区二区三区国产视频 | 最新高清无码专区 | 欧美日本亚洲 | 激情六月婷 | 国产在视频一区二区三区吞精 | 中文字幕国产视频 | 综合久久99 | 中文字幕视频在线免费观看 | 一本一道久久a久久精品综合蜜臀 | 在线视频亚洲 | 精品成人av| 亚洲日韩欧美一区二区在线 | 黑人巨大精品欧美黑白配亚洲 | 亚洲精品免费看 | 成人在线视频免费观看 | 成人午夜性a一级毛片免费看 | 国产青青草 | 国产一级黄片毛片 | 99这里只有精品视频 | 蜜桃视频网站在线观看 | 欧美色视频在线观看 | 国产亚洲一区二区三区在线观看 | 国产情侣自拍啪啪 | 久久精品在线视频 | 天堂中文资源在线 | 日韩视频在线观看 | 久久久精品一区 | 亚洲欧美日韩一区二区 | 亚洲一区欧美一区 | 国产一区在线观看视频 | 欧美成人精品 | 国产美女高潮一区二区三区 | 日韩a | 欧美精品一区在线观看 | 国产成人精品一区二区三区视频 | 大胆裸体gogo毛片免费看 | 羞羞视频在线观看免费 | 日韩国产二区 | 精品国产一区二区在线 | 国产精品久久久久久久久 | 福利久久久 | 精品国产区 | 一二区精品 | 亚洲a网 | 国产精品毛片一区二区在线看 | 国内精品久久久久 | 精品综合| 一本一道久久a久久精品综合蜜臀 | 欧美成人高清视频 | 密色视频| 亚洲国产精品成人 | 色官网 | 91婷婷射| 在线免费观看日韩视频 | 一区二区三区视频免费在线观看 | 黄a在线看 | 精品久久久中文字幕 | 成年人在线视频播放 | 精品中出 | 国产精品视频一区二区三区 | 久久国产精品99久久久久久老狼 | 日本欧美大片 | 999精品网 | 奇米av | 一区二区三区免费网站 | 久久久天天 | 老司机福利在线观看 | 成人精品视频在线 | 国产精品久久久久久久久久久久久 | 999在线视频免费观看 | 国产伦精品一区二区三区高清 | 国内精品久久久久久影视8 91一区二区在线观看 | 日韩一区二区视频 | 日韩国产一区二区三区 | 亚洲精品免费在线观看 | 精品亚洲一区二区三区 | 免费国产一区二区 | 中文字幕日韩欧美 | 久久久久中文 | 国产精品成人在线观看 | 91视频免费看| 久热伊人 | 在线观看免费的av | 欧美日韩在线看 | 欧美成人黄色 | 欧美色综合天天久久综合精品 | 日韩极品视频 | 毛片网站在线 | 玖玖操| 狠狠干干 | 亚洲国产精品一区 | 日日日操 | 国产午夜精品一区二区三区免费 | 成人资源在线观看 | 播放一级毛片 | 国产黄色一级大片 | 国产一级大片 | 中文字幕三区 | 日韩国产欧美亚洲 | 日韩一级视频 | 亚色在线| 精品国产一区二区在线 | 国产二区视频 | 91在线综合 | 亚洲视频一区二区三区四区 | 欧美激情在线狂野欧美精品 | 一级在线看| 91精品国产综合久久福利 | 在线看91| 91在线视频免费播放 | 国产精品1区2区 | 日韩av免费| 免费毛片网 | 成人免费一区二区三区视频网站 | 波多野结衣一二三区 | 欧美成人高清视频 | 依人成人综合网 | 亚洲精选久久 | 日韩高清中文字幕 | 999久久久国产精品 免费视频一区 | 在线观看亚洲精品视频 | 91亚洲国产精品 | 久久久www | 精品久| 欧美激情免费 | 成人精品在线 | 欧美午夜精品久久久久免费视 | 天天操天天插天天干 | 人人草人人 | 91天堂| 日本高清精品 | 国产综合精品 | 亚洲aⅴ天堂av在线电影软件 | xnxx 美女19| www久久精品| 成人一区二区av | 国产精品一区二区三区在线播放 | 不卡黄色 | 精品国产一区二区三区免费 | 久久久国产视频 | av天天操 | 成人性大片免费观看网站 | 精品久久久久久国产 | 波多野吉衣网站 | jizz18毛片| 羞羞视频免费看 | 亚洲精品一区二区三区四区高清 | 国产精品一二三四区 | 天天噜天天干 | 日韩视频在线不卡 | 亚洲在线视频 | 日韩精品一二三区 | 色九九九 | 国产91成人在在线播放 | 国产精品久久久久久久久久久久久久 | 精产国产伦理一二三区 | 精品久久久久久久久久久久 | 久久精品成人 | 亚洲一区二区日韩 | 午夜激情视频在线观看 | 午夜网| 午夜影视剧场 | 黄色在线免费观看 | 亚洲性网| 欧美日韩精品区 | 成人国产精品色哟哟 | 国产美女在线观看 | 成人在线免费观看 | 黄a免费 | 一级毛片电影 | 国产一区二区自拍视频 | 91精品视频一区 | 日韩欧美在线观看一区二区三区 | 综合网视频 | а天堂中文最新一区二区三区 | av网站久久 | 欧美午夜视频 | 另类国产ts人妖高潮系列视频 | 男人的天堂在线视频 | 国产在线观看一区二区 | 瑟瑟视频在线看 | 成人1区 | 亚洲国产精品一区二区久久 | 亚洲国产精品一区二区久久,亚洲午夜 | 欧美日韩国产一区二区三区 | 91精品国产综合久久婷婷香蕉 | 亚洲欧美视频在线 | v888av成人 | 午夜tv免费观看 | 亚洲视频在线免费观看 | 久久久久久国产精品 | xvideos视频| 午夜私人视频 | 久久九| 国产精品亚洲一区二区三区在线 | 久久精品一区二区 | 不卡一二区 | 99久久久国产精品 | 久久国产成人 | 日韩午夜免费 | 成人a在线视频免费观看 | 日韩精品极品视频在线 | 成人免费在线观看视频 | 久久99久 | 欧美日韩精品一区二区 | 午夜精品久久久久99蜜 | 在线视频 中文字幕 | 亚洲成人av | 亚洲国产精品久久久久久 | 久久人人爽人人爽人人片av不 | 欧美日韩在线二区 | 特级丰满少妇一级aaaa爱毛片 | 久久久精品一区二区三区 | av下一页| 在线观看日韩精品 | 精品中文在线 | 日韩三级视频 | 日韩精品一区二区三区在线观看 | 精品国模一区二区三区欧美 | 日韩成人中文字幕 | 国产成人99 | 69免费视频 | 四虎av| 色婷网| 求av网址| 日韩精品一区二区三区在线播放 | 日韩免费av网站 | 亚洲精品日韩激情在线电影 | 国产欧美精品区一区二区三区 | 在线精品一区二区 | 国产成人精品一区二区三区视频 | 色网在线观看 | 亚洲色图88 | 在线观看免费的网站www | 男人的天堂在线视频 | 黄色一级大片网站 | 亚洲一区久久 | 欧美成人午夜视频 | 在线精品日韩 | 国产日韩在线播放 | 超碰人人在线 | 最新日韩在线观看视频 | 欧美精品久久久 | 免费v片| 中文字幕第31页 | 91精品国产乱码久久久久久久久 | 国产午夜精品一区二区三区 | av午夜电影| 伊人一二三区 | 亚洲精品在线播放 | 亚洲精品66 | av影音资源| 欧美日韩中文字幕在线 | 欧美一区二区免费 | 亚洲欧美激情在线 | 免费一区二区三区视频在线 | 午夜亚洲一区 | 国产精品二区三区在线观看 | 中文字幕亚洲欧美日韩在线不卡 | 精品视频免费观看 |