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

首頁 > 學院 > 操作系統 > 正文

TCP客戶/服務器程序實例——回射服務器

2024-06-28 13:27:30
字體:
來源:轉載
供稿:網友
TCP客戶/服務器程序實例——回射服務器

目錄

客戶/服務器程序源碼

POSIX信號處理

POSIX信號語義

處理SIGCHLD信號

處理僵死進程

處理被中斷的系統調用

wait和waitpid函數

wait和waitpid函數的區別

網絡編程可能會遇到的三種情況

TCP程序小結

數據格式

 

回射輸入行這樣一個客戶/服務器程序是一個雖然簡單然而卻很有效的網絡應用程序的例子。實現任何客戶/服務器網絡應用所需的所有基本步驟可通過本例子闡明。若想把本例子擴充成你自己的應用程序,你只需修改服務器對于來自客戶的輸入的處理過程。

image

TCP回射服務器程序:main函數
/* tcpserv01.c */#include <sys/socket.h>#include <strings.h>#include <sys/types.h>#include <netinet/in.h>#include <stdio.h>#include <stdlib.h>#include <unistd.h>#include <errno.h>intmain(int argc, char **argv){    int                   listenfd, connfd;    pid_t                 childpid;    socklen_t             clilen;    struct sockaddr_in    cliaddr, servaddr;        if((listenfd = socket(AF_INET, SOCK_STREAM, 0)) < 0)    {        perror("socket");        exit(1);    }        bzero(&servaddr, sizeof(servaddr));    servaddr.sin_family = AF_INET;    servaddr.sin_addr.s_addr = htonl(INADDR_ANY);    servaddr.sin_port = htons(9877);    if(bind(listenfd, (struct sockaddr *)&servaddr, sizeof(servaddr)) < 0)    {        perror("bind");        exit(1);    }    if(listen(listenfd, 5) < 0)    {        perror("listen");        exit(1);    }    for(;;)    {        clilen = sizeof(cliaddr);        if((connfd = accept(listenfd, (struct sockaddr *)&cliaddr, &clilen)) < 0)        {            perror("accept");                    exit(1);        }                if((childpid = fork()) < 0)        {            perror("fork");            exit(1);        }        else if(childpid == 0)    /* child PRocess */        {            if(close(listenfd) < 0) /* close listening socket */            {                perror("child close");                exit(1);            }            str_echo(connfd);    /* process the request */            exit(0);                    }        if(close(connfd) < 0) /* parent close connected socket */        {            perror("parent close");            exit(1);        }    }        }
TCP回射服務器程序:str_echo函數
/* str_echo.c */#include <stdio.h>#include <stdlib.h>#include <errno.h>voidstr_echo(int sockfd){    ssize_t        n;    char           buf[4096];again:    while((n = read(sockfd, buf, 4096)) > 0)        writen(sockfd, buf, n);    if(n < 0 && errno == EINTR)        goto again;    else if(n < 0)    {        perror("read");        exit(1);    }        }
TCP回射客戶程序:main函數
/* tcpcli01.c */#include <stdio.h>#include <strings.h>#include <string.h>#include <stdlib.h>#include <errno.h>#include <unistd.h>#include <sys/types.h>#include <netinet/in.h>#include <sys/socket.h>intmain(int argc, char **argv){    int                   sockfd;    struct sockaddr_in    servaddr;        if(argc != 2)    {        printf("usage: tcpcli <ipaddress> ");        exit(0);    }        if((sockfd = socket(AF_INET, SOCK_STREAM, 0)) < 0)    {        perror("socket");        exit(1);    }        bzero(&servaddr, sizeof(servaddr));    servaddr.sin_family = AF_INET;    servaddr.sin_port = htons(9877);    if(inet_pton(AF_INET, argv[1], &servaddr.sin_addr) < 0)    {        perror("inet_pton");            exit(1);    }        if(connect(sockfd, (struct sockaddr *)&servaddr, sizeof(servaddr)) < 0)    {        perror("connect");        exit(1);    }        str_cli(stdin, sockfd);    /* do it all */    exit(0);}
TCP回射客戶程序:str_cli函數
/* str_cli.c */#include <stdio.h>#include <stdlib.h>#include <string.h>voidstr_cli(FILE *fp, int sockfd){    char    sendline[4096], recvline[4096];    while(fgets(sendline, 4096, fp) != NULL)    {        writen(sockfd, sendline, strlen(sendline));                if(readline(sockfd, recvline, 4096) == 0)        {            printf("str_cli: server terminated prematurely");            exit(0);        }        fputs(recvline, stdout);    }}
服務器和客戶都要調用的自定義函數:
#include <stdio.h>#include <stdlib.h>#include <errno.h>#include <unistd.h>#include <sys/types.h>ssize_t                    /* read "n" bytes from a descriptor. */readn(int fd, void *vptr, size_t n){    size_t     nleft;    ssize_t    nread;    char       *ptr;        ptr = vptr;    nleft = n;    while(nleft > 0)    {        if((nread = read(fd, ptr, nleft)) < 0)        {            if(errno == EINTR)                nread = 0;    /* and call read() again */            else                return(-1);        }        else if(nread == 0)            break;            /* EOF */        nleft -= nread;        ptr += nread;    }    return(n - nleft);            /* return >= 0 */}ssize_t                    /* write n bytes to a descriptor */writen(int fd, const void *vptr, size_t n){    size_t         nleft;    ssize_t        nwritten;    const char    *ptr;    ptr = vptr;    nleft = n;    while(nleft > 0)    {        if((nwritten = write(fd, ptr, nleft)) <= 0)        {            if(nwritten < 0 && errno == EINTR)                nwritten = 0;    /* and call write again */            else                return(-1);    /* error */        }                nleft -= nwritten;        ptr += nwritten;    }    return(n - nwritten);}ssize_treadline(int fd, void *vptr, size_t maxlen){    ssize_t        n, rc;    char           c, *ptr;        ptr = vptr;    for(n = 1; n < maxlen; n++)    {    again:        if((rc = read(fd, &c, 1)) == 1)        {            *ptr++ = c;            if(c == '/n')                break;    /* newline is stored, like fgets() */        }        else if(rc == 0)        {            *ptr = 0;            return(n - 1);    /* EOF, n - 1 bytes were read */        }        else        {            if(errno == EINTR)                goto again;            return(-1);    /* error, errno set by read() */        }    }    *ptr = 0;    /* null terminate like fgets() */    return(n);}
正常啟動:

