精品欧美一区二区三区在线观看 _久久久久国色av免费观看性色_国产精品久久在线观看_亚洲第一综合网站_91精品又粗又猛又爽_小泽玛利亚一区二区免费_91亚洲精品国偷拍自产在线观看 _久久精品视频在线播放_美女精品久久久_欧美日韩国产成人在线

您可知道如何通過HTTP2實現TCP的內網穿透?

開發 前端
由于我們的客戶端有些特殊,再server中部署的它不需要監聽端口,它只需要將服務器的數據轉發到指定的一個地址即可,所以我們需要將客戶端的server部署的和本地部署的分開實現,再服務器部署的客戶端我們命名為MonitorClient.cs。

可能有人很疑惑應用層 轉發傳輸層?,為什么會有這樣的需求啊???哈哈技術無所不用其極,由于一些場景下,對于一個服務器存在某一個內部網站中,但是對于這個服務器它沒有訪問外網的權限,雖然也可以申請端口訪問外部指定的ip+端口,但是對于訪問服務內部的TCP的時候我們就會發現忘記申請了!這個時候我們又要提交申請,又要等審批,然后開通端口,對于這個步驟不是一般的麻煩,所以我在想是否可以直接利用現有的Http網關的端口進行轉發內部的TCP服務?這個時候我詢問了我們的老九大佬,由于我之前也做過通過H2實現HTTP內網穿透,可以利用H2將內部網絡中的服務映射出來,但是由于底層是基于yarp的一些方法實現,所以并沒有考慮過TCP,然后于老九大佬交流深究,決定嘗試驗證可行性,然后我們的Taibai項目就誕生了,為什么叫Taibai?您仔細看看這個拼音,翻譯過來就是太白,確實全稱應該叫太白金星,寓意上天遁地無所不能!下面我們介紹一下具體實現邏輯,確實您仔細看會發現實現是真的超級簡單的!

創建Core項目用于共用的核心類庫

創建項目名Taibai.Core

下面幾個方法都是用于操作Stream的類

DelegatingStream.cs

namespace Taibai.Core;

/// <summary>
/// 委托流
/// </summary>
public abstract class DelegatingStream : Stream
{
    /// <summary>
    /// 獲取所包裝的流對象
    /// </summary>
    protected readonly Stream Inner;

    /// <summary>
    /// 委托流
    /// </summary>
    /// <param name="inner"></param>
    public DelegatingStream(Stream inner)
    {
        this.Inner = inner;
    }

    /// <inheritdoc/>
    public override bool CanRead => Inner.CanRead;

    /// <inheritdoc/>
    public override bool CanSeek => Inner.CanSeek;

    /// <inheritdoc/>
    public override bool CanWrite => Inner.CanWrite;

    /// <inheritdoc/>
    public override long Length => Inner.Length;

    /// <inheritdoc/>
    public override bool CanTimeout => Inner.CanTimeout;

    /// <inheritdoc/>
    public override int ReadTimeout
    {
        get => Inner.ReadTimeout;
        set => Inner.ReadTimeout = value;
    }

    /// <inheritdoc/>
    public override int WriteTimeout
    {
        get => Inner.WriteTimeout;
        set => Inner.WriteTimeout = value;
    }


    /// <inheritdoc/>
    public override long Position
    {
        get => Inner.Position;
        set => Inner.Position = value;
    }

    /// <inheritdoc/>
    public override void Flush()
    {
        Inner.Flush();
    }

    /// <inheritdoc/>
    public override Task FlushAsync(CancellationToken cancellationToken)
    {
        return Inner.FlushAsync(cancellationToken);
    }

    /// <inheritdoc/>
    public override int Read(byte[] buffer, int offset, int count)
    {
        return Inner.Read(buffer, offset, count);
    }

    /// <inheritdoc/>
    public override int Read(Span<byte> destination)
    {
        return Inner.Read(destination);
    }

    /// <inheritdoc/>
    public override Task<int> ReadAsync(byte[] buffer, int offset, int count, CancellationToken cancellationToken)
    {
        return Inner.ReadAsync(buffer, offset, count, cancellationToken);
    }

    /// <inheritdoc/>
    public override ValueTask<int> ReadAsync(Memory<byte> destination, CancellationToken cancellationToken = default)
    {
        return Inner.ReadAsync(destination, cancellationToken);
    }

    /// <inheritdoc/>
    public override long Seek(long offset, SeekOrigin origin)
    {
        return Inner.Seek(offset, origin);
    }

    /// <inheritdoc/>
    public override void SetLength(long value)
    {
        Inner.SetLength(value);
    }

    /// <inheritdoc/>
    public override void Write(byte[] buffer, int offset, int count)
    {
        Inner.Write(buffer, offset, count);
    }

