LOGO OA教程 ERP教程 模切知識(shí)交流 PMS教程 CRM教程 開(kāi)發(fā)文檔 其他文檔  
 
網(wǎng)站管理員

C#的內(nèi)網(wǎng)穿透學(xué)習(xí)(附源碼)非常詳細(xì),零基礎(chǔ)入門(mén)到精通

admin
2024年6月2日 16:44 本文熱度 1724

 如何讓兩臺(tái)處在不同內(nèi)網(wǎng)的主機(jī)直接互連?你需要內(nèi)網(wǎng)穿透

上圖是一個(gè)非完整版內(nèi)外網(wǎng)通訊圖由內(nèi)網(wǎng)端先發(fā)起,內(nèi)網(wǎng)設(shè)備192.168.1.2:6677發(fā)送數(shù)據(jù)到外網(wǎng)時(shí)候必須經(jīng)過(guò)nat會(huì)轉(zhuǎn)換成對(duì)應(yīng)的外網(wǎng)ip+端口,然后在發(fā)送給外網(wǎng)設(shè)備,外網(wǎng)設(shè)備回復(fù)數(shù)據(jù)也是發(fā)給你的外網(wǎng)ip+端口。

這只是單向的內(nèi)去外,那反過(guò)來(lái),如果外網(wǎng)的設(shè)備需要主動(dòng)訪(fǎng)問(wèn)我局域網(wǎng)里的某一個(gè)設(shè)備是無(wú)法訪(fǎng)問(wèn)的,因?yàn)檫@個(gè)時(shí)候還沒(méi)做nat轉(zhuǎn)換所以外網(wǎng)不知道你內(nèi)網(wǎng)設(shè)備的應(yīng)用具體對(duì)應(yīng)的是哪個(gè)端口,這個(gè)時(shí)候我們就需要內(nèi)網(wǎng)穿透了,內(nèi)網(wǎng)穿透也叫NAT穿透;

穿透原理

如上圖所示經(jīng)NAT轉(zhuǎn)換后的內(nèi)外網(wǎng)地址+端口,會(huì)緩存一段時(shí)間,在這段時(shí)間內(nèi)192.168.1.2:6677和112.48.69.2020:8899的映射關(guān)系會(huì)一直存在,這樣你的內(nèi)網(wǎng)主機(jī)就得到一個(gè)外網(wǎng)地址,這個(gè)對(duì)應(yīng)關(guān)系又根據(jù)NAT轉(zhuǎn)換方法類(lèi)型的不同,得用對(duì)應(yīng)的方式實(shí)現(xiàn)打洞,NAT轉(zhuǎn)換方法類(lèi)型有下列幾種(來(lái)源百度百科NAT):

**(1)Full cone NAT:**即著名的一對(duì)一(one-to-one)NAT。

一旦一個(gè)內(nèi)部地址(iAddr:port1)映射到外部地址(eAddr:port2),所有發(fā)自iAddr:port1的包都經(jīng)由eAddr:port2向外發(fā)送。任意外部主機(jī)都能通過(guò)給eAddr:port2發(fā)包到iAddr:port1(純天然不用打洞!)

**(2)Address-Restricted cone NAT :**限制地址,即只接收曾經(jīng)發(fā)送到對(duì)端的IP地址來(lái)的數(shù)據(jù)包。

一旦一個(gè)內(nèi)部地址(iAddr:port1)映射到外部地址(eAddr:port2),所有發(fā)自iAddr:port1的包都經(jīng)由eAddr:port2向外發(fā)送。

任意外部主機(jī)(hostAddr:any)都能通過(guò)給eAddr:port2發(fā)包到達(dá)iAddr:port1的前提是:iAddr:port1之前發(fā)送過(guò)包到hostAddr:any. "any"也就是說(shuō)端口不受限制(只需知道某個(gè)轉(zhuǎn)換后的外網(wǎng)ip+端口即可。)

**(3)Port-Restricted cone NAT:**類(lèi)似受限制錐形NAT(Restricted cone NAT),但是還有端口限制。