首先,我們在主機linux上后臺啟動服務器。

image

服務器啟動后,它調用socket、bind、listen和accept,并阻塞于accept調用。(我們還沒有啟動客戶。)在啟動客戶之前,我們運行netstat程序來檢查服務器監聽套接口的狀態。netstat -a

image

這個輸出正是我們所期望的:套接口處于LISTEN狀態,它有通配的本地IP地址,本地端口號為9877(這正是我們所配置的端口號)。netstat用星號“*”來表示一個為0的IP地址(INADDR_ANY,通配地址)或為0的端口號。

我們接著在同一個主機上啟動客戶,并指定服務器主機的IP地址為127.0.0.1(回饋地址)。當然我們也可以指定該地址為 該主機的普通(非回饋)IP地址。

image

客戶調用socket和connect,后者引起TCP的三路握手過程。當三路握手完成后,客戶中的connect和服務器中的accept均返回,連接于是建立。

服務器父進程再次調用accept并阻塞,等待下一個客戶連接。

我們特意在同一個主機上運行客戶和服務器,因為這是試驗客戶/服務器應用程序的最簡單方法。既然我們是在同一個主機上運行客戶和服務器,netstat對于所建立的TCP連接給出兩行輸出(下圖紅色框內)。

image

第一個ESTABLISHED行對應于服務器子進程的套接口,因為它的本地端口號是9877;第二個ESTABLISHED行對應于客戶進程的套接口,因為它的本地端口號是54076. 要是我們在不同的主機上運行客戶和服務器,那么客戶主機就只輸出客戶進程的套接口,服務器主機也只輸出兩個服務器進程(一個父進程、一個子進程)的套接口。

正常終止:

至此連接已經建立,我們在客戶的標準輸入中不論鍵入什么,都回射到它的標準輸出中。

image

注:<Ctrl-D>是我們的終端EOF字符

此時如果立即執行netstat命令,我們將看到如下結果:

image

當前連接的客戶端(它的本地端口號為60779)進入了TIME_WAIT狀態,而監聽服務器仍在等待另一個客戶連接。