    /// <inheritdoc/>
    public override void Write(ReadOnlySpan<byte> source)
    {
        Inner.Write(source);
    }

    /// <inheritdoc/>
    public override Task WriteAsync(byte[] buffer, int offset, int count, CancellationToken cancellationToken)
    {
        return Inner.WriteAsync(buffer, offset, count, cancellationToken);
    }

    /// <inheritdoc/>
    public override ValueTask WriteAsync(ReadOnlyMemory<byte> source, CancellationToken cancellationToken = default)
    {
        return Inner.WriteAsync(source, cancellationToken);
    }

    /// <inheritdoc/>
    public override IAsyncResult BeginRead(byte[] buffer, int offset, int count, AsyncCallback? callback, object? state)
    {
        return TaskToAsyncResult.Begin(ReadAsync(buffer, offset, count), callback, state);
    }

    /// <inheritdoc/>
    public override int EndRead(IAsyncResult asyncResult)
    {
        return TaskToAsyncResult.End<int>(asyncResult);
    }

    /// <inheritdoc/>
    public override IAsyncResult BeginWrite(byte[] buffer, int offset, int count, AsyncCallback? callback,
        object? state)
    {
        return TaskToAsyncResult.Begin(WriteAsync(buffer, offset, count), callback, state);
    }

    /// <inheritdoc/>
    public override void EndWrite(IAsyncResult asyncResult)
    {
        TaskToAsyncResult.End(asyncResult);
    }

    /// <inheritdoc/>
    public override int ReadByte()
    {
        return Inner.ReadByte();
    }

    /// <inheritdoc/>
    public override void WriteByte(byte value)
    {
        Inner.WriteByte(value);
    }

    /// <inheritdoc/>
    public sealed override void Close()
    {
        base.Close();
    }
}

SafeWriteStream.cs

public class SafeWriteStream(Stream inner) : DelegatingStream(inner)
{
    private readonly SemaphoreSlim semaphoreSlim = new(1, 1);

    public override async ValueTask WriteAsync(ReadOnlyMemory<byte> source, CancellationToken cancellationToken = default)
    {
        try
        {
            await this.semaphoreSlim.WaitAsync(CancellationToken.None);
            await base.WriteAsync(source, cancellationToken);
            await this.FlushAsync(cancellationToken);
        }
        finally
        {
            this.semaphoreSlim.Release();
        }
    }

    public override ValueTask DisposeAsync()
    {
        this.semaphoreSlim.Dispose();
        return this.Inner.DisposeAsync();
    }

    protected override void Dispose(bool disposing)
    {
        this.semaphoreSlim.Dispose();
        this.Inner.Dispose();
    }
}

創建服務端

創建一個WebAPI的項目項目名Taibai.Server并且依賴Taibai.Core項目

創建ServerService.cs,這個類是用于管理內網的客戶端的,這個一般是部署在內網服務器上,用于將內網的端口映射出來,但是我們的Demo只實現了簡單的管理不做端口的管理。

using System.Collections.Concurrent;
using Microsoft.AspNetCore.Http.Features;
using Microsoft.AspNetCore.Http.Timeouts;
using Taibai.Core;

namespace Taibai.Server;

public static class ServerService
{
    private static readonly ConcurrentDictionary<string, (CancellationToken, Stream)> ClusterConnections = new();

    public static async Task StartAsync(HttpContext context)
    {
        // 如果不是http2協議,我們不處理, 因為我們只支持http2
        if (context.Request.Protocol != HttpProtocol.Http2)
        {
            return;
        }

        // 獲取query
        var query = context.Request.Query;

        // 我們需要強制要求name參數
        var name = query["name"];

        if (string.IsNullOrEmpty(name))
        {
            context.Response.StatusCode = 400;
            Console.WriteLine("Name is required");
            return;
        }
        
        Console.WriteLine("Accepted connection from " + name);

        // 獲取http2特性
        var http2Feature = context.Features.Get<IHttpExtendedConnectFeature>();
        
        // 禁用超時
        context.Features.Get<IHttpRequestTimeoutFeature>()?.DisableTimeout();

        // 得到雙工流
        var stream = new SafeWriteStream(await http2Feature.AcceptAsync());

        // 將其添加到集合中,以便我們可以在其他地方使用
        CreateConnectionChannel(name, context.RequestAborted, stream);

        // 注冊取消連接
        context.RequestAborted.Register(() =>
        {
            // 當取消時,我們需要從集合中刪除
            ClusterConnections.TryRemove(name, out _);
        });
        
        // 由于我們需要保持連接,所以我們需要等待,直到客戶端主動斷開連接。
        await Task.Delay(-1, context.RequestAborted);
    }