一旦一個(gè)內(nèi)部地址(iAddr:port1)映射到外部地址(eAddr:port2),所有發(fā)自iAddr:port1的包都經(jīng)由eAddr:port2向外發(fā)送。一個(gè)外部主機(jī)(hostAddr:port3)能夠發(fā)包到達(dá)iAddr:port1的前提是:iAddr:port1之前發(fā)送過(guò)包到hostAddr:port3.(雙方需要各自知道對(duì)方轉(zhuǎn)換后的外網(wǎng)ip+端口,然后一方先發(fā)一次嘗試連接,另一方在次連接過(guò)來(lái)的時(shí)候就能直接連通了。)

(4)Symmetric NAT(對(duì)稱(chēng)NAT)

每一個(gè)來(lái)自相同內(nèi)部IP與port的請(qǐng)求到一個(gè)特定目的地的IP地址和端口,映射到一個(gè)獨(dú)特的外部來(lái)源的IP地址和端口。

同一個(gè)內(nèi)部主機(jī)發(fā)出一個(gè)信息包到不同的目的端,不同的映射使用外部主機(jī)收到了一封包從一個(gè)內(nèi)部主機(jī)可以送一封包回來(lái)(只能和Full cone NAT連,沒(méi)法打洞,手機(jī)流量開(kāi)熱點(diǎn)就是,同一個(gè)本地端口連接不同的服務(wù)器得到的外網(wǎng)第地址和IP不同!)

例子:

下面用一個(gè)例子演示下“受限制錐形NAT”的打洞,實(shí)現(xiàn)了這個(gè)它前面兩個(gè)類(lèi)型也能通用。對(duì)稱(chēng)型的話(huà)不考慮,打不了洞。

我們知道要實(shí)現(xiàn)兩臺(tái)“受限制錐形NAT”互連重點(diǎn)就是要知道對(duì)方轉(zhuǎn)換后的外網(wǎng)IP+端口,這樣我們可以:

1、準(zhǔn)備一臺(tái)Full cone NAT 類(lèi)型的外網(wǎng)服務(wù)端,接受來(lái)自?xún)蓚€(gè)客戶(hù)端的連接,并對(duì)應(yīng)告知對(duì)方ip+端口;

2、知道了對(duì)方ip+端口 需要設(shè)置socke:Socket.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ReuseAddress, true);這樣才能端口復(fù)用;目的就是讓連接對(duì)外的端口一致;

3、最后,我們可以讓兩臺(tái)客戶(hù)端互相連接,或者一臺(tái)先發(fā)一個(gè)請(qǐng)求,打個(gè)洞;另一個(gè)在去連接;

代碼:

1、TCP+IOCP方式,相對(duì) “面向?qū)ο蟆钡貙?shí)現(xiàn)穿透!

服務(wù)端 ServerListener類(lèi),用SocketAsyncEventArgs:

/// <summary>  

/// 打洞服務(wù)端,非常的簡(jiǎn)單,接收兩個(gè)連接并且轉(zhuǎn)發(fā)給對(duì)方;  

/// </summary>  

public class ServerListener : IServerListener  

{  

    IPEndPoint EndPoint { get; set; }  

    //消息委托  

    public delegate void EventMsg(object sender, string e);  

    public static object obj = new object();  

    //通知消息  

    public event EventMsg NoticeMsg;  

    //接收事件  

    public event EventMsg ReceivedMsg;  

    /// <summary>  

    /// 上次鏈接的  

    /// </summary>  

    private Socket Previous;  

    public ServerListener(IPEndPoint endpoint)    {  

        this.EndPoint = endpoint;  

    }  

    private Socket listener;  

    public void Start()    {  

        this.listener = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);  

        var connectArgs = new SocketAsyncEventArgs();              

        listener.Bind(EndPoint);  

        listener.Listen(2);  

        EndPoint = (IPEndPoint)listener.LocalEndPoint;   

        connectArgs.Completed += OnAccept;  

        //是否同步就完成了,同步完成需要自己觸發(fā)  

        if (!listener.AcceptAsync(connectArgs))  

