一区二区三区日韩精品-日韩经典一区二区三区-五月激情综合丁香婷婷-欧美精品中文字幕专区

分享

gRPC官方文檔(gRPC基礎(chǔ):C++)

 碼農(nóng)書館 2020-09-14

文章來(lái)自gRPC 官方文檔中文版

本教程提供了C++程序員如何使用gRPC的指南。

通過(guò)學(xué)習(xí)教程中例子,你可以學(xué)會(huì)如何:

  • 在一個(gè) .proto 文件內(nèi)定義服務(wù).
  • 用 protocol buffer 編譯器生成服務(wù)器和客戶端代碼.
  • 使用 gRPC 的 C++ API 為你的服務(wù)實(shí)現(xiàn)一個(gè)簡(jiǎn)單的客戶端和服務(wù)器.

假設(shè)你已經(jīng)閱讀了概覽并且熟悉protocol buffers. 注意,教程中的例子使用的是 protocol buffers 語(yǔ)言的 proto3 版本,它目前只是 alpha 版:可以在proto3 語(yǔ)言指南和 protocol buffers 的 Github 倉(cāng)庫(kù)的版本注釋發(fā)現(xiàn)更多關(guān)于新版本的內(nèi)容.

這算不上是一個(gè)在 C++ 中使用 gRPC 的綜合指南:以后會(huì)有更多的參考文檔.

為什么使用 gRPC?

我們的例子是一個(gè)簡(jiǎn)單的路由映射的應(yīng)用,它允許客戶端獲取路由特性的信息,生成路由的總結(jié),以及交互路由信息,如服務(wù)器和其他客戶端的流量更新。

有了 gRPC, 我們可以一次性的在一個(gè) .proto 文件中定義服務(wù)并使用任何支持它的語(yǔ)言去實(shí)現(xiàn)客戶端和服務(wù)器,反過(guò)來(lái),它們可以在各種環(huán)境中,從Google的服務(wù)器到你自己的平板電腦- gRPC 幫你解決了不同語(yǔ)言間通信的復(fù)雜性以及環(huán)境的不同.使用 protocol buffers 還能獲得其他好處,包括高效的序列號(hào),簡(jiǎn)單的 IDL 以及容易進(jìn)行接口更新。

例子代碼和設(shè)置

教程的代碼在這里 grpc/grpc/examples/cpp/route_guide. 要下載例子,通過(guò)運(yùn)行下面的命令去克隆grpc代碼庫(kù):

$ git clone https://github.com/grpc/grpc.git

改變當(dāng)前的目錄到examples/cpp/route_guide

$ cd examples/cpp/route_guide

你還需要安裝生成服務(wù)器和客戶端的接口代碼相關(guān)工具-如果你還沒(méi)有安裝的話,查看下面的設(shè)置指南 C++快速開(kāi)始指南。

定義服務(wù)

我們的第一步(可以從概覽中得知)是使用 protocol buffers去定義 gRPC service 和方法 request 以及 response 的類型。你可以在examples/protos/route_guide.proto看到完整的 .proto 文件。

要定義一個(gè)服務(wù),你必須在你的 .proto 文件中指定 service

service RouteGuide {
   ...
}