    /// <summary>
    /// 通過名稱獲取連接
    /// </summary>
    /// <param name="host"></param>
    /// <returns></returns>
    public static (CancellationToken, Stream) GetConnectionChannel(string host)
    {
        return ClusterConnections[host];
    }

    /// <summary>
    /// 注冊連接
    /// </summary>
    /// <param name="host"></param>
    /// <param name="cancellationToken"></param>
    /// <param name="stream"></param>
    public static void CreateConnectionChannel(string host, CancellationToken cancellationToken, Stream stream)
    {
        ClusterConnections.GetOrAdd(host,
            _ => (cancellationToken, stream));
    }
}

然后再創建ClientMiddleware.cs,并且繼承IMiddleware,這個是我們本地使用的客戶端鏈接的時候進入的中間件,再這個中間件會獲取query中攜帶的name去找到指定的Stream,然后會將客戶端的Stream和獲取的server的Stream進行Copy,在這里他們會將讀取的數據寫入到對方的流中,這樣就實現了雙工通信

using Microsoft.AspNetCore.Http.Features;
using Microsoft.AspNetCore.Http.Timeouts;
using Taibai.Core;

namespace Taibai.Server;

public class ClientMiddleware : IMiddleware
{
    public async Task InvokeAsync(HttpContext context, RequestDelegate next)
    {
        
        // 如果不是http2協議,我們不處理, 因為我們只支持http2
        if (context.Request.Protocol != HttpProtocol.Http2)
        {
            return;
        }

        var name = context.Request.Query["name"];

        if (string.IsNullOrEmpty(name))
        {
            context.Response.StatusCode = 400;
            Console.WriteLine("Name is required");
            return;
        }
        
        Console.WriteLine("Accepted connection from " + name);

        var http2Feature = context.Features.Get<IHttpExtendedConnectFeature>();
        context.Features.Get<IHttpRequestTimeoutFeature>()?.DisableTimeout();

        // 得到雙工流
        var stream = new SafeWriteStream(await http2Feature.AcceptAsync());

        // 通過name找到指定的server鏈接,然后進行轉發。
        var (cancellationToken, reader) = ServerService.GetConnectionChannel(name);

        try
        {
            // 注冊取消連接
            cancellationToken.Register(() =>
            {
                Console.WriteLine("斷開連接");
                stream.Close();
            });

            // 得到客戶端的流,然后給我們的SafeWriteStream,然后我們就可以進行轉發了
            var socketStream = new SafeWriteStream(reader);

            // 在這里他們會將讀取的數據寫入到對方的流中,這樣就實現了雙工通信,這個非常簡單并且性能也不錯。
            await Task.WhenAll(
                stream.CopyToAsync(socketStream, context.RequestAborted),
                socketStream.CopyToAsync(stream, context.RequestAborted)
            );
        }
        catch (Exception e)
        {
            Console.WriteLine("斷開連接" + e.Message);
            throw;
        }
    }
}

打開Program.cs

using Taibai.Server;

var builder = WebApplication.CreateBuilder(new WebApplicationOptions());

builder.Host.ConfigureHostOptions(host => { host.ShutdownTimeout = TimeSpan.FromSeconds(1d); });

builder.Services.AddSingleton<ClientMiddleware>();

var app = builder.Build();

app.Map("/server", app =>
{
    app.Use(Middleware);

    static async Task Middleware(HttpContext context, RequestDelegate _)
    {
        await ServerService.StartAsync(context);
    }
});

app.Map("/client", app => { app.UseMiddleware<ClientMiddleware>(); });

app.Run();

在這里我們將server的所有路由都交過ServerService.StartAsync接管,再server會請求這個地址,

而/client則給了ClientMiddleware中間件。

創建客戶端

上面我們實現了服務端,其實服務端可以完全放置到現有的WebApi項目當中的,而且代碼也不是很多。

客戶端我們創建一個控制臺項目名:Taibai.Client,并且依賴Taibai.Core項目

由于我們的客戶端有些特殊,再server中部署的它不需要監聽端口,它只需要將服務器的數據轉發到指定的一個地址即可,所以我們需要將客戶端的server部署的和本地部署的分開實現,再服務器部署的客戶端我們命名為MonitorClient.cs

ClientOption.cs用于傳遞我們的客戶端地址配置

public class ClientOption
{
    /// <summary>
    /// 服務地址
    /// </summary>
    public string ServiceUri { get; set; }
    
}

MonitorClient.cs,作為服務器的轉發客戶端。

using System.Net;
using System.Net.Security;
using System.Net.Sockets;
using Taibai.Core;

namespace Taibai.Client;

public class MonitorClient(ClientOption option)
{
    private string Protocol = "taibai";
    private readonly HttpMessageInvoker httpClient = new(CreateDefaultHttpHandler(), true);
    private readonly Socket socket = new(SocketType.Stream, ProtocolType.Tcp);