            OnAccept(listener, connectArgs);  

    }  

    byte[] bytes = new byte[400];  

    private void OnAccept(object sender, SocketAsyncEventArgs e)    {  

        Socket socket = null;  

        try  

        {  

            var remoteEndPoint1 = e.AcceptSocket.RemoteEndPoint.ToString();  

            NoticeMsg?.Invoke(sender, $"客戶(hù)端:{remoteEndPoint1}連接上我了!\r\n");  

            SocketAsyncEventArgs readEventArgs = new SocketAsyncEventArgs();  

            readEventArgs.Completed += OnSocketReceived;  

            readEventArgs.UserToken = e.AcceptSocket;  

            readEventArgs.SetBuffer(bytes, 0, 400);  

            if (!e.AcceptSocket.ReceiveAsync(readEventArgs))  

                OnSocketReceived(e.AcceptSocket, readEventArgs);  

            lock (obj)  

            {  

                socket = e.AcceptSocket;  

                //上次有鏈接并且鏈接還”健在“  

                if (Previous == null||! Previous.Connected)  

                {  

                    Previous = e.AcceptSocket;  

                }  

                else  

                {  

                    //Previous.SendAsync()..?  

                    Previous.Send(Encoding.UTF8.GetBytes(remoteEndPoint1 + "_1"));  

                    socket.Send(Encoding.UTF8.GetBytes(Previous.RemoteEndPoint.ToString() + "_2"));  

                    NoticeMsg?.Invoke(sender, $"已經(jīng)通知雙方!\r\n");  

                    Previous = null;  

                }  

            }   

            e.AcceptSocket = null;  

            if (e.SocketError != SocketError.Success)  

                throw new SocketException((int)e.SocketError);  

             

            if(!listener.AcceptAsync(e))  

                OnAccept(listener, e);  

        }  

        catch  

        {  

            socket?.Close();  

       }  

    }  

    public void Close()    {  

        using (listener)  

        {  

           // listener.Shutdown(SocketShutdown.Both);  

            listener.Close();  

        }  

        //throw new NotImplementedException();  

    }  

    /// <summary>  

    /// 此處留有一個(gè)小BUG,接收的字符串大于400的時(shí)候會(huì)有問(wèn)題;可以參考客戶(hù)端修改  

    /// </summary>  

    public void OnSocketReceived(object sender, SocketAsyncEventArgs e)    {             

        Socket socket = e.UserToken as Socket;              

        var remoteEndPoint = socket.RemoteEndPoint.ToString();  

        try  

        {   

            if (e.BytesTransferred > 0 && e.SocketError == SocketError.Success)  

  

            {  

                ReceivedMsg?.Invoke(sender, $"收到:{remoteEndPoint}發(fā)來(lái)信息:{Encoding.UTF8.GetString(e.Buffer, 0, e.BytesTransferred)}\r\n");                   

  

            }  

            else  

            {  

                socket?.Close();  

                NoticeMsg?.Invoke(sender, $"鏈接:{remoteEndPoint}釋放啦!\r\n");  

                return;  

            }  

            if (!socket.ReceiveAsync(e))  

                OnSocketReceived(socket, e);  

        }  

        catch  

        {  

            socket?.Close();  

        }  

        //{  

        //   if (!((Socket)sender).AcceptAsync(e))  

        //        OnSocketReceived(sender, e);  

        //}  

        //catch  

        //{  

        //    return;  

        //}  

    }  

}

2、客戶(hù)端類(lèi) PeerClient用BeginReceive和EndReceive實(shí)現(xiàn)異步;

public class StateObject  

{  

    public Socket workSocket = null;  

    public const int BufferSize = 100;  

    public byte[] buffer = new byte[BufferSize];  

    public List<byte> buffers = new List<byte>();  

    //是不是和服務(wù)器的鏈接  

    public bool IsServerCon = false;  

}  

/// <summary>  

/// 打洞節(jié)點(diǎn)客戶(hù)端 實(shí)現(xiàn)的功能:  

/// 連接服務(wù)器獲取對(duì)方節(jié)點(diǎn)ip   

/// 請(qǐng)求對(duì)方ip(打洞)  

/// 根據(jù)條件判斷是監(jiān)聽(tīng)連接還是監(jiān)聽(tīng)等待連接  

/// </summary>  

public class PeerClient : IPeerClient  

{  

    //ManualResetEvent xxxxDone =  new ManualResetEvent(false);  

    //Semaphore   

    /// <summary>  

    /// 當(dāng)前鏈接  

    /// </summary>  

    public Socket Client { get;private set; }  