在服務器子進程終止時,給父進程發送一個SIGCHILD信號。這一點在本例中發生了,但是我們沒有在代碼中捕獲該信號,而 該信號的缺省行為是被忽略。既然父進程未加處理,子進程于是進入僵死狀態(http://www.CUOXin.com/nufangrensheng/p/3509618.html)。我們可以使用ps命令驗證這一點。

image

子進程的狀態現在是Z(表示僵死)。父進程的狀態現在是S(表示為等待某種資源而睡眠)。

我們必須清理僵死進程,這就涉及到UNIX信號的處理。

POSIX信號處理

信號(signal)就是通知某個進程發生了某個事件,有時也稱為軟件中斷(software interrupt)。信號通常是異步發生的,也就是說進程預先不知道信號準確發生的時刻。

信號可以:

(1)由一個進程發給另一個進程(或自身)。

(2)由內核發給某個進程。

SIGCHILD信號就是由內核在任何一個進程終止時發給它的父進程的一個信號。

每個信號都有一個與之關聯的處置(disposition),也稱為行為(action)。我們調用sigaction函數(http://www.CUOXin.com/nufangrensheng/p/3515945.html)或signal函數(http://www.CUOXin.com/nufangrensheng/p/3514547.html)來設定一個信號的處置,并有三種選擇。

(1)我們可以提供一個函數,它將在特定信號發生的任何時刻被調用。這樣的函數稱為信號處理函數(signal handler),這種行為稱為捕獲(catching)信號。有兩個信號不能被捕獲,它們是SIGKILL和SIGSTOP。信號處理函數由信號值這個單一的整數參數來調用,且沒有返回值,其函數原型如下:

void handler( int signo );

對于大多數信號來說,調用sigaction函數并指定信號發生時所調用的函數就是捕獲信號所要做的全部工作。不過,SIGIO、SIGPOLL和SIGURG這些個別信號還要求捕獲它們的進程做些額外工作。

(2)我們可以把某個信號的處置設定為SIG_IGN來忽略(ignore)它。SIGKILL和SIGSTOP這兩個信號不能被忽略。

(3)我們可以把某個信號的處置設定為SIG_DFL來啟用它的缺?。╠efault)處置。缺省處置通常是在收到信號后終止進程,其中某些信號還在當前工作目錄產生一個進程的核心映像(core image,也稱為內存映像)。另有個別的缺省處理是忽略:SIGCHLD和SIGURG(帶外數據到達時發送)就是缺省處置為忽略的兩個信號。

POSIX信號語義

我們把符合POSIX的系統上的信號處理總結如下:

(1)一旦安裝了信號處理函數,它便一直安裝著(較早期的系統是每執行一次就將其拆除)。

(2)在一個信號處理函數運行期間,正被遞交的信號是阻塞的。而且,安裝處理函數時在傳遞給sigaction函數的sa_mask信號集中指定的任何額外信號也被阻塞。

(3)如果一個信號在被阻塞期間產生了一次或多次,那么該信號被解阻塞之后通常只遞交一次,也就是說UNIX信號缺省是不排隊的。

(4)利用siagprocmask函數(http://www.CUOXin.com/nufangrensheng/p/3515257.html)選擇性地阻塞或解阻塞一組信號是可能的。這使得我們可以做到在一段臨界區代碼執行期間,防止捕獲某些信號,以此保護這段代碼。

處理SIGCHILD信號

設置僵死(zombie)狀態的目的是維護子進程的信息,以便父進程在以后某個時候獲取。這些信息包括子進程的進程ID、終止狀態以及資源利用信息(CPU時間、內存使用量等等)。如果一個進程終止,而該進程有子進程處于僵死狀態,那么它的所有僵死子進程的父進程ID將被重置為1(init進程)。繼承這些子進程的init進程將清理它們(也就是說init進程將wait它們,從而去除它們的僵死狀態)。有些UNIX系統在ps命令輸出的COMMAND欄以<defunct>指明僵死進程。

處理僵死進程

我們顯然不愿意留存僵死進程。它們占用內核中的空間,最終可能導致我們耗盡進程資源。無論何時我們fork子進程都得wait它們,以防它們變成僵死進程。為此我們建立一個俘獲SIGCHLD信號的信號處理函數,在函數體中我們調用wait。通過在TCP回射服務器程序:main函數中的listen調用之后增加如下函數調用:

signal(SIGCHLD, sig_chld);

這樣我們就建立了該信號處理函數。(這必須在fork第一個子進程之前完成,并且只做一次。) 我們接著定義名為sig_chld的這個信號處理函數,如下:

void sig_chld(int signo){    pid_t pid;    int   stat;    pid = wait(&stat);    printf("child %d terminatted/n", pid);    return;}

處理僵死進程的可移植方法就是捕獲SIGCHLD,并調用wait或waitpid。

新的問題是:在某些系統上(這些 系統標準C函數庫中提供的signal函數不會致使內核自動重啟被中斷的系統調用),SIGCHLD信號被捕獲并處理后,慢系統調用accept會返回一個EINTR錯誤(被中斷的系統調用).

處理被中斷的系統調用

慢系統調用(slow system call)是指那些可能永遠阻塞的系統調用(調用有可能永遠無法返回)。多數網絡支持函數都屬于這一類。

適用于慢系統調用的基本規則是:當阻塞于某個慢系統調用的一個進程捕獲某個信號且相應信號處理函數返回時,該系統調用可能返回一個EINTR錯誤。有些內核自動重啟某些被中斷的系統調用。

為了處理被中斷的accept,我們把對accept的調用從for循環開始修改如下:

for(;;){    clilen = sizeof(cliaddr);    if((connfd = accept(listenfd, (struct sockaddr *)&cliaddr, &clilen)) < 0)    {        if(errno == EINTR)            continue;    /* back to for() */        else        {            perror("accept error");            exit(1);        }    }}

這段代碼所做的事情就是自己重啟被中斷的系統調用。對于accept以及諸如read、write、select和open之類函數來說,這是合適的。不過有一個函數我們不能重啟:connect。如果該函數返回EINTR,我們就不能再次調用它,否則將立即返回一個錯誤。當connect被一個捕獲的信號中斷而且不自動重啟時,我們必須調用select來等待連接完成。

wait和waitpid函數
#include <sys/wait.h>pid_t wait(int *statloc);pid_t waitpid(pid_t pid, int *statloc, int options);二者均返回:若成功則返回已終止子進程的進程ID,若出錯則返回-1

函數wait和waitpid均返回兩個值:函數返回值是已終止子進程ID號,子進程的終止狀態(一個整數)則通過statloc指針返回。我們可以調用三個宏來檢查終止狀態,并辨別子進程是正常終止、由某個信號殺死還是僅僅由作業控制停止而已(http://www.CUOXin.com/nufangrensheng/p/3510101.html)。

如果調用wait的進程沒有已終止的子進程,不過有一個或多個子進程仍在執行,那么wait將阻塞到現有子進程第一個終止為止。

waitpid函數對于等待哪個進程以及是否阻塞給了我們更多的控制。首先,pid參數允許我們指定想等待的進程ID,值-1表示等待第一個終止的子進程。其次,options參數允許我們指定附加選項。最常用的選項是WNOHANG,它告知內核在沒有已終止子進程時不要阻塞。

函數wait和waipid的區別

為了說明wait和waitpid的區別,我們試想如下情況:

image

當客戶終止時,所有打開的描述符字由內核自動關閉,且所有5個連接基本在同一時刻終止。這就引發了5個FIN,每個連接一個,它們反過來使服務器的5個子進程基本在同一時刻終止。這又導致差不多在同一時刻遞交5個SIGCHLD信號給父進程。

image

如果我們調用函數wait來處理已終止的子進程,那么只會捕獲到一個SIGCHLD信號。也就是說,其他的4個子進程仍然作為僵死進程存在著。

所以,建立一個信號處理函數并在其中調用wait并不足以防止出現僵死進程。本問題在于:所有5個信號都在信號處理函數執行之前產生,而信號處理函數只執行一次,因為UNIX信號一般是不排隊的。

正確的解決辦法是調用waitpid而不是wait。如下所示給出了正確處理SIGCHLD的sig_chld函數的版本。這個版本管用的原因在于:我們在一個循環內調用waitpid,以獲取所有已終止子進程的狀態。我們必須指定WNOHANG選項,它告知waitpid在有尚未終止的子進程在運行時不要阻塞。我們不能在循環內調用wait,因為沒有辦法防止wait在尚有未終止的子進程在運行時阻塞。

voidsig_chld(int signo){    pid_t    pid;    int      stat;    while((pid = waitpid(-1, &stat, WNOHANG)) > 0)        printf("child %d terminated/n", pid);    return;}

服務器的最終版本

/* tcpserv01.c */#include <sys/socket.h>#include <strings.h>#include <sys/types.h>#include <netinet/in.h>#include <stdio.h>#include <stdlib.h>#include <unistd.h>#include <errno.h>intmain(int argc, char **argv){    int                    listenfd, connfd;    pid_t                  childpid;    socklen_t              clilen;    struct sockaddr_in     cliaddr, servaddr;    void                   sig_chld(int);        if((listenfd = socket(AF_INET, SOCK_STREAM, 0)) < 0)    {        perror("socket");        exit(1);    }        bzero(&servaddr, sizeof(servaddr));    servaddr.sin_family = AF_INET;    servaddr.sin_addr.s_addr = htonl(INADDR_ANY);    servaddr.sin_port = htons(9877);    if(bind(listenfd, (struct sockaddr *)&servaddr, sizeof(servaddr)) < 0)    {        perror("bind");        exit(1);    }    if(listen(listenfd, 5) < 0)    {        perror("listen");        exit(1);    }       signal(SIGCHLD, sig_chld);    /* must call waitpid() */    for(;;)    {        clilen = sizeof(cliaddr);        if((connfd = accept(listenfd, (struct sockaddr *)&cliaddr, &clilen)) < 0)        {            if(errno == EINTR)                  continue;        /* back to for */                    else            {                  perror("accept");                  exit(1);            }        }                if((childpid = fork()) < 0)        {            perror("fork");            exit(1);        }        else if(childpid == 0)    /* child process */        {            if(close(listenfd) < 0) /* close listening socket */            {                perror("child close");                exit(1);            }            str_echo(connfd);    /* process the request */            exit(0);                    }        if(close(connfd) < 0) /* parent close connected socket */        {            perror("parent close");            exit(1);        }    }        }
在網絡編程時可能會遇到的三種情況:

(1)當fork子進程時,必須捕獲SIGCHLD信號。

(2)當捕獲信號時,必須處理被中斷的系統調用。

(3)SIGCHLD的信號處理函數必須正確編寫,應使用waitpid函數以免留下僵死進程。

TCP程序實例小結:

在客戶和服務器可以彼此通信之前,每一端都得指定連接的套接口對:本地IP地址、本地端口、遠地IP地址、遠地端口。在下圖中我們以粗體圓點標出了這四個值。該圖處于客戶端的角度。遠地IP地址和遠地端口必須在客戶端調用connect時指定,而兩個本地值通常就由內核作為connect的一部分來選定??蛻粢部稍谡{用connect之前,通過調用bind來指定其中一個或全部兩個本地值,不過這么做不常見。

image

客戶可以在連接建立后通過調用getsockname獲取由內核指定的兩個本地值。

下圖中標出了同樣的四個值,不過是處于服務器的角度。

本地端口(服務器眾所周知的端口)由bind指定。bind調用中指定的本地IP地址通常是通配IP地址,盡管服務器也可以指定一個非通配的IP地址來限定接收目標為某個特定本地接口的連接。如果服務器在一個多宿主機上綁定通配IP地址,那么它可以在連接建立后通過調用getsockname來確定本地IP地址。兩個遠地值則由accept調用返回給服務器。如果另外一個程序由調用accept的服務器通過調用exec來執行,則這個新程序可以在必要時調用getpeername來確定客戶的IP地址和端口號。

image

數據格式

例子:在客戶和服務器之間 傳遞文本串

修改我們的服務器程序,它仍然從客戶讀入一行文本,不過新的服務器期望該文本行包含由空格分開的兩個整數,服務器將返回這兩個整數的和。我們的客戶和服務器程序的main函數仍保持不變,str_cli函數也保持不變,所有修改都在str_echo函數中,如下所示:

#include <stdio.h>#include <sys/types.h>#include <stdlib.h>#include <unistd.h>#include <string.h>voidstr_echo(int sockfd){    long       arg1, arg2;    ssize_t    n;    char       line[4096];    for(;;)    {        if((n = readline(sockfd, line, 4096)) == 0)            return;    /*  connection closed by other end */        if(sscanf(line, "%ld%ld", &arg1, &arg2) == 2)            snprintf(line, sizeof(line), "%ld/n", arg1 + arg2);        else            snprintf(line, sizeof(line), "input error/n");        n = strlen(line);        writen(sockfd, line, n);    }}

我們調用sscanf把文本串中的兩個參數轉換為長整數,然后調用snprintf把結果轉換為文本串。

不論客戶和服務器主機的字節序如何,這個新的客戶和服務器對都工作的很好。

例子:在客戶與服務器之間傳遞二進制結構

現在我們 把客戶和服務器程序修改為穿越套接口傳遞二進制值而不是文本串。

我們的客戶和服務器程序的main函數無需改動。另外我們給兩個參數定義了一個結構,給結果定義了另一個結構。

#ifndef _COMMON_H#define _COMMON_Hstruct args{    long arg1;    long arg2;    };struct result{    long sum;};#endif
/* str_cli09.c */#include "common.h"#include <stdio.h>#include <stdlib.h>#include <string.h>voidstr_cli(FILE *fp, int sockfd){    char    sendline[4096], recvline[4096];    struct args    args;        struct result    result;    while(fgets(sendline, 4096, fp) != NULL)    {        if(sscanf(sendline, "%ld%ld", &args.arg1, &args.arg2) != 2)        {            printf("invalid input: %s", sendline);            continue;        }        writen(sockfd, &args, sizeof(args));        if(readn(sockfd, &result, sizeof(result)) == 0)        {            printf("str_cli: server terminated prematurely");            exit(1);        }                printf("%ld/n", result.sum);    }}
/* str_echo09.c */#include <stdio.h>#include "common.h"#include <stdlib.h>#include <errno.h>voidstr_echo(int sockfd){    ssize_t        n;    struct args    args;    struct result    result;        for(;;)    {        if((n = readn(sockfd, &args, sizeof(args))) == 0)            return;        result.sum = args.arg1 + args.arg2;        writen(sockfd, &result, sizeof(result));    }    }

如果我們在具有相同體系結構的兩個主機上運行我們的客戶和服務器程序,那么什么問題都沒有。但是如果在具有不同體系結構的兩個主機上運行同樣的客戶和服務器程序(例如服務器運行在大端系統,而客戶運行在小端系統上),那就無法工作了。

本例子實際上存在三個潛在的問題:

(1)不同的實現以不同的格式存儲二進制數。(大端和小端)

(2)不同的實現在存儲相同的C數據類型上可能存在差異。(32位系統和64位系統)

(3)不同的實現給結構打包的方式存在差異,這取決于各種數據類型所用的位數以及機器的對齊限制。

因此,穿越套接口傳送二進制結構絕不是明智的選擇。

解決這種數據格式問題有兩個常用的方法:

(1)把所有的數值數據作為文本串來傳遞。當然這里假設客戶和服務器主機具有相同的字符集。

(2)顯示定義所支持數據類型的二進制格式(位數、大端或小端),并以這樣的格式在客戶和服務器之間傳遞所有數據。


發表評論 共有條評論
用戶名: 密碼:
驗證碼: 匿名發表
亚洲香蕉成人av网站在线观看_欧美精品成人91久久久久久久_久久久久久久久久久亚洲_热久久视久久精品18亚洲精品_国产精自产拍久久久久久_亚洲色图国产精品_91精品国产网站_中文字幕欧美日韩精品_国产精品久久久久久亚洲调教_国产精品久久一区_性夜试看影院91社区_97在线观看视频国产_68精品久久久久久欧美_欧美精品在线观看_国产精品一区二区久久精品_欧美老女人bb
欧美大成色www永久网站婷| 欧美日韩国产激情| 亚洲成人在线网| 色av吧综合网| 国产精品丝袜一区二区三区| 亚洲欧美色图片| 中文字幕综合一区| 亚洲人成在线电影| 国产精品国产三级国产aⅴ9色| 欧美又大粗又爽又黄大片视频| 亚洲激情第一页| 中文字幕在线视频日韩| 最近2019年手机中文字幕| 热久久免费视频精品| 国产精品免费在线免费| 伊人伊成久久人综合网小说| 91精品在线影院| 亚洲精品第一国产综合精品| 国产精品视频免费在线| 亚洲精品电影网在线观看| 久久深夜福利免费观看| 午夜精品久久久久久99热软件| 精品高清一区二区三区| 国产精品久久久久久av福利| 日韩美女中文字幕| 国产97在线观看| 亚洲免费视频观看| 97成人精品视频在线观看| 精品久久久久久中文字幕| 日韩一二三在线视频播| 91亚洲va在线va天堂va国| 国产精品免费观看在线| 日韩大陆欧美高清视频区| 国产成人精品久久二区二区91| 欧美制服第一页| 亚洲成人中文字幕| 亚洲乱码一区二区| 欧洲成人午夜免费大片| 欧美影院在线播放| 日本一区二区在线播放| 在线观看久久久久久| 色偷偷888欧美精品久久久| 97高清免费视频| 国产精品一区二区性色av| 国产精品美女视频网站| 日韩视频中文字幕| 亚洲欧美日韩第一区| 久久影视电视剧免费网站| 最近2019中文免费高清视频观看www99| 97精品国产97久久久久久| 国产精品美女久久久免费| 91亚洲va在线va天堂va国| 亚洲欧美另类在线观看| 中文字幕亚洲国产| 精品国偷自产在线视频99| 在线成人免费网站| 欧美综合国产精品久久丁香| 色综合视频一区中文字幕| 亚洲国产高清福利视频| 欧美一级黄色网| 亚洲视频在线观看视频| 国产欧美日韩中文字幕| 国产日韩欧美视频在线| 亚洲精品电影网| 日韩中文字幕在线精品| 精品久久久久久久中文字幕| 日韩av色在线| 亚洲九九九在线观看| 91久久久久久久久久久久久| 久久国产精品99国产精| 亚洲欧美三级伦理| 不卡av在线播放| 久久久久亚洲精品国产| 亚洲美女在线看| 欧美日韩国产在线| 欧美性xxxxxxx| 久久精品国产亚洲7777| 国产一区玩具在线观看| 2018日韩中文字幕| 亚洲久久久久久久久久| 国产精品网红福利| 久久天堂av综合合色| 亚洲无限av看| 久久好看免费视频| 在线亚洲欧美视频| 亚洲精品电影网站| 欧美黑人巨大xxx极品| 中文字幕亚洲情99在线| xxxxx91麻豆| 亚洲国产精品热久久| 日韩av在线网页| 欧美性极品xxxx娇小| 红桃视频成人在线观看| 欧美一级淫片丝袜脚交| 久久久精品视频成人| 亚洲第一天堂无码专区| 亚洲区一区二区| 久久精品成人欧美大片| 欧美一区第一页| 精品激情国产视频| 黑人巨大精品欧美一区二区| 最近中文字幕2019免费| 欧美在线精品免播放器视频| 美女久久久久久久久久久| 视频在线一区二区| 91午夜理伦私人影院| 欧美电影在线观看| 国产精品日韩在线播放| 欧美一区二区三区精品电影| 国产手机视频精品| 97视频在线观看成人| 久久久久免费视频| 最近2019中文字幕mv免费看| 欧美激情网友自拍| 精品人伦一区二区三区蜜桃网站| 精品久久久久久久久久国产| 日韩av电影国产| 国产91露脸中文字幕在线| 97国产在线观看| 久久99视频免费| 色噜噜狠狠狠综合曰曰曰| 午夜精品三级视频福利| 亚洲美女性生活视频| 国产精品久久电影观看| 2020欧美日韩在线视频| 欧美巨猛xxxx猛交黑人97人| 精品无人区太爽高潮在线播放| 中文字幕亚洲综合久久筱田步美| 国产最新精品视频| 日韩国产精品一区| 亚洲国产欧美一区二区丝袜黑人| 亚洲国产精品yw在线观看| 国产拍精品一二三| 国产成人亚洲综合| 久久免费视频网| 久久久久久久999| 青青久久av北条麻妃黑人| 欧美激情国内偷拍| 韩国三级电影久久久久久| 欧美成人精品在线观看| 羞羞色国产精品| 亚洲欧美在线磁力| 92福利视频午夜1000合集在线观看| 日韩精品视频免费在线观看| 中文字幕亚洲激情| 欧美亚洲成人免费| 亚洲一区二区日本| 欧美精品在线免费| 91精品久久久久久久久久久久久久| 欧美老女人bb| 国产精品免费一区豆花| 亚洲第一av网站| 91免费国产网站| 九九精品在线视频| 欧美激情欧美激情在线五月| www.亚洲免费视频| 狠狠色香婷婷久久亚洲精品| 日本精品久久久久久久| 伊人伊成久久人综合网站| 日韩精品免费在线视频| 色偷偷偷亚洲综合网另类| 国产精品av网站| 欧美黑人狂野猛交老妇| 91国偷自产一区二区三区的观看方式|