    private static SocketsHttpHandler CreateDefaultHttpHandler()
    {
        return new SocketsHttpHandler
        {
            // 允許多個http2連接
            EnableMultipleHttp2Connections = true,
            // 設置連接超時時間
            ConnectTimeout = TimeSpan.FromSeconds(60),
            SslOptions = new SslClientAuthenticationOptions
            {
                // 由于我們沒有證書,所以我們需要設置為true
                RemoteCertificateValidationCallback = (_, _, _, _) => true,
            },
        };
    }

    public async Task TransportAsync(CancellationToken cancellationToken)
    {
        Console.WriteLine("鏈接中!");

        // 由于是測試,我們就目前先寫死遠程地址
        await socket.ConnectAsync(new IPEndPoint(IPAddress.Parse("192.168.31.250"), 3389), cancellationToken);

        Console.WriteLine("連接成功");

        // 將Socket轉換為流
        var stream = new NetworkStream(socket);
        try
        {
            // 創建服務器的連接,然后返回一個流,這個是H2的流
            var serverStream = await this.CreateServerConnectionAsync(cancellationToken);

            Console.WriteLine("鏈接服務器成功");

            // 將兩個流連接起來,這樣我們就可以進行雙工通信了。它們會自動進行數據的傳輸。
            await Task.WhenAll(
                stream.CopyToAsync(serverStream, cancellationToken),
                serverStream.CopyToAsync(stream, cancellationToken)
            );
        }
        catch (Exception ex)
        {
            Console.WriteLine(ex.Message);
            throw;
        }
    }

    /// <summary>
    /// 創建服務器的連接
    /// </summary> 
    /// <param name="cancellationToken"></param>
    /// <exception cref="OperationCanceledException"></exception>
    /// <returns></returns>
    public async Task<SafeWriteStream> CreateServerConnectionAsync(CancellationToken cancellationToken)
    {
        var stream = await Http20ConnectServerAsync(cancellationToken);
        return new SafeWriteStream(stream);
    }

    /// <summary>
    /// 創建http2連接
    /// </summary>
    /// <param name="cancellationToken"></param>
    /// <returns></returns>
    private async Task<Stream> Http20ConnectServerAsync(CancellationToken cancellationToken)
    {
        var serverUri = new Uri(option.ServiceUri);
        // 這里我們使用Connect方法,因為我們需要建立一個雙工流, 這樣我們就可以進行雙工通信了。
        var request = new HttpRequestMessage(HttpMethod.Connect, serverUri);
        // 如果設置了Connect,那么我們需要設置Protocol
        request.Headers.Protocol = Protocol;
        // 我們需要設置http2的版本
        request.Version = HttpVersion.Version20;
        
        // 我們需要確保我們的請求是http2的
        request.VersionPolicy = HttpVersionPolicy.RequestVersionExact;

        // 設置一下超時時間,這樣我們就可以在超時的時候取消連接了。
        using var timeoutTokenSource = new CancellationTokenSource(TimeSpan.FromSeconds(60));
        using var linkedTokenSource =
            CancellationTokenSource.CreateLinkedTokenSource(timeoutTokenSource.Token, cancellationToken);

        // 發送請求,然后等待響應
        var httpResponse = await this.httpClient.SendAsync(request, linkedTokenSource.Token);

        // 返回h2的流,用于傳輸數據
        return await httpResponse.Content.ReadAsStreamAsync(linkedTokenSource.Token);
    }
}

創建我們的本地客戶端實現類。

Client.cs這個就是在我們本地部署的服務,然后會監聽本地的60112的端口,然后會吧這個端口的數據轉發到我們的服務器,然后服務器會根據我們使用的name去找到指定的客戶端進行交互傳輸。

using System.Net;
using System.Net.Security;
using System.Net.Sockets;
using Taibai.Core;
using HttpMethod = System.Net.Http.HttpMethod;

namespace Taibai.Client;


public class Client
{
    private readonly ClientOption option;

    private string Protocol = "taibai";
    private readonly HttpMessageInvoker httpClient;
    private readonly Socket socket;

    public Client(ClientOption option)
    {
        this.option = option;
        this.httpClient = new HttpMessageInvoker(CreateDefaultHttpHandler(), true);

        this.socket = new Socket(SocketType.Stream, ProtocolType.Tcp);

        // 監聽本地端口
        this.socket.Bind(new IPEndPoint(IPAddress.Loopback, 60112));
        this.socket.Listen(10);
    }

    private static SocketsHttpHandler CreateDefaultHttpHandler()
    {
        return new SocketsHttpHandler
        {
            // 允許多個http2連接
            EnableMultipleHttp2Connections = true,
            ConnectTimeout = TimeSpan.FromSeconds(60),
            ResponseDrainTimeout = TimeSpan.FromSeconds(60),  
            SslOptions = new SslClientAuthenticationOptions
            {
                // 由于我們沒有證書,所以我們需要設置為true
                RemoteCertificateValidationCallback = (sender, certificate, chain, sslPolicyErrors) => true,
            },
        };
    }