    #region 服務(wù)端  

    public string ServerHostName { get;private set; }  

    public int ServerPort { get; private set; }  

    #endregion  

  

    #region 接收和通知事件  

    public delegate void EventMsg(object sender, string e);  

    //接收事件  

    public event EventMsg ReceivedMsg;  

    //通知消息  

    public event EventMsg NoticeMsg;  

    #endregion  

    //本地綁定的節(jié)點(diǎn)  

    private IPEndPoint LocalEP;  

    public PeerClient(string hostname, int port)    {  

        Client = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);  

        this.ServerHostName = hostname;  

        this.ServerPort = port;  

    }  

  

    /// <summary>  

    /// 初始化客戶(hù)端(包括啟動(dòng))  

    /// </summary>  

    public void Init()    {  

        try  

        {  

            Client.Connect(ServerHostName, ServerPort);  

        }  

        catch (SocketException ex)  

        {  

            NoticeMsg?.Invoke(Client, $"連接服務(wù)器失敗!{ex}!\r\n");  

            throw;  

        }  

        catch (Exception ex)  

        {  

            NoticeMsg?.Invoke(Client, $"連接服務(wù)器失敗!{ex}!\r\n");  

            throw;  

        }  

        NoticeMsg?.Invoke(Client, $"連接上服務(wù)器了!\r\n");  

        var _localEndPoint = Client.LocalEndPoint.ToString();  

        LocalEP = new IPEndPoint(IPAddress.Parse(_localEndPoint.Split(':')[0])  

            , int.Parse(_localEndPoint.Split(':')[1]));  

        Receive(Client);  

    }  

    private void Receive(Socket client)    {  

        try  

        {  

            StateObject state = new StateObject();  

            state.workSocket = client;  

            state.IsServerCon = true;  

            client.BeginReceive(state.buffer, 0, StateObject.BufferSize, 0,  

                new AsyncCallback(ReceiveCallback), state);  

        }  

        catch (Exception e)  

        {  

            NoticeMsg?.Invoke(Client, $"接收消息出錯(cuò)了{(lán)e}!\r\n");  

        }  

    }  

    private void ReceiveCallback(IAsyncResult ar)    {  

        try  

        {  

            var state = (StateObject)ar.AsyncState;  

            Socket _client = state.workSocket;  

            //因?yàn)榈竭@邊的經(jīng)常Connected 還是true  

            //if (!_client.Connected)  

            //{  

            //    _client.Close();  

            //    return;  

            //}  

            SocketError error = SocketError.Success;  

            int bytesRead = _client.EndReceive(ar,out error);  

            if (error == SocketError.ConnectionReset)  

            {  

                NoticeMsg?.Invoke(Client, $"鏈接已經(jīng)釋放!\r\n");  

                _client.Close();  

                _client.Dispose();  

                return;  

            }  

            if (SocketError.Success!= error)  

            {  

               throw new SocketException((int)error);  

            }  

            var arr = state.buffer.AsQueryable().Take(bytesRead).ToArray();  

            state.buffers.AddRange(arr);  

            if (bytesRead >= state.buffer.Length)  

            {  

                _client.BeginReceive(state.buffer, 0, StateObject.BufferSize, 0,  

                  new AsyncCallback(ReceiveCallback), state);  

                state.buffers.CopyTo(state.buffers.Count, state.buffer, 0, bytesRead);  

                //_client.BeginReceive(state.buffer, 0, StateObject.BufferSize, 0,  

                //    new AsyncCallback(ReceiveCallback), state);  

            }  

            else  

            {  

                var _msg = Encoding.UTF8.GetString(state.buffers.ToArray());  

                ReceivedMsg?.Invoke(_client, _msg);  

                if (state.IsServerCon)  

                {  

                    _client.Shutdown(SocketShutdown.Both);  

                    _client.Close();  

                    int retryCon = _msg.Contains("_1") ? 1 : 100;  

                    _msg = _msg.Replace("_1", "").Replace("_2", "");  

                    TryConnection(_msg.Split(':')[0], int.Parse(_msg.Split(':')[1]), retryCon);  

                    return;  

                }  

                state = new StateObject();  

                state.IsServerCon = false;  

                state.workSocket = _client;  

                _client.BeginReceive(state.buffer, 0, StateObject.BufferSize, 0,  

                 new AsyncCallback(ReceiveCallback), state);  

            }  

        }               

        catch (SocketException ex)  

        {  

            //10054  

            NoticeMsg?.Invoke(Client, $"鏈接已經(jīng)釋放!{ex}!\r\n");  

        }  

        catch (Exception e)  

        {  

            NoticeMsg?.Invoke(Client, $"接收消息出錯(cuò)了2{e}!\r\n");  

        }  

    }  

    /// <summary>  

    /// 打洞或者嘗試鏈接  

    /// </summary>  

    private void TryConnection(string remoteHostname, int remotePort,int retryCon)    {  

        Client = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);  

        Client.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ReuseAddress, true);  

        var _iPRemotePoint = new IPEndPoint(IPAddress.Parse(remoteHostname), remotePort);  

        Client.Bind(LocalEP);  

        System.Threading.Thread.Sleep(retryCon==1?1:3*1000);  

        for (int i = 0; i < retryCon; i++)  

        {  

            try  

            {  

                Client.Connect(_iPRemotePoint);  

                NoticeMsg?.Invoke(Client, $"已經(jīng)連接上:{remoteHostname}:{remotePort}!\r\n");  

                StateObject state = new StateObject();  

                state.workSocket = Client;  

                state.IsServerCon = false;  

                Client.BeginReceive(state.buffer, 0, StateObject.BufferSize, 0,  

                 new AsyncCallback(ReceiveCallback), state);  

                return;  

            }  

            catch  

            {  

                NoticeMsg?.Invoke(Client, $"嘗試第{i+1}次鏈接:{remoteHostname}:{remotePort}!\r\n");  

            }  

        }  

        if (retryCon==1)  

        {  

            Listening(LocalEP.Port);  

            return;  

        }  

        NoticeMsg?.Invoke(Client, $"嘗試了{(lán)retryCon}次都沒(méi)有辦法連接到:{remoteHostname}:{remotePort},涼了!\r\n");   }  

  

    /// <summary>  

    /// 如果連接不成功,因?yàn)槭孪扔写蚨催^(guò)了,根據(jù)條件監(jiān)聽(tīng) 等待對(duì)方連接來(lái)  

    /// </summary>  

    private void Listening(int Port)    {  

        try  

        {  

            Client = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);  

            Client.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ReuseAddress, true);  

            Client.Bind(new IPEndPoint(IPAddress.Any, Port));Client.Listen((int)SocketOptionName.MaxConnections);  

            NoticeMsg?.Invoke(Client, $"開(kāi)始偵聽(tīng)斷開(kāi)等待鏈接過(guò)來(lái)!\r\n");  

            StateObject state = new StateObject();  

            state.IsServerCon = false;  

            var _socket = Client.Accept();//只有一個(gè)鏈接 不用BeginAccept  

            Client.Close();//關(guān)系現(xiàn)有偵聽(tīng)  

            Client = _socket;  

            state.workSocket = Client;  

            NoticeMsg?.Invoke(Client, $"接收到來(lái)自{Client.RemoteEndPoint}的連接!\r\n");  

            Client.BeginReceive(state.buffer, 0, StateObject.BufferSize, 0,  

                new AsyncCallback(ReceiveCallback), state);  

        }  

        catch (Exception ex)  

        {  

            NoticeMsg?.Invoke(Client, $"監(jiān)聽(tīng)出錯(cuò)了{(lán)ex}涼了!\r\n");  

        }  

        //scoket.send  

    }  

    /// <summary>  

    /// 本例子只存在一個(gè)成功的鏈接,對(duì)成功的連接發(fā)送消息!  

    /// </summary>  

    /// <param name="strMsg"></param>  

    public void Send(string strMsg)    {   

        byte[] bytes = Encoding.UTF8.GetBytes(strMsg);   

        Client.BeginSend(bytes, 0, bytes.Length, 0,  

            new AsyncCallback(SendCallback), Client);  

    }  

    private void SendCallback(IAsyncResult ar)    {  

        try  

        {   

            Socket _socket = (Socket)ar.AsyncState;  

            //if(ar.IsCompleted)  

            _socket.EndSend(ar);   

        }  

        catch (Exception e)  

        {  

            NoticeMsg?.Invoke(Client, $"發(fā)送消息出錯(cuò)了{(lán)e}!\r\n");  

        }  

    }  

}

