Linux C編程第十八章 高并發服務器(一)

      網友投稿 605 2025-04-02

      f高并發服務器

      一、多進程并發服務器

      1. 實現示意圖

      2. 使用多進程并發服務器時要考慮以下幾點:

      父進程最大文件描述個數(父進程中需要close關閉accept返回的新文件描述符)

      系統內創建進程個數(與內存大小相關)

      進程創建過多是否降低整體服務性能(進程調度)

      3. 使用多進程的方式, 解決服務器處理多連接的問題:

      (1)共享

      讀時共享, 寫時復制

      文件描述符

      內存映射區 -- mmap

      (2)父進程 的角色是什么?

      等待接受客戶端連接 -- accept

      有鏈接:

      創建一個子進程 fork()

      將通信的文件描述符關閉

      (3)子進程的角色是什么?

      1)通信

      使用accept返回值 - fd

      2)關掉監聽的文件描述符

      【Linux C編程】第十八章 高并發服務器(一)

      浪費資源

      (4)創建的進程的個數有限制嗎?

      受硬件限制

      文件描述符默認也是有上限的1024

      (5)子進程資源回收

      1)wait/waitpid

      2)使用信號回收

      信號捕捉

      signal

      sigaction - 推薦

      捕捉信號: SIGCHLD

      代碼實現:

      wrap.c

      1 #include 2 #include 3 #include 4 #include 5 #include 6 7 void perr_exit(const char *s) 8 { 9 perror(s); 10 exit(-1); 11 } 12 13 int Accept(int fd, struct sockaddr *sa, socklen_t *salenptr) 14 { 15 int n; 16 17 again: 18 if ((n = accept(fd, sa, salenptr)) < 0) { 19 if ((errno == ECONNABORTED) || (errno == EINTR)) 20 goto again; 21 else 22 perr_exit("accept error"); 23 } 24 return n; 25 } 26 27 int Bind(int fd, const struct sockaddr *sa, socklen_t salen) 28 { 29 int n; 30 31 if ((n = bind(fd, sa, salen)) < 0) 32 perr_exit("bind error"); 33 34 return n; 35 } 36 37 int Connect(int fd, const struct sockaddr *sa, socklen_t salen) 38 { 39 int n; 40 41 if ((n = connect(fd, sa, salen)) < 0) 42 perr_exit("connect error"); 43 44 return n; 45 } 46 47 int Listen(int fd, int backlog) 48 { 49 int n; 50 51 if ((n = listen(fd, backlog)) < 0) 52 perr_exit("listen error"); 53 54 return n; 55 } 56 57 int Socket(int family, int type, int protocol) 58 { 59 int n; 60 61 if ((n = socket(family, type, protocol)) < 0) 62 perr_exit("socket error"); 63 64 return n; 65 } 66 67 ssize_t Read(int fd, void *ptr, size_t nbytes) 68 { 69 ssize_t n; 70 71 again: 72 if ( (n = read(fd, ptr, nbytes)) == -1) { 73 if (errno == EINTR) 74 goto again; 75 else 76 return -1; 77 } 78 return n; 79 } 80 81 ssize_t Write(int fd, const void *ptr, size_t nbytes) 82 { 83 ssize_t n; 84 85 again: 86 if ( (n = write(fd, ptr, nbytes)) == -1) { 87 if (errno == EINTR) 88 goto again; 89 else 90 return -1; 91 } 92 return n; 93 } 94 95 int Close(int fd) 96 { 97 int n; 98 if ((n = close(fd)) == -1) 99 perr_exit("close error"); 100 101 return n; 102 } 103 104 /*參三: 應該讀取的字節數*/ 105 ssize_t Readn(int fd, void *vptr, size_t n) 106 { 107 size_t nleft; //usigned int 剩余未讀取的字節數 108 ssize_t nread; //int 實際讀到的字節數 109 char *ptr; 110 111 ptr = vptr; 112 nleft = n; 113 114 while (nleft > 0) { 115 if ((nread = read(fd, ptr, nleft)) < 0) { 116 if (errno == EINTR) 117 nread = 0; 118 else 119 return -1; 120 } else if (nread == 0) 121 break; 122 123 nleft -= nread; 124 ptr += nread; 125 } 126 return n - nleft; 127 } 128 129 ssize_t Writen(int fd, const void *vptr, size_t n) 130 { 131 size_t nleft; 132 ssize_t nwritten; 133 const char *ptr; 134 135 ptr = vptr; 136 nleft = n; 137 while (nleft > 0) { 138 if ( (nwritten = write(fd, ptr, nleft)) <= 0) { 139 if (nwritten < 0 && errno == EINTR) 140 nwritten = 0; 141 else 142 return -1; 143 } 144 145 nleft -= nwritten; 146 ptr += nwritten; 147 } 148 return n; 149 } 150 151 static ssize_t my_read(int fd, char *ptr) 152 { 153 static int read_cnt; 154 static char *read_ptr; 155 static char read_buf[100]; 156 157 if (read_cnt <= 0) { 158 again: 159 if ( (read_cnt = read(fd, read_buf, sizeof(read_buf))) < 0) { 160 if (errno == EINTR) 161 goto again; 162 return -1; 163 } else if (read_cnt == 0) 164 return 0; 165 read_ptr = read_buf; 166 } 167 read_cnt--; 168 *ptr = *read_ptr++; 169 170 return 1; 171 } 172 173 ssize_t Readline(int fd, void *vptr, size_t maxlen) 174 { 175 ssize_t n, rc; 176 char c, *ptr; 177 178 ptr = vptr; 179 for (n = 1; n < maxlen; n++) { 180 if ( (rc = my_read(fd, &c)) == 1) { 181 *ptr++ = c; 182 if (c == '\n') 183 break; 184 } else if (rc == 0) { 185 *ptr = 0; 186 return n - 1; 187 } else 188 return -1; 189 } 190 *ptr = 0; 191 192 return n; 193 }

      wrap.h

      1 #ifndef __WRAP_H_ 2 #define __WRAP_H_ 3 4 void perr_exit(const char *s); 5 int Accept(int fd, struct sockaddr *sa, socklen_t *salenptr); 6 int Bind(int fd, const struct sockaddr *sa, socklen_t salen); 7 int Connect(int fd, const struct sockaddr *sa, socklen_t salen); 8 int Listen(int fd, int backlog); 9 int Socket(int family, int type, int protocol); 10 ssize_t Read(int fd, void *ptr, size_t nbytes); 11 ssize_t Write(int fd, const void *ptr, size_t nbytes); 12 int Close(int fd); 13 ssize_t Readn(int fd, void *vptr, size_t n); 14 ssize_t Writen(int fd, const void *vptr, size_t n); 15 ssize_t my_read(int fd, char *ptr); 16 ssize_t Readline(int fd, void *vptr, size_t maxlen); 17 18 #endif

      server.c

      1 #include 2 #include 3 #include 4 #include 5 #include 6 #include 7 #include 8 #include 9 10 #include "wrap.h" 11 12 #define MAXLINE 8192 13 #define SERV_PORT 8000 14 15 void do_sigchild(int num) 16 { 17 while (waitpid(0, NULL, WNOHANG) > 0); 18 } 19 20 int main(void) 21 { 22 struct sockaddr_in servaddr, cliaddr; 23 socklen_t cliaddr_len; 24 int listenfd, connfd; 25 char buf[MAXLINE]; 26 char str[INET_ADDRSTRLEN]; 27 int i, n; 28 pid_t pid; 29 30 //臨時屏蔽sigchld信號 31 sigset_t myset; 32 sigemptyset(&myset); 33 sigaddset(&myset, SIGCHLD); 34 // 自定義信號集 -》 內核阻塞信號集 35 sigprocmask(SIG_BLOCK, &myset, NULL); 36 37 38 listenfd = Socket(AF_INET, SOCK_STREAM, 0); 39 40 int opt = 1; 41 // 設置端口復用 42 setsockopt(listenfd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt)); 43 44 bzero(&servaddr, sizeof(servaddr)); 45 servaddr.sin_family = AF_INET; 46 servaddr.sin_addr.s_addr = htonl(INADDR_ANY); 47 servaddr.sin_port = htons(SERV_PORT); 48 49 Bind(listenfd, (struct sockaddr *)&servaddr, sizeof(servaddr)); 50 51 Listen(listenfd, 20); 52 53 printf("Accepting connections ...\n"); 54 while (1) 55 { 56 cliaddr_len = sizeof(cliaddr); 57 connfd = Accept(listenfd, (struct sockaddr *)&cliaddr, &cliaddr_len); 58 59 // 有新的連接則創建一個進程 60 pid = fork(); 61 if (pid == 0) 62 { 63 Close(listenfd); 64 while (1) 65 { 66 n = Read(connfd, buf, MAXLINE); 67 if (n == 0) 68 { 69 printf("the other side has been closed.\n"); 70 break; 71 } 72 printf("received from %s at PORT %d\n", 73 inet_ntop(AF_INET, &cliaddr.sin_addr, str, sizeof(str)), 74 ntohs(cliaddr.sin_port)); 75 76 for (i = 0; i < n; i++) 77 buf[i] = toupper(buf[i]); 78 79 Write(STDOUT_FILENO, buf, n); 80 Write(connfd, buf, n); 81 } 82 Close(connfd); 83 return 0; 84 } 85 else if (pid > 0) 86 { 87 struct sigaction act; 88 act.sa_flags = 0; 89 act.sa_handler = do_sigchild; 90 sigemptyset(&act.sa_mask); 91 sigaction(SIGCHLD, &act, NULL); 92 // 解除對sigchld信號的屏蔽 93 sigprocmask(SIG_UNBLOCK, &myset, NULL); 94 95 Close(connfd); 96 } 97 else 98 { 99 perr_exit("fork"); 100 } 101 } 102 return 0; 103 }

      client.c

      1 /* client.c */ 2 #include 3 #include 4 #include 5 #include 6 #include 7 8 #include "wrap.h" 9 10 #define MAXLINE 8192 11 #define SERV_PORT 8000 12 13 int main(int argc, char *argv[]) 14 { 15 struct sockaddr_in servaddr; 16 char buf[MAXLINE]; 17 int sockfd, n; 18 19 sockfd = Socket(AF_INET, SOCK_STREAM, 0); 20 21 bzero(&servaddr, sizeof(servaddr)); 22 servaddr.sin_family = AF_INET; 23 inet_pton(AF_INET, "127.0.0.1", &servaddr.sin_addr); 24 servaddr.sin_port = htons(SERV_PORT); 25 26 Connect(sockfd, (struct sockaddr *)&servaddr, sizeof(servaddr)); 27 28 while (fgets(buf, MAXLINE, stdin) != NULL) 29 { 30 Write(sockfd, buf, strlen(buf)); 31 n = Read(sockfd, buf, MAXLINE); 32 if (n == 0) 33 { 34 printf("the other side has been closed.\n"); 35 break; 36 } 37 else 38 Write(STDOUT_FILENO, buf, n); 39 } 40 41 Close(sockfd); 42 43 return 0; 44 }

      makefile

      1 src = $(wildcard *.c) 2 obj = $(patsubst %.c, %.o, $(src)) 3 4 all: server client 5 6 server: server.o wrap.o 7 gcc server.o wrap.o -o server -Wall 8 client: client.o wrap.o 9 gcc client.o wrap.o -o client -Wall 10 11 %.o:%.c 12 gcc -c $< -Wall 13 14 .PHONY: clean all 15 clean: 16 -rm -rf server client $(obj)

      二、多線程并發服務器

      1. 實現示意圖

      2. 使用線程模型開發服務器時需考慮以下問題:

      調整進程內最大文件描述符上限

      線程如有共享數據,考慮線程同步

      服務于客戶端線程退出時,退出處理。(退出值,分離態)

      系統負載,隨著鏈接客戶端增加,導致其它線程不能及時得到CPU

      3.?線程共享:

      全局數據區

      堆區

      一塊有效內存的地址

      代碼實現:

      wrap.c

      1 #include 2 #include 3 #include 4 #include 5 #include 6 7 void perr_exit(const char *s) 8 { 9 perror(s); 10 exit(-1); 11 } 12 13 int Accept(int fd, struct sockaddr *sa, socklen_t *salenptr) 14 { 15 int n; 16 17 again: 18 if ((n = accept(fd, sa, salenptr)) < 0) { 19 if ((errno == ECONNABORTED) || (errno == EINTR)) 20 goto again; 21 else 22 perr_exit("accept error"); 23 } 24 return n; 25 } 26 27 int Bind(int fd, const struct sockaddr *sa, socklen_t salen) 28 { 29 int n; 30 31 if ((n = bind(fd, sa, salen)) < 0) 32 perr_exit("bind error"); 33 34 return n; 35 } 36 37 int Connect(int fd, const struct sockaddr *sa, socklen_t salen) 38 { 39 int n; 40 41 if ((n = connect(fd, sa, salen)) < 0) 42 perr_exit("connect error"); 43 44 return n; 45 } 46 47 int Listen(int fd, int backlog) 48 { 49 int n; 50 51 if ((n = listen(fd, backlog)) < 0) 52 perr_exit("listen error"); 53 54 return n; 55 } 56 57 int Socket(int family, int type, int protocol) 58 { 59 int n; 60 61 if ((n = socket(family, type, protocol)) < 0) 62 perr_exit("socket error"); 63 64 return n; 65 } 66 67 ssize_t Read(int fd, void *ptr, size_t nbytes) 68 { 69 ssize_t n; 70 71 again: 72 if ( (n = read(fd, ptr, nbytes)) == -1) { 73 if (errno == EINTR) 74 goto again; 75 else 76 return -1; 77 } 78 return n; 79 } 80 81 ssize_t Write(int fd, const void *ptr, size_t nbytes) 82 { 83 ssize_t n; 84 85 again: 86 if ( (n = write(fd, ptr, nbytes)) == -1) { 87 if (errno == EINTR) 88 goto again; 89 else 90 return -1; 91 } 92 return n; 93 } 94 95 int Close(int fd) 96 { 97 int n; 98 if ((n = close(fd)) == -1) 99 perr_exit("close error"); 100 101 return n; 102 } 103 104 /*參三: 應該讀取的字節數*/ 105 ssize_t Readn(int fd, void *vptr, size_t n) 106 { 107 size_t nleft; //usigned int 剩余未讀取的字節數 108 ssize_t nread; //int 實際讀到的字節數 109 char *ptr; 110 111 ptr = vptr; 112 nleft = n; 113 114 while (nleft > 0) { 115 if ((nread = read(fd, ptr, nleft)) < 0) { 116 if (errno == EINTR) 117 nread = 0; 118 else 119 return -1; 120 } else if (nread == 0) 121 break; 122 123 nleft -= nread; 124 ptr += nread; 125 } 126 return n - nleft; 127 } 128 129 ssize_t Writen(int fd, const void *vptr, size_t n) 130 { 131 size_t nleft; 132 ssize_t nwritten; 133 const char *ptr; 134 135 ptr = vptr; 136 nleft = n; 137 while (nleft > 0) { 138 if ( (nwritten = write(fd, ptr, nleft)) <= 0) { 139 if (nwritten < 0 && errno == EINTR) 140 nwritten = 0; 141 else 142 return -1; 143 } 144 145 nleft -= nwritten; 146 ptr += nwritten; 147 } 148 return n; 149 } 150 151 static ssize_t my_read(int fd, char *ptr) 152 { 153 static int read_cnt; 154 static char *read_ptr; 155 static char read_buf[100]; 156 157 if (read_cnt <= 0) { 158 again: 159 if ( (read_cnt = read(fd, read_buf, sizeof(read_buf))) < 0) { 160 if (errno == EINTR) 161 goto again; 162 return -1; 163 } else if (read_cnt == 0) 164 return 0; 165 read_ptr = read_buf; 166 } 167 read_cnt--; 168 *ptr = *read_ptr++; 169 170 return 1; 171 } 172 173 ssize_t Readline(int fd, void *vptr, size_t maxlen) 174 { 175 ssize_t n, rc; 176 char c, *ptr; 177 178 ptr = vptr; 179 for (n = 1; n < maxlen; n++) { 180 if ( (rc = my_read(fd, &c)) == 1) { 181 *ptr++ = c; 182 if (c == '\n') 183 break; 184 } else if (rc == 0) { 185 *ptr = 0; 186 return n - 1; 187 } else 188 return -1; 189 } 190 *ptr = 0; 191 192 return n; 193 }

      wrap.h

      1 #ifndef __WRAP_H_ 2 #define __WRAP_H_ 3 4 void perr_exit(const char *s); 5 int Accept(int fd, struct sockaddr *sa, socklen_t *salenptr); 6 int Bind(int fd, const struct sockaddr *sa, socklen_t salen); 7 int Connect(int fd, const struct sockaddr *sa, socklen_t salen); 8 int Listen(int fd, int backlog); 9 int Socket(int family, int type, int protocol); 10 ssize_t Read(int fd, void *ptr, size_t nbytes); 11 ssize_t Write(int fd, const void *ptr, size_t nbytes); 12 int Close(int fd); 13 ssize_t Readn(int fd, void *vptr, size_t n); 14 ssize_t Writen(int fd, const void *vptr, size_t n); 15 ssize_t my_read(int fd, char *ptr); 16 ssize_t Readline(int fd, void *vptr, size_t maxlen); 17 18 #endif

      server.c

      1 #include 2 #include 3 #include 4 #include 5 #include 6 #include 7 #include 8 9 #include "wrap.h" 10 11 #define MAXLINE 8192 12 #define SERV_PORT 8000 13 14 struct s_info 15 { //定義一個結構體, 將地址結構跟cfd捆綁 16 struct sockaddr_in cliaddr; 17 int connfd; 18 }; 19 20 void *do_work(void *arg) 21 { 22 int n,i; 23 struct s_info *ts = (struct s_info*)arg; 24 char buf[MAXLINE]; 25 char str[INET_ADDRSTRLEN]; //#define INET_ADDRSTRLEN 16 可用"[+d"查看 26 27 while (1) 28 { 29 n = Read(ts->connfd, buf, MAXLINE); //讀客戶端 30 if (n == 0) 31 { 32 printf("the client %d closed...\n", ts->connfd); 33 break; //跳出循環,關閉cfd 34 } 35 printf("received from %s at PORT %d\n", 36 inet_ntop(AF_INET, &(*ts).cliaddr.sin_addr, str, sizeof(str)), 37 ntohs((*ts).cliaddr.sin_port)); //打印客戶端信息(IP/PORT) 38 39 for (i = 0; i < n; i++) 40 { 41 buf[i] = toupper(buf[i]); //小寫-->大寫 42 } 43 44 Write(STDOUT_FILENO, buf, n); //寫出至屏幕 45 Write(ts->connfd, buf, n); //回寫給客戶端 46 } 47 Close(ts->connfd); 48 49 return NULL; 50 } 51 52 int main(void) 53 { 54 struct sockaddr_in servaddr, cliaddr; 55 socklen_t cliaddr_len; 56 int listenfd, connfd; 57 pthread_t tid; 58 struct s_info ts[256]; //根據最大線程數創建結構體數組. 59 int i = 0; 60 61 listenfd = Socket(AF_INET, SOCK_STREAM, 0); //創建一個socket, 得到lfd 62 63 bzero(&servaddr, sizeof(servaddr)); //地址結構清零 64 servaddr.sin_family = AF_INET; 65 servaddr.sin_addr.s_addr = htonl(INADDR_ANY); //指定本地任意IP 66 servaddr.sin_port = htons(SERV_PORT); //指定端口號 8000 67 68 Bind(listenfd, (struct sockaddr *)&servaddr, sizeof(servaddr)); //綁定 69 70 Listen(listenfd, 128); //設置同一時刻鏈接服務器上限數 71 72 printf("Accepting client connect ...\n"); 73 74 while (1) 75 { 76 cliaddr_len = sizeof(cliaddr); 77 connfd = Accept(listenfd, (struct sockaddr *)&cliaddr, &cliaddr_len); //阻塞監聽客戶端鏈接請求 78 ts[i].cliaddr = cliaddr; 79 ts[i].connfd = connfd; 80 81 pthread_create(&tid, NULL, do_work, (void*)&ts[i]); 82 pthread_detach(tid); //子線程分離,防止僵線程產生. 83 i++; 84 if(i == 256) 85 { 86 break; 87 } 88 } 89 90 return 0; 91 }

      client.c

      1 /* client.c */ 2 #include 3 #include 4 #include 5 #include 6 #include 7 #include "wrap.h" 8 9 #define MAXLINE 80 10 #define SERV_PORT 8000 11 12 int main(int argc, char *argv[]) 13 { 14 struct sockaddr_in servaddr; 15 char buf[MAXLINE]; 16 int sockfd, n; 17 18 sockfd = Socket(AF_INET, SOCK_STREAM, 0); 19 20 bzero(&servaddr, sizeof(servaddr)); 21 servaddr.sin_family = AF_INET; 22 inet_pton(AF_INET, "127.0.0.1", &servaddr.sin_addr.s_addr); 23 servaddr.sin_port = htons(SERV_PORT); 24 25 Connect(sockfd, (struct sockaddr *)&servaddr, sizeof(servaddr)); 26 27 while (fgets(buf, MAXLINE, stdin) != NULL) 28 { 29 Write(sockfd, buf, strlen(buf)); 30 n = Read(sockfd, buf, MAXLINE); 31 if (n == 0) 32 printf("the other side has been closed.\n"); 33 else 34 Write(STDOUT_FILENO, buf, n); 35 } 36 37 Close(sockfd); 38 39 return 0; 40 }

      makefile

      1 src = $(wildcard *.c) 2 obj = $(patsubst %.c, %.o, $(src)) 3 4 all: server client 5 6 server: server.o wrap.o 7 gcc server.o wrap.o -o server -Wall -lpthread 8 client: client.o wrap.o 9 gcc client.o wrap.o -o client -Wall -lpthread 10 11 %.o:%.c 12 gcc -c $< -Wall 13 14 .PHONY: clean all 15 clean: 16 -rm -rf server client $(obj)

      三、多路I/O轉接服務器

      1. IO多路轉接技術概述

      多路IO轉接服務器也叫做多任務IO服務器。該類服務器實現的主旨思想是,不再由應用程序自己監視客戶端連接,取而代之由內核替應用程序監視文件。

      1)先構造一張有關文件描述符的列表, 將要監聽的文件描述符添加到該表中

      2)然后調用一個函數,監聽該表中的文件描述符,直到這些描述符表中的一個進行I/O操作時,該函數才返回。

      該函數為阻塞函數

      函數對文件描述符的檢測操作是由內核完成的

      3)在返回時,它告訴進程有多少(哪些)描述符要進行I/O操作。

      IO操作方式:

      (1)阻塞等待

      優點:不占用cpu寶貴的時間片

      缺點:同一時刻只能處理一個操作, 效率低

      (2)非阻塞, 忙輪詢

      優點: 提高了程序的執行效率

      缺點: 需要占用更多的cpu和系統資源

      一個任務:

      多個任務:

      解決方案:使用IO多路轉接技術 select/poll/epoll

      第一種: select/poll

      注意:select 代收員比較懶, 她只會告訴你有幾個快遞到了,但是哪個快遞,你需要挨個遍歷一遍。

      第二種: epoll

      注意:epoll代收快遞員很勤快, 她不僅會告訴你有幾個快遞到了, 還會告訴你是哪個快遞公司的快遞。

      主要使用的方法有三種:select/poll/epoll

      2.?select

      (1)首先分析select的工作原理?

      select能監聽的文件描述符個數受限于FD_SETSIZE,一般為1024,單純改變進程打開的文件描述符個數并不能改變select監聽文件個數。

      解決1024以下客戶端時使用select是很合適的,但如果鏈接客戶端過多,select采用的是輪詢模型,會大大降低服務器響應效率,不應在select上投入更多精力。

      結合下面select函數的介紹及下面的偽代碼用select實現一個server端有助于上面select工作流程的理解:

      【Linux C編程】第十八章 高并發服務器(二)

      Linux 任務調度

      版權聲明:本文內容由網絡用戶投稿,版權歸原作者所有,本站不擁有其著作權,亦不承擔相應法律責任。如果您發現本站中有涉嫌抄襲或描述失實的內容,請聯系我們jiasou666@gmail.com 處理,核實后本網站將在24小時內刪除侵權內容。

      版權聲明:本文內容由網絡用戶投稿,版權歸原作者所有,本站不擁有其著作權,亦不承擔相應法律責任。如果您發現本站中有涉嫌抄襲或描述失實的內容,請聯系我們jiasou666@gmail.com 處理,核實后本網站將在24小時內刪除侵權內容。

      上一篇:Excel隱藏公式內容操作方法詳解教程
      下一篇:WPS文字辦公—給表格一鍵添加多行表格(wps怎么添加多行表格)
      相關文章
      久久青青草原亚洲av无码| 亚洲AV色欲色欲WWW| 午夜亚洲国产精品福利| 亚洲国产欧洲综合997久久| 亚洲影视一区二区| 久久狠狠高潮亚洲精品| 亚洲AV中文无码乱人伦下载| 亚洲精品亚洲人成人网| 亚洲午夜久久久久久噜噜噜| 亚洲无码精品浪潮| 久久久久亚洲AV成人网| 亚洲无码黄色网址| 亚洲精品中文字幕乱码三区| 久久亚洲伊人中字综合精品| 亚洲AV无码专区亚洲AV伊甸园| 亚洲AV无码专区亚洲AV伊甸园| 亚洲av日韩综合一区在线观看| 久久久亚洲欧洲日产国码农村| 亚洲成人在线网站| 99久久亚洲综合精品成人网| 亚洲精品福利网泷泽萝拉| 亚洲成人免费网址| 中文字幕亚洲综合小综合在线| 亚洲精品无码永久在线观看男男| 亚洲AV无码国产剧情| 亚洲日韩人妻第一页| 中文字幕亚洲乱码熟女一区二区| 亚洲精品制服丝袜四区| 亚洲电影一区二区| 亚洲成a人片在线观| 亚洲乱码中文字幕小综合| 亚洲无码一区二区三区| 日韩精品成人亚洲专区| 国产亚洲自拍一区| 亚洲制服中文字幕第一区| www.亚洲日本| 国产精品无码亚洲一区二区三区| 区三区激情福利综合中文字幕在线一区亚洲视频1 | 国产亚洲成人在线播放va| 国产亚洲精品资在线| 久久国产亚洲精品麻豆|