    public async Task TransportAsync(CancellationToken cancellationToken)
    {
        Console.WriteLine("Listening on 60112");

        // 等待客戶端連接
        var client = await this.socket.AcceptAsync(cancellationToken);

        Console.WriteLine("Accepted connection from " + client.RemoteEndPoint);

        try
        {
            // 將Socket轉換為流
            var stream = new NetworkStream(client);

            // 創建服務器的連接,然后返回一個流, 這個是H2的流
            var serverStream = await this.CreateServerConnectionAsync(cancellationToken);

            Console.WriteLine("Connected to server");

            // 將兩個流連接起來, 這樣我們就可以進行雙工通信了. 它們會自動進行數據的傳輸.
            await Task.WhenAll(
                stream.CopyToAsync(serverStream, cancellationToken),
                serverStream.CopyToAsync(stream, cancellationToken)
            );
        }
        catch (Exception e)
        {
            Console.WriteLine(e);
            throw;
        }
    }

    /// <summary>
    /// 創建與服務器的連接
    /// </summary> 
    /// <param name="cancellationToken"></param>
    /// <exception cref="OperationCanceledException"></exception>
    /// <returns></returns>
    public async Task<SafeWriteStream> CreateServerConnectionAsync(CancellationToken cancellationToken)
    {
        var stream = await this.Http20ConnectServerAsync(cancellationToken);
        return new SafeWriteStream(stream);
    }

    private async Task<Stream> Http20ConnectServerAsync(CancellationToken cancellationToken)
    {
        var serverUri = new Uri(option.ServiceUri);
        // 這里我們使用Connect方法, 因為我們需要建立一個雙工流
        var request = new HttpRequestMessage(HttpMethod.Connect, serverUri);

        // 由于我們設置了Connect方法, 所以我們需要設置協議,這樣服務器才能識別
        request.Headers.Protocol = Protocol;
        // 設置http2版本
        request.Version = HttpVersion.Version20;
        // 強制使用http2
        request.VersionPolicy = HttpVersionPolicy.RequestVersionExact;

        using var timeoutTokenSource = new CancellationTokenSource(TimeSpan.FromSeconds(60));
        using var linkedTokenSource =
            CancellationTokenSource.CreateLinkedTokenSource(timeoutTokenSource.Token, cancellationToken);

        // 發送請求,等待服務器驗證。
        var httpResponse = await this.httpClient.SendAsync(request, linkedTokenSource.Token);

        // 返回一個流
        return await httpResponse.Content.ReadAsStreamAsync(linkedTokenSource.Token);
    }
}

然后再Program.cs中,我們封裝一個簡單的控制臺版本。

using Taibai.Client;

const string commandTemplate = @"

當前是 Taibai 客戶端,輸入以下命令:

- `help` 顯示幫助
- `monitor` 使用監控模式,監聽本地端口,將流量轉發到服務端的指定地址
    - `monitor=https://localhost:7153/server?name=test`  監聽本地端口,將流量轉發到服務端指定的客戶端名稱為 test 的地址
- `client` 使用客戶端模式,連接服務端的指定地址,將流量轉發到本地端口
    - `client=https://localhost:7153/client?name=test`  連接服務端指定當前客戶端名稱為 test,將流量轉發到本地端口
- `exit` 退出

輸入命令:

";

while (true)
{
    Console.WriteLine(commandTemplate);

    var command = Console.ReadLine();


    if (command?.StartsWith("monitor=") == true)
    {
        var client = new MonitorClient(new ClientOption()
        {
            ServiceUri = command[8..]
        });

        await client.TransportAsync(new CancellationToken());
    }
    else if (command?.StartsWith("client=") == true)
    {
        var client = new Client(new ClientOption()
        {
            ServiceUri = command[7..]
        });

        await client.TransportAsync(new CancellationToken());
    }
    else if (command == "help")
    {
        Console.WriteLine(commandTemplate);
    }
    else if (command == "exit")
    {
        Console.WriteLine("Bye!");
        break;
    }
    else
    {
        Console.WriteLine("未知命令");
    }
}

我們默認提供了命令去使用指定的一個模式去鏈接客戶端,

然后我們發布一下Taibai.Client,發布完成以后我們使用ide啟動我們的Taibai.Server,請注意我們需要使用HTTPS進行啟動的,HTTP是不支持H2的!

然后再客戶端中打開倆個控制臺面板,一個作為監聽的monitor,一個作為client進行鏈接到我們的服務器中。

圖片圖片

圖片圖片