然后在你的服務(wù)中定義 rpc 方法,指定請(qǐng)求的和響應(yīng)類型。gRPC允 許你定義4種類型的 service 方法,在 RouteGuide 服務(wù)中都有使用:

  • 一個(gè) 簡(jiǎn)單 RPC , 客戶端使用存根發(fā)送請(qǐng)求到服務(wù)器并等待響應(yīng)返回,就像平常的函數(shù)調(diào)用一樣。
   // Obtains the feature at a given position.
   rpc GetFeature(Point) returns (Feature) {}

  • 一個(gè) 服務(wù)器端流式 RPC , 客戶端發(fā)送請(qǐng)求到服務(wù)器,拿到一個(gè)流去讀取返回的消息序列。 客戶端讀取返回的流,直到里面沒(méi)有任何消息。從例子中可以看出,通過(guò)在 響應(yīng) 類型前插入 stream 關(guān)鍵字,可以指定一個(gè)服務(wù)器端的流方法。
  // Obtains the Features available within the given Rectangle.  Results are
  // streamed rather than returned at once (e.g. in a response message with a
  // repeated field), as the rectangle may cover a large area and contain a
  // huge number of features.
  rpc ListFeatures(Rectangle) returns (stream Feature) {}

  • 一個(gè) 客戶端流式 RPC , 客戶端寫入一個(gè)消息序列并將其發(fā)送到服務(wù)器,同樣也是使用流。一旦客戶端完成寫入消息,它等待服務(wù)器完成讀取返回它的響應(yīng)。通過(guò)在 請(qǐng)求 類型前指定 stream 關(guān)鍵字來(lái)指定一個(gè)客戶端的流方法。
  // Accepts a stream of Points on a route being traversed, returning a
  // RouteSummary when traversal is completed.
  rpc RecordRoute(stream Point) returns (RouteSummary) {}

  • 一個(gè) 雙向流式 RPC 是雙方使用讀寫流去發(fā)送一個(gè)消息序列。兩個(gè)流獨(dú)立操作,因此客戶端和服務(wù)器可以以任意喜歡的順序讀寫:比如, 服務(wù)器可以在寫入響應(yīng)前等待接收所有的客戶端消息,或者可以交替的讀取和寫入消息,或者其他讀寫的組合。 每個(gè)流中的消息順序被預(yù)留。你可以通過(guò)在請(qǐng)求和響應(yīng)前加 stream 關(guān)鍵字去制定方法的類型。
  // Accepts a stream of RouteNotes sent while a route is being traversed,
  // while receiving other RouteNotes (e.g. from other users).
  rpc RouteChat(stream RouteNote) returns (stream RouteNote) {}

我們的 .proto 文件也包含了所有請(qǐng)求的 protocol buffer 消息類型定義以及在服務(wù)方法中使用的響應(yīng)類型-比如,下面的Point消息類型:

// Points are represented as latitude-longitude pairs in the E7 representation
// (degrees multiplied by 10**7 and rounded to the nearest integer).
// Latitudes should be in the range +/- 90 degrees and longitude should be in
// the range +/- 180 degrees (inclusive).
message Point {
  int32 latitude = 1;
  int32 longitude = 2;
}

生成客戶端和服務(wù)器端代碼

接下來(lái)我們需要從 .proto 的服務(wù)定義中生成 gRPC 客戶端和服務(wù)器端的接口。我們通過(guò) protocol buffer 的編譯器 protoc 以及一個(gè)特殊的 gRPC C++ 插件來(lái)完成。

簡(jiǎn)單起見(jiàn),我們提供一個(gè) makefile 幫您用合適的插件,輸入,輸出去運(yùn)行 protoc(如果你想自己去運(yùn)行,確保你已經(jīng)安裝了 protoc,并且請(qǐng)遵循下面的 gRPC 代碼安裝指南)來(lái)操作:

$ make route_guide.grpc.pb.cc route_guide.pb.cc

實(shí)際上運(yùn)行的是:

$ protoc -I ../../protos --grpc_out=. --plugin=protoc-gen-grpc=`which grpc_cpp_plugin` ../../protos/route_guide.proto
$ protoc -I ../../protos --cpp_out=. ../../protos/route_guide.proto

運(yùn)行這個(gè)命令可以在當(dāng)前目錄中生成下面的文件:

  • route_guide.pb.h, 聲明生成的消息類的頭文件
  • route_guide.pb.cc, 包含消息類的實(shí)現(xiàn)
  • route_guide.grpc.pb.h, 聲明你生成的服務(wù)類的頭文件
  • route_guide.grpc.pb.cc, 包含服務(wù)類的實(shí)現(xiàn)

這些包括:

  • 所有的填充,序列化和獲取我們請(qǐng)求和響應(yīng)消息類型的 protocol buffer 代碼
  • 名為 RouteGuide 的類,包含
    • 為了客戶端去調(diào)用定義在 RouteGuide 服務(wù)的遠(yuǎn)程接口類型(或者 存根 )
    • 讓服務(wù)器去實(shí)現(xiàn)的兩個(gè)抽象接口,同時(shí)包括定義在 RouteGuide 中的方法。

創(chuàng)建服務(wù)器

首先來(lái)看看我們?nèi)绾蝿?chuàng)建一個(gè) RouteGuide 服務(wù)器。如果你只對(duì)創(chuàng)建 gRPC 客戶端感興趣,你可以跳過(guò)這個(gè)部分,直接到創(chuàng)建客戶端 (當(dāng)然你也可能發(fā)現(xiàn)它也很有意思)。