**完整代碼:**https://gitee.com/qqljcn/zsg_-peer-to-peer

附件:zsg_-peer-to-peer-master.zip

二、面向過(guò)程方式

Task+(TcpClient+TcpListener )|(UdpClient)實(shí)現(xiàn) tcp|udp的打洞!這個(gè)就不貼代碼了直接放碼云連接

https://gitee.com/qqljcn/zsg_-peer-to-peer_-lite

附件:zsg_-peer-to-peer_-lite-master.zip

三、說(shuō)明

1、本人是個(gè)老菜鳥(niǎo)代碼僅供參考,都是挺久以前寫(xiě)的也沒(méi)有經(jīng)過(guò)嚴(yán)格的測(cè)試僅能演示這個(gè)例子,有不成熟的地方,煩請(qǐng)各位大神海涵指教;

2、不要都用本機(jī)試這個(gè)例子,本機(jī)不走nat

3、然后udp因?yàn)槭菬o(wú)連接的所以打孔成功后不要等太久再發(fā)消息,nat緩存一過(guò)就失效了!

4、確定自己不是對(duì)稱(chēng)型nat的話(huà),如果打洞不成功,那就多試幾次!

5 、我這個(gè)例子代碼名字叫 PeerToPeer 但不是真的p2p, 微軟提供了p2p的實(shí)現(xiàn) 在using System.Net.PeerToPeer命名空間下。