然后我們使用遠程桌面訪問我們的127.0.0.1:60112,然后我們發現鏈接成功!如果您跟著寫代碼您會您發您也成功了,哦耶您獲得了一個牛逼的技能,來源于微軟MVP token的雙休大法的傳授!

圖片 圖片

責任編輯:武曉燕 來源: token的技術分享
相關推薦

2015-11-24 15:22:53

HTTP2 WEB 內網穿透

2016-10-21 10:36:54

http2spdynode.js

2017-09-22 10:53:52

HTTPHTTP2TCP協議

2019-10-15 08:00:00

HTTP2HTTP前端

2020-10-13 14:03:50

搭建ngrok服務

2018-11-14 15:00:08

HTTP程序員前端

2023-10-11 18:30:38

2018-12-18 10:07:41

Spring Boot服務器HTTP2

2020-09-03 08:03:52

內網穿透

2011-02-23 10:32:16

網頁設計Web

2024-11-25 16:25:23

內網穿透網絡協議

2024-10-12 20:56:19

ProxyChain

2025-02-25 10:56:32

內網穿透開源桌面應用程序

2011-04-18 08:31:54

2019-06-12 09:02:20

2015-08-13 10:31:18

Java 9新功能

2010-04-19 14:44:50

無線路由連接設置

2022-09-19 13:11:56

命令SSH內網穿透

2019-11-11 08:45:52

HTTPTCP數據

2019-02-28 08:44:19

內網釘釘Web
點贊
收藏

51CTO技術棧公眾號