RouteGuide 服務(wù)工作有兩個(gè)部分:

  • 實(shí)現(xiàn)我們服務(wù)定義的生成的服務(wù)接口:做我們的服務(wù)的實(shí)際的“工作”。
  • 運(yùn)行一個(gè) gRPC 服務(wù)器,監(jiān)聽(tīng)來(lái)自客戶端的請(qǐng)求并返回服務(wù)的響應(yīng)。

你可以從examples/cpp/route_guide/route_guide_server.cc看到我們的 RouteGuide 服務(wù)器的實(shí)現(xiàn)代碼?,F(xiàn)在讓我們近距離研究它是如何工作的。

實(shí)現(xiàn)RouteGuide

我們可以看出,服務(wù)器有一個(gè)實(shí)現(xiàn)了生成的 RouteGuide::Service 接口的 RouteGuideImpl 類:

class RouteGuideImpl final : public RouteGuide::Service {
...
}

在這個(gè)場(chǎng)景下,我們正在實(shí)現(xiàn) 同步 版本的RouteGuide,它提供了 gRPC 服務(wù)器缺省的行為。同時(shí),也有可能去實(shí)現(xiàn)一個(gè)異步的接口 RouteGuide::AsyncService,它允許你進(jìn)一步定制服務(wù)器線程的行為,雖然在本教程中我們并不關(guān)注這點(diǎn)。

RouteGuideImpl 實(shí)現(xiàn)了所有的服務(wù)方法。讓我們先來(lái)看看最簡(jiǎn)單的類型 GetFeature,它從客戶端拿到一個(gè) Point 然后將對(duì)應(yīng)的特性返回給數(shù)據(jù)庫(kù)中的 Feature。

  Status GetFeature(ServerContext* context, const Point* point,
                    Feature* feature) override {
    feature->set_name(GetFeatureName(*point, feature_list_));
    feature->mutable_location()——>CopyFrom(*point);
    return Status::OK;
  }

這個(gè)方法為 RPC 傳遞了一個(gè)上下文對(duì)象,包含了客戶端的 Point protocol buffer 請(qǐng)求以及一個(gè)填充響應(yīng)信息的Feature protocol buffer。在這個(gè)方法中,我們用適當(dāng)?shù)男畔⑻畛?Feature,然后返回OK的狀態(tài),告訴 gRPC 我們已經(jīng)處理完 RPC,并且 Feature 可以返回給客戶端。

現(xiàn)在讓我們看看更加復(fù)雜點(diǎn)的情況——流式RPC。 ListFeatures 是一個(gè)服務(wù)器端的流式 RPC,因此我們需要給客戶端返回多個(gè) Feature

  Status ListFeatures(ServerContext* context, const Rectangle* rectangle,
                      ServerWriter<Feature>* writer) override {
    auto lo = rectangle->lo();
    auto hi = rectangle->hi();
    long left = std::min(lo.longitude(), hi.longitude());
    long right = std::max(lo.longitude(), hi.longitude());
    long top = std::max(lo.latitude(), hi.latitude());
    long bottom = std::min(lo.latitude(), hi.latitude());
    for (const Feature& f : feature_list_) {
      if (f.location().longitude() >= left &&
          f.location().longitude() <= right &&
          f.location().latitude() >= bottom &&
          f.location().latitude() <= top) {
        writer->Write(f);
      }
    }
    return Status::OK;
  }

如你所見(jiàn),這次我們拿到了一個(gè)請(qǐng)求對(duì)象(客戶端期望在 Rectangle 中找到的 Feature)以及一個(gè)特殊的 ServerWriter 對(duì)象,而不是在我們的方法參數(shù)中獲取簡(jiǎn)單的請(qǐng)求和響應(yīng)對(duì)象。在方法中,根據(jù)返回的需要填充足夠多的 Feature 對(duì)象,用 ServerWriterWrite() 方法寫入。最后,和我們簡(jiǎn)單的 RPC 例子相同,我們返回Status::OK去告知gRPC我們已經(jīng)完成了響應(yīng)的寫入。