以上是通過(guò)nat的方式,另外還有一種方式是,通過(guò)一個(gè)有外網(wǎng)ip的第三方服務(wù)器轉(zhuǎn)發(fā)像 花生殼、nat123這類(lèi)軟件,也有做個(gè)小程序,并且自己在用以后演示;

- EOF -


該文章在 2024/6/2 16:44:27 編輯過(guò)
關(guān)鍵字查詢(xún)
相關(guān)文章
正在查詢(xún)...
點(diǎn)晴ERP是一款針對(duì)中小制造業(yè)的專(zhuān)業(yè)生產(chǎn)管理軟件系統(tǒng),系統(tǒng)成熟度和易用性得到了國(guó)內(nèi)大量中小企業(yè)的青睞。
點(diǎn)晴PMS碼頭管理系統(tǒng)主要針對(duì)港口碼頭集裝箱與散貨日常運(yùn)作、調(diào)度、堆場(chǎng)、車(chē)隊(duì)、財(cái)務(wù)費(fèi)用、相關(guān)報(bào)表等業(yè)務(wù)管理,結(jié)合碼頭的業(yè)務(wù)特點(diǎn),圍繞調(diào)度、堆場(chǎng)作業(yè)而開(kāi)發(fā)的。集技術(shù)的先進(jìn)性、管理的有效性于一體,是物流碼頭及其他港口類(lèi)企業(yè)的高效ERP管理信息系統(tǒng)。
點(diǎn)晴WMS倉(cāng)儲(chǔ)管理系統(tǒng)提供了貨物產(chǎn)品管理,銷(xiāo)售管理,采購(gòu)管理,倉(cāng)儲(chǔ)管理,倉(cāng)庫(kù)管理,保質(zhì)期管理,貨位管理,庫(kù)位管理,生產(chǎn)管理,WMS管理系統(tǒng),標(biāo)簽打印,條形碼,二維碼管理,批號(hào)管理軟件。
點(diǎn)晴免費(fèi)OA是一款軟件和通用服務(wù)都免費(fèi),不限功能、不限時(shí)間、不限用戶(hù)的免費(fèi)OA協(xié)同辦公管理系統(tǒng)。
Copyright 2010-2025 ClickSun All Rights Reserved

黄频国产免费高清视频,久久不卡精品中文字幕一区,激情五月天AV电影在线观看,欧美国产韩国日本一区二区
一级午夜福利不卡片在线69网 | 亚洲AV最新高清每天更新 | 亚洲国产一二三区欧美日韩 | 亚洲一区二区偷拍精品 | 一区二区免费高清在线观看国产 | 亚洲欧美日韩中文字幕二区 |