国产日韩欧美综合精品| 欧美风情在线观看| 亚洲精品成人在线播放| 欧美极品少妇videossex| 成人精品一区二区三区四区| 久久久久女教师免费一区| 中文字幕一区二区人妻在线不卡| 久久天天久久| 偷拍日韩校园综合在线| 亚洲精品在线观看免费| 韩国av电影在线观看| 日本少妇一区二区| 国模精品视频一区二区三区| 黄色激情小视频| 噜噜噜天天躁狠狠躁夜夜精品| 欧美婷婷六月丁香综合色| 草草视频在线免费观看| 欧美日韩视频在线播放| 97成人超碰视| 91九色极品视频| 中文字幕乱伦视频| 国产午夜精品一区二区三区欧美| 自拍偷拍亚洲区| 亚洲精品理论片| www.豆豆成人网.com| 欧美日韩大陆一区二区| 日日橹狠狠爱欧美超碰| 美女精品导航| 亚洲欧美日韩电影| 亚洲 国产 日韩 综合一区| 亚洲av成人精品一区二区三区在线播放 | 极品美女销魂一区二区三区| 日本亚洲欧美成人| 国产精品二区一区二区aⅴ| 五月天久久777| 综合网中文字幕| 国产精品无码一区二区三区免费| 超碰97久久国产精品牛牛| 91精品国产入口| 欧美国产日韩另类 | 国产一区免费| 丰满人妻一区二区三区无码av| 国产在线一区观看| 国产日韩换脸av一区在线观看| 在线免费观看av网址| 亚洲一区二区三区免费在线观看| 欧美极品少妇xxxxⅹ喷水| 久久久不卡网国产精品一区| 欧洲高清一区二区| 婷婷婷国产在线视频| fc2成人免费人成在线观看播放| 91高跟黑色丝袜呻吟在线观看| 国产强伦人妻毛片| 国产一区二区在线视频| **亚洲第一综合导航网站| 99国产精品欲| 国产成人精品免费| 国产精品v欧美精品v日韩| 黄色av小说在线观看| 波多野结衣在线一区| 国产主播一区二区三区四区| 天堂网av在线播放| 久久欧美中文字幕| 欧美一级二级三级九九九| 国产小视频免费在线网址| 国产亚洲精品aa| 亚洲精品久久区二区三区蜜桃臀| 91社区在线观看| 中文字幕在线观看一区二区| 亚洲精品偷拍视频| 久久五月精品中文字幕| 狠狠躁夜夜躁人人爽天天天天97| 日本一极黄色片| 久久亚洲人体| 日韩免费视频一区| 黄色免费看视频| 亚洲a级精品| 色噜噜亚洲精品中文字幕| 人妻少妇精品一区二区三区| 一区二区三区四区五区在线 | 精品久久对白| 国产亚洲一区精品| 午夜精品福利在线视频| 亚洲一级黄色| 国产精品久久99久久| av中文字幕免费在线观看| 成人sese在线| 污视频在线免费观看一区二区三区 | 久草在线综合| 在线亚洲国产精品网| 欧美性猛交xxxxx少妇| 亚洲欧美日韩国产一区| 国产剧情久久久久久| 亚洲AV无码精品国产| 久久久久亚洲综合| 香蕉视频免费版| 韩日精品一区二区| 日韩欧美国产不卡| 国产高潮呻吟久久| 欧美国产先锋| 国产精品久久久久久五月尺| 国产视频手机在线播放| 国产精品久久久久久吹潮| 精品黑人一区二区三区久久| 山东少妇露脸刺激对白在线| 亚洲性感美女99在线| 国产精品入口日韩视频大尺度| а√天堂资源在线| 欧美国产1区2区| 久色视频在线播放| 麻豆国产一区二区三区四区| 亚洲新中文字幕| 豆国产97在线 | 亚洲| 久久99国产乱子伦精品免费| 欧美日韩精品一区| av日韩国产| 欧美日韩夫妻久久| 欧美熟妇激情一区二区三区| 亚洲精品四区| 999视频在线观看| 日本中文字幕伦在线观看| 懂色av一区二区三区| 少妇性l交大片7724com| 91麻豆国产自产在线观看亚洲| 日韩免费在线视频| 欧洲亚洲精品视频| 黄色一区二区在线| 99热超碰在线| 欧美大片专区| 亚洲一区久久久| 黄色网在线免费观看| 欧美色偷偷大香| 中文字幕在线看高清电影| 悠悠资源网久久精品| 99电影网电视剧在线观看| 日本亚洲精品| 欧美日韩不卡在线| 欧美xxxx精品| 男人的j进女人的j一区| 视频一区二区三区免费观看| 婷婷午夜社区一区| 亚洲精品在线不卡| 亚洲影院在线播放| 久久久精品人体av艺术| 人妻熟女一二三区夜夜爱| 另类尿喷潮videofree| 国产综合在线视频| 特黄aaaaaaaaa真人毛片| 亚洲国产三级在线| 黄色免费看视频| 欧美中文日韩| 欧美激情第一页在线观看| 美女福利一区二区| 国产亚洲欧洲高清| 一本一道精品欧美中文字幕| 中文字幕中文乱码欧美一区二区 | 高清电影一区| 亚洲天堂av网| 中日精品一色哟哟| 亚洲欧美综合色| 亚洲色图欧美自拍| 综合国产精品| 极品日韩久久| 女生影院久久| 最近日韩中文字幕中文| 国产精品久久久久久免费| 亚洲美女视频在线| 插我舔内射18免费视频| 免费永久网站黄欧美| 日韩av大全| 亚洲午夜剧场| 久久免费视频观看| 欧美大片aaa| 欧美日韩中文一区| 中文字幕av免费在线观看| 成人丝袜视频网| 国产综合免费视频| 国产精品久久久久久影院8一贰佰| 亚洲精品女av网站| 天堂中文最新版在线中文| 国产一区二区久久精品| av中文字幕免费| 欧美午夜精品久久久久久人妖| 超薄肉色丝袜一二三| 国产美女精品人人做人人爽| 欧美一级欧美一级| 精品国产一区二区三区小蝌蚪 | 国产精品无码av无码| 亚洲男女av一区二区| 国产麻豆乱码精品一区二区三区| 欧美影视资讯| 色综合视频一区中文字幕| 免费人成在线观看网站| 欧美一区二区三区人| 69视频免费在线观看| 亚洲视频一区二区免费在线观看| 午夜一区二区三区免费| 美国一区二区三区在线播放| 国产va亚洲va在线va| 99国产**精品****| 久久涩涩网站| 欧美2区3区4区| 国产激情综合五月久久| 电影k8一区二区三区久久| 少妇精69xxtheporn| 免费看日韩av| 91精品国产欧美一区二区成人| 日韩欧美成人一区二区三区| 亚洲视频 欧洲视频| 亚洲色成人网站www永久四虎| 成人性生交大片免费看视频在线| 第四色婷婷基地| 亚洲欧美日韩精品一区二区| 国产精品免费看久久久无码| 91视频一区| 色噜噜一区二区| 亚洲丝袜美腿一区| 国产精品12| 亚洲精品一二三**| 91综合免费在线| www.一区| 国产精品jvid在线观看蜜臀| av免费不卡国产观看| 欧美激情精品久久久久| 黄色在线视频网站| 深夜福利91大全| 成人网视频在线观看| 亚洲欧美国产制服动漫| 天天操天天爱天天干| 欧美大肚乱孕交hd孕妇| a天堂在线观看视频| 欧美伦理视频网站| 国产一区二区视频免费观看| 欧美日韩五月天| 中日韩av在线| 欧美日韩国产美| 一区二区精品视频在线观看| 欧美三级韩国三级日本一级| 久久永久免费视频| 欧洲av一区二区嗯嗯嗯啊| 久久久精品毛片| 色综合天天综合网天天狠天天 | 国语对白在线刺激| 欧美成人免费全部| 亚洲无线看天堂av| 欧美国产乱视频| 国产探花在线观看| 久久久噜噜噜久久久| brazzers在线观看| 91高清在线免费观看| 在线成人av观看| 日本视频久久久| 国产在线|日韩| 国产精品青草久久久久福利99| 福利精品在线| 亚洲一区国产精品| 国产精品毛片视频| 蜜桃传媒视频麻豆一区| 国产亚洲一卡2卡3卡4卡新区| 日韩欧美手机在线| 999国产精品| 国产成人一二三区| 亚洲久久在线| 免费男同深夜夜行网站| 久久国产精品无码网站| 日本少妇一区二区三区| 99精品在线观看视频| 深爱五月激情网| 中文字幕人成不卡一区| 久久久久久国产精品免费播放| 精品欧美激情精品一区| av首页在线观看| 在线播放日韩导航| 深爱五月激情五月| 在线观看亚洲区| 日韩激情av| 国产ts一区二区| 中文幕av一区二区三区佐山爱| 国产传媒一区二区三区| 精品国产中文字幕第一页| 在线观看视频黄色| 国产一区二区三区的电影 | 综合久久给合久久狠狠狠97色| 精品少妇久久久| 色婷婷综合视频在线观看| 国产色片在线观看| 日韩国产高清视频在线| 男人天堂手机在线| 91av视频在线观看| 91精品一区| 欧美一区二区三区电影在线观看| 国产精品传媒精东影业在线| 日韩欧美一区三区| 国产一区二区三区av电影 | 欧美wwwwwww| 不卡视频一二三| 黄色精品视频在线观看| 精品久久久视频| www.久久综合| 国产亚洲在线播放| 国产伦子伦对白在线播放观看| 国产精品丝袜视频| 天天躁日日躁狠狠躁欧美巨大小说| 日韩精品久久一区二区三区| 亚洲美女黄网| 一二三av在线| 国产精品天天摸av网| 可以在线观看av的网站| 日韩亚洲欧美综合| 尤物视频在线免费观看| 国产成人精品一区二区在线| 成人精品动漫一区二区三区| 在线观看欧美一区| 免费在线观看不卡| 中文字幕av网址| 亚洲18色成人| www.日日夜夜| 精品国内亚洲在观看18黄| 国产一区一一区高清不卡| 久久久久成人精品免费播放动漫| 午夜国产一区| 亚洲热在线视频| 国产精品久久久久久久久动漫 | 91大学生片黄在线观看| 日本不卡一二三区黄网| 97超碰在线免费观看| 五月天精品一区二区三区| 高h放荡受浪受bl| 久久高清视频免费| 亚洲精品无播放器在线播放| 亚洲一区三区视频在线观看| 日韩精品高清不卡| 丰满少妇高潮一区二区| 欧美丝袜一区二区| 午夜视频在线免费播放| 亚洲91精品在线观看| jizz性欧美23| 成人免费毛片在线观看| 成人永久aaa| 一区二区三区免费高清视频| 日韩精品专区在线影院重磅| 少妇视频在线| 国产超碰91| 在线亚洲观看| 91中文字幕永久在线| 一本色道久久综合亚洲91| 久草在现在线| 国产日韩亚洲欧美| 999国产精品视频| 亚洲五月激情网| 亚洲资源在线观看| 色网站免费观看| 欧美亚洲日本网站| 精品久久久亚洲| av网站在线不卡| 中文字幕日韩av资源站| 99热这里只有精品3| 久久久久国色av免费观看性色| jizzjizzjizz欧美| 欧美一级黄色片视频| 国产欧美日韩卡一| 国产女人高潮毛片| 欧美激情性做爰免费视频| 日本在线中文字幕一区| 久久精品免费一区二区| 国产精品成人一区二区三区夜夜夜 | 在线三级中文| 精品中文字幕一区| 日本成人中文字幕| 久久免费看少妇高潮v片特黄 | 91成人网在线| 蜜桃视频在线观看免费视频网站www| 亚洲自拍在线观看| 99国产精品| 91无套直看片红桃在线观看| 日韩欧美国产一区在线观看| 欧美少妇网站| 中文字幕日韩精品一区二区| 波波电影院一区二区三区| 中文字幕手机在线视频| 久久av.com| 精品在线91| 久久久久亚洲av无码专区首jn| 欧美视频在线视频| www.欧美日本韩国| 免费日韩电影在线观看| 国产一区福利在线| 亚洲婷婷综合网| 欧美猛少妇色xxxxx| 自拍欧美一区| 先锋资源在线视频| 欧美影视一区在线| 免费在线观看的电影网站| 深田咏美在线x99av| 成人免费毛片嘿嘿连载视频| 在线免费av片| 91精品国产高清自在线 | 国产日产欧美视频| 一区二区三区资源| 91caoporn在线|