如果你看過(guò)客戶端流方法RecordRoute,你會(huì)發(fā)現(xiàn)它很類似,除了這次我們拿到的是一個(gè)ServerReader而不是請(qǐng)求對(duì)象和單一的響應(yīng)。我們使用 ServerReaderRead() 方法去重復(fù)的往請(qǐng)求對(duì)象(在這個(gè)場(chǎng)景下是一個(gè) Point)讀取客戶端的請(qǐng)求直到?jīng)]有更多的消息:在每次調(diào)用后,服務(wù)器需要檢查 Read() 的返回值。如果返回值為 true,流仍然存在,它就可以繼續(xù)讀取;如果返回值為 false,則表明消息流已經(jīng)停止。

while (stream->Read(&point)) {
  ...//process client input
}

最后,讓我們看看雙向流RPCRouteChat()。

  Status RouteChat(ServerContext* context,
                   ServerReaderWriter<RouteNote, RouteNote>* stream) override {
    std::vector<RouteNote> received_notes;
    RouteNote note;
    while (stream->Read(&note)) {
      for (const RouteNote& n : received_notes) {
        if (n.location().latitude() == note.location().latitude() &&
            n.location().longitude() == note.location().longitude()) {
          stream->Write(n);
        }
      }
      received_notes.push_back(note);
    }

    return Status::OK;
  }

這次我們得到的 ServerReaderWriter 對(duì)象可以用來(lái)讀 寫消息。這里讀寫的語(yǔ)法和我們客戶端流以及服務(wù)器流方法是一樣的。雖然每一端獲取對(duì)方信息的順序和寫入的順序一致,客戶端和服務(wù)器都可以以任意順序讀寫——流的操作是完全獨(dú)立的。

啟動(dòng)服務(wù)器

一旦我們實(shí)現(xiàn)了所有的方法,我們還需要啟動(dòng)一個(gè)gRPC服務(wù)器,這樣客戶端才可以使用服務(wù)。下面這段代碼展示了在我們RouteGuide服務(wù)中實(shí)現(xiàn)的過(guò)程:

void RunServer(const std::string& db_path) {
  std::string server_address("0.0.0.0:50051");
  RouteGuideImpl service(db_path);

  ServerBuilder builder;
  builder.AddListeningPort(server_address, grpc::InsecureServerCredentials());
  builder.RegisterService(&service);
  std::unique_ptr<Server> server(builder.BuildAndStart());
  std::cout << "Server listening on " << server_address << std::endl;
  server->Wait();
}

如你所見(jiàn),我們通過(guò)使用ServerBuilder去構(gòu)建和啟動(dòng)服務(wù)器。為了做到這點(diǎn),我們需要:

  1. 創(chuàng)建我們的服務(wù)實(shí)現(xiàn)類 RouteGuideImpl 的一個(gè)實(shí)例。
  2. 創(chuàng)建工廠類 ServerBuilder 的一個(gè)實(shí)例。
  3. 在生成器的 AddListeningPort() 方法中指定客戶端請(qǐng)求時(shí)監(jiān)聽(tīng)的地址和端口。
  4. 用生成器注冊(cè)我們的服務(wù)實(shí)現(xiàn)。
  5. 調(diào)用生成器的 BuildAndStart() 方法為我們的服務(wù)創(chuàng)建和啟動(dòng)一個(gè)RPC服務(wù)器。
  6. 調(diào)用服務(wù)器的 Wait() 方法實(shí)現(xiàn)阻塞等待,直到進(jìn)程被殺死或者 Shutdown() 被調(diào)用。

創(chuàng)建客戶端

在這部分,我們將嘗試為RouteGuide服務(wù)創(chuàng)建一個(gè)C++的客戶端。你可以從examples/cpp/route_guide/route_guide_client.cc看到我們完整的客戶端例子代碼.

創(chuàng)建一個(gè)存根

為了能調(diào)用服務(wù)的方法,我們得先創(chuàng)建一個(gè) 存根。

首先需要為我們的存根創(chuàng)建一個(gè)gRPC channel,指定我們想連接的服務(wù)器地址和端口,以及 channel 相關(guān)的參數(shù)——在本例中我們使用了缺省的 ChannelArguments 并且沒(méi)有使用SSL:

grpc::CreateChannel("localhost:50051", grpc::InsecureCredentials(), ChannelArguments());

現(xiàn)在我們可以利用channel,使用從.proto中生成的RouteGuide類提供的NewStub方法去創(chuàng)建存根。

 public:
  RouteGuideClient(std::shared_ptr<ChannelInterface> channel,
                   const std::string& db)
      : stub_(RouteGuide::NewStub(channel)) {
    ...
  }

調(diào)用服務(wù)的方法

現(xiàn)在我們來(lái)看看如何調(diào)用服務(wù)的方法。注意,在本教程中調(diào)用的方法,都是 阻塞/同步 的版本:這意味著 RPC 調(diào)用會(huì)等待服務(wù)器響應(yīng),要么返回響應(yīng),要么引起一個(gè)異常。

簡(jiǎn)單RPC

調(diào)用簡(jiǎn)單 RPC GetFeature 幾乎是和調(diào)用一個(gè)本地方法一樣直觀。

  Point point;
  Feature feature;
  point = MakePoint(409146138, -746188906);
  GetOneFeature(point, &feature);

...

  bool GetOneFeature(const Point& point, Feature* feature) {
    ClientContext context;
    Status status = stub_->GetFeature(&context, point, feature);
    ...
  }

如你所見(jiàn),我們創(chuàng)建并且填充了一個(gè)請(qǐng)求的 protocol buffer 對(duì)象(例子中為 Point),同時(shí)為了服務(wù)器填寫創(chuàng)建了一個(gè)響應(yīng) protocol buffer 對(duì)象。為了調(diào)用我們還創(chuàng)建了一個(gè) ClientContext 對(duì)象——你可以隨意的設(shè)置該對(duì)象上的配置的值,比如期限,雖然現(xiàn)在我們會(huì)使用缺省的設(shè)置。注意,你不能在不同的調(diào)用間重復(fù)使用這個(gè)對(duì)象。最后,我們?cè)诖娓险{(diào)用這個(gè)方法,將其傳給上下文,請(qǐng)求以及響應(yīng)。如果方法的返回是OK,那么我們就可以從服務(wù)器從我們的響應(yīng)對(duì)象中讀取響應(yīng)信息。

      std::cout << "Found feature called " << feature->name()  << " at "
                << feature->location().latitude()/kCoordFactor_ << ", "
                << feature->location().longitude()/kCoordFactor_ << std::endl;

流式RPC

現(xiàn)在來(lái)看看我們的流方法。如果你已經(jīng)讀過(guò)創(chuàng)建服務(wù)器,本節(jié)的一些內(nèi)容看上去很熟悉——流式 RPC 是在客戶端和服務(wù)器兩端以一種類似的方式實(shí)現(xiàn)的。下面就是我們稱作是服務(wù)器端的流方法 ListFeatures,它會(huì)返回地理的 Feature

    std::unique_ptr<ClientReader<Feature> > reader(
        stub_->ListFeatures(&context, rect));
    while (reader->Read(&feature)) {
      std::cout << "Found feature called "
                << feature.name() << " at "
                << feature.location().latitude()/kCoordFactor_ << ", "
                << feature.location().longitude()/kCoordFactor_ << std::endl;
    }
    Status status = reader->Finish();

我們將上下文傳給方法并且請(qǐng)求,得到 ClientReader 返回對(duì)象,而不是將上下文,請(qǐng)求和響應(yīng)傳給方法??蛻舳丝梢允褂?ClientReader 去讀取服務(wù)器的響應(yīng)。我們使用 ClientReaderRead() 反復(fù)讀取服務(wù)器的響應(yīng)到一個(gè)響應(yīng) protocol buffer 對(duì)象(在這個(gè)例子中是一個(gè) Feature),直到?jīng)]有更多的消息:客戶端需要去檢查每次調(diào)用完 Read() 方法的返回值。如果返回值為 true,流依然存在并且可以持續(xù)讀取;如果是 false,說(shuō)明消息流已經(jīng)結(jié)束。最后,我們?cè)诹魃险{(diào)用 Finish() 方法結(jié)束調(diào)用并獲取我們 RPC 的狀態(tài)。

客戶端的流方法 RecordRoute 的使用很相似,除了我們將一個(gè)上下文和響應(yīng)對(duì)象傳給方法,拿到一個(gè) ClientWriter 返回。

    std::unique_ptr<ClientWriter<Point> > writer(
        stub_->RecordRoute(&context, &stats));
    for (int i = 0; i < kPoints; i++) {
      const Feature& f = feature_list_[feature_distribution(generator)];
      std::cout << "Visiting point "
                << f.location().latitude()/kCoordFactor_ << ", "
                << f.location().longitude()/kCoordFactor_ << std::endl;
      if (!writer->Write(f.location())) {
        // Broken stream.
        break;
      }
      std::this_thread::sleep_for(std::chrono::milliseconds(
          delay_distribution(generator)));
    }
    writer->WritesDone();
    Status status = writer->Finish();
    if (status.IsOk()) {
      std::cout << "Finished trip with " << stats.point_count() << " points\n"
                << "Passed " << stats.feature_count() << " features\n"
                << "Travelled " << stats.distance() << " meters\n"
                << "It took " << stats.elapsed_time() << " seconds"
                << std::endl;
    } else {
      std::cout << "RecordRoute rpc failed." << std::endl;
    }

一旦我們用 Write() 將客戶端請(qǐng)求寫入到流的動(dòng)作完成,我們需要在流上調(diào)用 WritesDone() 通知 gRPC 我們已經(jīng)完成寫入,然后調(diào)用 Finish() 完成調(diào)用同時(shí)拿到 RPC 的狀態(tài)。如果狀態(tài)是 OK,我們最初傳給 RecordRoute() 的響應(yīng)對(duì)象會(huì)跟著服務(wù)器的響應(yīng)被填充。

最后,讓我們看看雙向流式 RPC RouteChat()。在這種場(chǎng)景下,我們將上下文傳給一個(gè)方法,拿到一個(gè)可以用來(lái)讀寫消息的ClientReaderWriter的返回。

    std::shared_ptr<ClientReaderWriter<RouteNote, RouteNote> > stream(
        stub_->RouteChat(&context));

這里讀寫的語(yǔ)法和我們客戶端流以及服務(wù)器端流方法沒(méi)有任何區(qū)別。雖然每一方都能按照寫入時(shí)的順序拿到另一方的消息,客戶端和服務(wù)器端都可以以任意順序讀寫——流操作起來(lái)是完全獨(dú)立的。

來(lái)試試吧!

構(gòu)建客戶端和服務(wù)器:

$ make

運(yùn)行服務(wù)器,它會(huì)監(jiān)聽(tīng)50051端口:

$ ./route_guide_server

在另外一個(gè)終端運(yùn)行客戶端:

$ ./route_guide_client

    本站是提供個(gè)人知識(shí)管理的網(wǎng)絡(luò)存儲(chǔ)空間,所有內(nèi)容均由用戶發(fā)布,不代表本站觀點(diǎn)。請(qǐng)注意甄別內(nèi)容中的聯(lián)系方式、誘導(dǎo)購(gòu)買等信息,謹(jǐn)防詐騙。如發(fā)現(xiàn)有害或侵權(quán)內(nèi)容,請(qǐng)點(diǎn)擊一鍵舉報(bào)。
    轉(zhuǎn)藏 分享 獻(xiàn)花(0

    0條評(píng)論

    發(fā)表

    請(qǐng)遵守用戶 評(píng)論公約

    類似文章 更多

    亚洲日本中文字幕视频在线观看| 内射精子视频欧美一区二区| 99久久成人精品国产免费| 亚洲第一香蕉视频在线| 樱井知香黑人一区二区| 日本中文字幕在线精品| 亚洲中文字幕高清视频在线观看| 午夜福利黄片免费观看| 日韩精品视频一二三区| 麻豆蜜桃星空传媒在线观看| 中文字幕乱子论一区二区三区| 草草草草在线观看视频| 台湾综合熟女一区二区| 国产女高清在线看免费观看| 免费在线观看激情小视频| 99视频精品免费视频| 国产主播精品福利午夜二区| 日韩黄色大片免费在线| 成人国产激情在线视频| 国产精品一区二区三区日韩av| 亚洲乱妇熟女爽的高潮片| 国产日产欧美精品大秀| 1024你懂的在线视频| 高清欧美大片免费在线观看| 日本一区二区三区黄色| 国产午夜精品美女露脸视频| 福利在线午夜绝顶三级| 中国一区二区三区不卡| 成人区人妻精品一区二区三区| 国产一级内片内射免费看| 国产熟女高清一区二区| 亚洲精品国产福利在线| 精品一区二区三区三级视频 | 国产午夜福利在线免费观看| 亚洲精品av少妇在线观看| 精品老司机视频在线观看| 九九热精彩视频在线免费| 婷婷九月在线中文字幕| 91亚洲国产成人久久| 欧美日韩精品久久第一页| 又黄又爽禁片视频在线观看|