LOGO OA教程 ERP教程 模切知識交流 PMS教程 CRM教程 開發文檔 其他文檔  
 
網站管理員

c# SuperWebSocket服務端、html WebSocket客戶端、c# WebSocket4Net客戶端的使用

admin
2019年11月12日 17:33 本文熱度 5155
c# superwebsocket服務端可以寄宿在控制臺程序、窗體程序、Windows服務
c# websocket客戶端可以是控制臺程序、窗體程序、Windows服務、html、手機,能連上websocket的就行了
服務端可以開啟wss安全鏈接

如果想做即時通訊,雙工通訊,或者因為頻繁發送http請求導致的web服務器承受不住,都可以轉用websocket

websocket 有良好的交互體驗,快速的數據傳輸
websocket 開發簡單,部署方便,上手沒難度

我這里展示了,把websocket服務端寄宿在Windows服務里,客戶端則用控制臺訪問和用web訪問,調試階段,為了避免頻繁停啟Windows服務,所以我用控制臺啟動了服務端,生產環境就可以直接把exe執行文件注冊成服務了

底部有源代碼百度盤下載,Windows服務安裝方法

1.服務端
把websocket服務端類,拷貝到項目里

using SuperSocket.SocketBase;
using SuperSocket.SocketBase.Config;
using SuperSocket.SocketEngine;
using SuperWebSocket;
using System;
using System.Threading;
using System.Threading.Tasks;
//install-package SuperWebSocket
//install-package nlog
//install-package nlog.config
namespace WebSocketService
{
    public class WSocketServer:IDisposable
    {
        public static NLog.Logger _Logger = NLog.LogManager.GetCurrentClassLogger();

        #region 向外傳遞數據事件
        public event Action<WebSocketSession, string> MessageReceived;
        public event Action<WebSocketSession> NewConnected;
        public event Action<WebSocketSession> Closed;
        #endregion

        public WebSocketServer WebSocket;

        Thread _thread;
        bool _isRunning = true;


        public WSocketServer()
        {
        }

        #region WebSockertServer
        /// <summary>
        /// 開啟服務端
        /// </summary>
        /// <param name="port"></param>
        /// <param name="serverName"></param>
        /// <param name="isUseCertificate"></param>
        /// <param name="serverStoreName"></param>
        /// <param name="serverSecurity"></param>
        /// <param name="serverThumbprint"></param>
        /// <returns></returns>
        public bool Open(int port, string serverName, bool isUseCertificate = false, string serverStoreName = "", string serverSecurity = "", string serverThumbprint = "")
        {
            bool isSetuped = false;
            try
            {
                this.WebSocket = new WebSocketServer();
                var serverConfig = new ServerConfig
                {
                    Name = serverName,
                    MaxConnectionNumber = 10000, //最大允許的客戶端連接數目,默認為100。
                    Mode = SocketMode.Tcp,
                    Port = port, //服務器監聽的端口。
                    ClearIdleSession = false,   //true或者false, 是否清除空閑會話,默認為false。
                    ClearIdleSessionInterval = 120,//清除空閑會話的時間間隔,默認為120,單位為秒。
                    ListenBacklog = 10,
                    ReceiveBufferSize = 64 * 1024, //用于接收數據的緩沖區大小,默認為2048。
                    SendBufferSize = 64 * 1024,   //用戶發送數據的緩沖區大小,默認為2048。
                    KeepAliveInterval = 1,     //keep alive消息發送時間間隔。單位為秒。
                    KeepAliveTime = 60,    //keep alive失敗重試的時間間隔。單位為秒。
                    SyncSend = false
                };
                SocketServerFactory socketServerFactory = null;
                //開啟wss 使用證書
                if (isUseCertificate)
                {
                    serverConfig.Security = serverSecurity;
                    serverConfig.Certificate = new SuperSocket.SocketBase.Config.CertificateConfig
                    {
                        StoreName = serverStoreName,
                        StoreLocation = System.Security.Cryptography.X509Certificates.StoreLocation.LocalMachine,
                        Thumbprint = serverThumbprint
                    };
                    socketServerFactory = new SocketServerFactory();
                }
                isSetuped = this.WebSocket.Setup(new RootConfig(),serverConfig, socketServerFactory);
                if (isSetuped)
                {
                    _Logger.Info("Setup Success...");
                }
                else
                {
                    _Logger.Error("Failed to setup!");
                }
                this.WebSocket.NewSessionConnected += NewSessionConnected;
                this.WebSocket.NewMessageReceived += NewMessageReceived;
                this.WebSocket.SessionClosed += SessionClosed;
                isSetuped = this.WebSocket.Start();
                if (isSetuped)
                {
                    _Logger.Info("Start Success...");
                    _Logger.Info("Server Listen at " + this.WebSocket.Listeners[0].EndPoint.Port.ToString());
                    this._isRunning = true;
                    this._thread = new Thread(new ThreadStart(ProcessMaintainance));
                    this._thread.Start();
                }
                else
                {
                    _Logger.Error("Failed to start!");
                }
            }
            catch (Exception ex)
            {
                _Logger.Error(ex.ToString());
            }
            return isSetuped;
        }
        /// <summary>
        /// 消息觸發事件
        /// </summary>
        /// <param name="session"></param>
        /// <param name="value"></param>
        void NewMessageReceived(WebSocketSession session, string value)
        {
            try
            {
                _Logger.Info("Receive:" + value.ToString() + " ClientIP:" + session.RemoteEndPoint);
                if(value.ToString().Equals("IsHere**"))//客戶端定時發送心跳,維持鏈接
                {
                    return;
                }
                else
                {
                    MessageReceived?.Invoke(session, value.ToString());
                }
            }
            catch (Exception e)
            {
                _Logger.Error(e.ToString());
            }
        }
        /// <summary>
        /// 新鏈接觸發事件
        /// </summary>
        /// <param name="session"></param>
        void NewSessionConnected(WebSocketSession session)
        {
            try
            {
                string message = string.Format("New Session Connected:{0}, Path:{1}, Host:{2}, IP:{3}",
                    session.SessionID.ToString(), session.Path, session.Host, session.RemoteEndPoint);
                _Logger.Info(message);
                NewConnected?.Invoke(session);
                
            }
            catch (Exception e)
            {
                _Logger.Error(e.ToString());
            }
        }
        /// <summary>
        /// 客戶端鏈接關閉觸發事件
        /// </summary>
        /// <param name="session"></param>
        /// <param name="value"></param>
        void SessionClosed(WebSocketSession session, CloseReason value)
        {            
            string message = string.Format("Session Close:{0}, Path:{1}, IP:{2}", value.ToString(), session.Path,session.RemoteEndPoint);
            _Logger.Info(message);
            Closed?.Invoke(session);
        }

        #endregion
        /// <summary>
        /// 關閉服務端觸發事件
        /// </summary>
        public void Dispose()
        {
            this._isRunning = false;
            foreach (WebSocketSession session in this.WebSocket.GetAllSessions())
            {
                session.Close();
            }
            try
            {
                this.WebSocket.Stop();
            }
            catch { }
        }
        /// <summary>
        /// 輸出實時連接線程
        /// </summary>
        void ProcessMaintainance()
        {
            do
            {
                try
                {
                    _Logger.Debug("Display Session Info:" + this.WebSocket.SessionCount);
                    foreach (WebSocketSession session in this.WebSocket.GetAllSessions())
                    {
                        string message = string.Format("ID:{0}, Remote:{1}, Path:{2}, LastActiveTime:{3}, StartTime:{4}",
                            session.SessionID, session.RemoteEndPoint, session.Path
                          , session.LastActiveTime, session.StartTime);
                        _Logger.Debug(message);
                    }
                }
                catch (Exception e)
                {
                    _Logger.Error(e.ToString());
                }
                System.Threading.Thread.Sleep(5 * 60000);
            } while (this._isRunning);
        }

        /// <summary>
        /// 發送消息
        /// </summary>
        /// <param name="session">客戶端連接</param>
        /// <param name="message">消息內容</param>

        public void SendMessage(WebSocketSession session, string message)
        {
            Task.Factory.StartNew(() =>{if (session != null && session.Connected) session.Send(message);});
        }
        
    }
}
打開控制臺 按順序安裝這幾個包
install-package SuperWebSocket
install-package nlog
install-package nlog.config

然后寫個業務邏輯類,操作websocket服務端類
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace WebSocketService
{
    public class BLL
    {
        WSocketServer _server = null;
        bool _isRunning = false;
        public BLL()
        {
            try
            {

                _server = new WSocketServer();
                _server.MessageReceived += Server_MessageReceived;
                _server.NewConnected += Server_NewConnected;
                _server.Closed += _server_Closed;
            }
            catch (Exception ex)
            {
                WSocketServer._Logger.Error(ex.ToString());
            }
        }

        private void _server_Closed(SuperWebSocket.WebSocketSession obj)
        {
            Console.WriteLine($"Closed {System.Web.HttpUtility.UrlDecode(obj.Path, System.Text.Encoding.UTF8)}");
        }

        private void Server_NewConnected(SuperWebSocket.WebSocketSession obj)
        {
            //對新鏈接做處理,驗證鏈接是否合法等等,不合法則關閉該鏈接
            //新鏈接進行數據初始化
            
            Console.WriteLine($"NewConnected {System.Web.HttpUtility.UrlDecode(obj.Path, System.Text.Encoding.UTF8)}");
        }

        private void Server_MessageReceived(SuperWebSocket.WebSocketSession arg1, string arg2)
        {
            //接收到客戶端鏈接發送的東西
            Console.WriteLine($"from {System.Web.HttpUtility.UrlDecode(arg1.Path, System.Text.Encoding.UTF8)} => {arg2}");
        }

        public bool Start()
        {
            _isRunning = true;
            //設置監聽端口
            var result = _server.Open(1234, "MySocket");

            //模擬 服務端主動推送信息給客戶端
            if (result)
            {
                Task.Factory.StartNew(() => {
                    while (_isRunning)
                    {
                        foreach (var item in _server.WebSocket.GetAllSessions()) _server.SendMessage(item,"服務器時間:"+DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss"));
                        System.Threading.Thread.Sleep(1000);
                    }
                });
            }
            return result;
        }
        public void Stop()
        {
            _isRunning = false;
            _server.Dispose();
        }
    }
}
然后就是在Windows服務停啟時操作業務邏輯

好了 一個c# websocket 服務端搭建完成了

因為這里是通過控制臺來調用調試這個服務,所以要再建一個控制臺項目

然后選中控制臺項目,ctrl+f5,啟動項目,如果沒什么問題,那么就搞定了,注意websocket監聽的端口是可用的,我這里用了1234

2.客戶端-控制臺
接下來就建個控制臺項目作為客戶端,去連接服務端
新建控制臺項目,把websocket客戶端類拷進去

using SuperSocket.ClientEngine;
using System;
using System.Collections.Generic;
using System.Configuration;
using System.Linq;
using System.Net.WebSockets;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using WebSocket4Net;
//install-package WebSocket4Net
//install-package nlog
//install-package nlog.config
namespace WebSocketClient
{
   public class WSocketClient:IDisposable
    {
    //日志管理
        public static NLog.Logger _Logger = NLog.LogManager.GetCurrentClassLogger();

        #region 向外傳遞數據事件
        public event Action<string> MessageReceived;
        #endregion

        WebSocket4Net.WebSocket _webSocket;
        /// <summary>
        /// 檢查重連線程
        /// </summary>
        Thread _thread;
        bool _isRunning = true;
        /// <summary>
        /// WebSocket連接地址
        /// </summary>
        public string ServerPath { get; set; } 

        public WSocketClient(string url)
        {
            ServerPath = url;
            this._webSocket = new WebSocket4Net.WebSocket(url);
            this._webSocket.Opened += WebSocket_Opened;
            this._webSocket.Error += WebSocket_Error;
            this._webSocket.Closed += WebSocket_Closed;
            this._webSocket.MessageReceived += WebSocket_MessageReceived;
        }

        #region "web socket "
        /// <summary>
        /// 連接方法
        /// <returns></returns>
        public bool Start() 
        {
            bool result = true;
            try 
            {
                this._webSocket.Open();

                this._isRunning = true;
                this._thread = new Thread(new ThreadStart(CheckConnection));
                this._thread.Start();
            }
            catch (Exception ex) 
            {
                _Logger.Error(ex.ToString());
                result = false;
            }
            return result;
        }
        /// <summary>
        /// 消息收到事件
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        void WebSocket_MessageReceived(object sender, MessageReceivedEventArgs e) 
        {
            _Logger.Info(" Received:" +e.Message);
            MessageReceived?.Invoke(e.Message);
        }
        /// <summary>
        /// Socket關閉事件
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        void WebSocket_Closed(object sender, EventArgs e) 
        {
            _Logger.Info("websocket_Closed");
        }
        /// <summary>
        /// Socket報錯事件
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        void WebSocket_Error(object sender, ErrorEventArgs e) 
        {
            _Logger.Info("websocket_Error:" + e.Exception.ToString());
        }
        /// <summary>
        /// Socket打開事件
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        void WebSocket_Opened(object sender, EventArgs e) 
        {
            _Logger.Info(" websocket_Opened");         
        }
        /// <summary>
        /// 檢查重連線程
        /// </summary>
        private void CheckConnection()
        {
            do
            {
                try
                {
                    if (this._webSocket.State != WebSocket4Net.WebSocketState.Open && this._webSocket.State != WebSocket4Net.WebSocketState.Connecting)
                    {
                        _Logger.Info(" Reconnect websocket WebSocketState:" + this._webSocket.State);
                        this._webSocket.Close();
                        this._webSocket.Open();
                        Console.WriteLine("正在重連");
                    }
                }
                catch (Exception ex)
                {
                    _Logger.Error(ex.ToString());
                }
                System.Threading.Thread.Sleep(5000);
            } while (this._isRunning);
        }
        #endregion

        /// <summary>
        /// 發送消息
        /// </summary>
        /// <param name="Message"></param>
        public void SendMessage(string Message)
        {
            Task.Factory.StartNew(() =>
            {
                if (_webSocket != null && _webSocket.State == WebSocket4Net.WebSocketState.Open)
                {
                    this._webSocket.Send(Message);
                }
            });
        }

        public void Dispose()
        {
            this._isRunning = false;
            try
            {
                _thread.Abort();
            }
            catch
            {

            }
            this._webSocket.Close();
            this._webSocket.Dispose();
            this._webSocket = null;  
        }
    }
}
同樣打開包管理控制臺,順序安裝這幾個必要的包,注意選擇安裝的項目名字
install-package WebSocket4Net
install-package nlog
install-package nlog.config

裝完就可以開始連接服務端了

3.客戶端-網頁

<!DOCTYPE html>

<html lang="en" xmlns="http://www.w3.org/1999/xhtml">
<head>
    <meta charset="utf-8" />
    <title></title>
</head>
<body>
<div id="onMessage" style="width:100%;height:200px;overflow-y:scroll;">

</div>
<input type="text" id="input" /><input id="sendMessage" type="button" value="發送" />
</body>
</html>
<script>
    //websocket 類
    var myWS = function () {
        this.ws = null;
        this.url = null;
        this.onmessage = function () { };
        this.onopen = function () {
        };
        this.onclose = function () { };
        this.onerror = function () {
        };
    };
    myWS.prototype.sendMessage = function (message) {//客戶端發送消息到服務器方法
        if (this.ws != null && this.ws.readyState == 1) {
            this.ws.send(message);
        }
    };
    myWS.prototype.start = function () {//啟動鏈接
        this.connect();
        this.conncheckstatus();
        this.heartbeat();
    };
    //心跳
    myWS.prototype.heartbeat = function () {
        var obj = this;
        setTimeout(function () {
            if (obj.ws != null && obj.ws.readyState == 1) {
                var message = "IsHere**";
                obj.ws.send(message);
            }
            setTimeout(arguments.callee, 300000);
        }, 10);
    };
    myWS.prototype.conncheckstatus = function () {
        var obj = this;
        setTimeout(function () {
            if (obj.ws != null && obj.ws.readyState != 0 && obj.ws.readyState != 1) {
                obj.connect();
            }
            setTimeout(arguments.callee, 5000);
        }, 15);
    };
    myWS.prototype.connect = function () {
        this.disconnect();
        //WebSocket地址
        if (this.url != null && this.url != "") {
            try {
                if ("WebSocket" in window) {
                    this.ws = new WebSocket(this.url);
                }
                else if ("MozWebSocket" in window) {
                    this.ws = new MozWebSocket(this.url);
                }
                else {
                    alert("browser not support websocket");
                }
                if (this.ws != null) {
                    var that = this;
                    this.ws.onopen = function (event) { that.onopen(event); };
                    this.ws.onmessage = function (event) { that.onmessage(event); };
                    this.ws.onclose = function (event) { that.onclose(event); };
                    this.ws.onerror = function (event) { that.onerror(event); };
                }
            }
            catch (ex) {
                console.log("connect:" + ex);
            }
        }
    };
    myWS.prototype.disconnect = function () {
        if (this.ws != null) {
            try {
                this.ws.close();
                this.ws = null;
            }
            catch (ex) {
                this.ws = null;
            }
        }
    };
</script>

<script>
    var _ws = new myWS();
    _ws.url = ''ws://192.168.1.13:1234/lcj網頁'';
    //注冊接收服務端消息的方法 服務端數據位于event.data字段
    _ws.onmessage = (event) => {
        document.getElementById(''onMessage'').innerHTML += event.data + ''<br/>'';
        document.getElementById(''onMessage'').scrollTop = document.getElementById(''onMessage'').scrollHeight;
    };
    //啟動鏈接
    _ws.start();

    document.getElementById(''sendMessage'').addEventListener(''click'', () => {
        //客戶端發送消息到服務端
        _ws.sendMessage(document.getElementById(''input'').value);
        document.getElementById(''input'').value = '''';
    });
</script>
完成…
源代碼下載下載

Windows服務安裝方法
1.把項目模式改成發布模式Release
2.右鍵項目生成或者重新生成
3.打開項目位置,打開bin文件夾,打開Release文件夾

4.把服務必備的一些組件都復制走


安裝完成,在計算機管理-服務里面可以看到,你的服務名字,還沒啟動,需要手動開啟,或者電腦重啟后自動開啟

右鍵服務-選擇第一個“啟動”

卸載服務,先打開服務管理,右鍵,停止,然后復制刪除命令到cmd執行

刷新一下服務列表,服務已經刪除

如果想要更新服務的話,要先停止服務,然后把生成后的新dll啊或者exe,覆蓋掉舊的就可以了,修改配置文件的話也需要重新啟動服務才能生效,停止服務需要一點時間,不能一點停止就立即覆蓋,系統會報文件占用

所有步驟都完成了

這里是安裝服務的工具和命令
鏈接:下載

WSS 開啟介紹

各參數說明,這里可以有兩種方式使用證書
1.是填證書安裝后的存儲區名字和證書的指紋
2.是直接填證書的所在路徑,和密碼
目前啊 我只成功地用過路徑,和密碼的方式開啟,
關于怎么用指紋,我還是不知道,如有哪位知道的,望相告.
另外開了wss后,需要用域名來訪問,ip不行
(以上結論來于,阿里云免費試用服務器個人版 win server 2008 r2 和免費國外域名 和 阿里云上申請的免費 ssl證書…)


/// <summary>
        /// 開啟服務端
        /// </summary>
        /// <param name="port">123</param>
        /// <param name="serverName">test</param>
        /// <param name="isUseCertificate">true 如果開啟wss需要用域名訪問(wss:\\abc.com:123\)</param>
        /// <param name="certificateStoreName">My -證書安裝后的存儲位置(一般是“個人”)</param>
        /// <param name="security">ssl/tls</param>
        /// <param name="certificateThumbprint">‎7384a6027fa8004585c0958c7fcbcb8fd9cd27fb 證書指紋 如果指紋填空,則使用路徑加載證書,否則使用指紋</param>
        /// <param name="certificatePath">D:\1774183_xxx_iis\1774183_xxx.pfx 證書所在路徑</param>
        /// <param name="certificatePwd">ABC 證書密碼</param>
        /// <returns></returns>
        public bool Open(int port, string serverName, bool isUseCertificate = false, string certificateStoreName = "", string security = "", string certificateThumbprint = "",string certificatePath="",string certificatePwd="")
        {
            bool isSetuped = false;
            try
            {
                this.WebSocket = new WebSocketServer();
                var serverConfig = new ServerConfig
                {
                    Name = serverName,
                    MaxConnectionNumber = 10000, //最大允許的客戶端連接數目,默認為100。
                    Mode = SocketMode.Tcp,
                    Port = port, //服務器監聽的端口。
                    ClearIdleSession = false,   //true或者false, 是否清除空閑會話,默認為false。
                    ClearIdleSessionInterval = 120,//清除空閑會話的時間間隔,默認為120,單位為秒。
                    ListenBacklog = 10,
                    ReceiveBufferSize = 64 * 1024, //用于接收數據的緩沖區大小,默認為2048。
                    SendBufferSize = 64 * 1024,   //用戶發送數據的緩沖區大小,默認為2048。
                    KeepAliveInterval = 1,     //keep alive消息發送時間間隔。單位為秒。
                    KeepAliveTime = 60,    //keep alive失敗重試的時間間隔。單位為秒。
                    SyncSend = false                    
                };
                SocketServerFactory socketServerFactory = null;
                //開啟wss 使用證書
                if (isUseCertificate)
                {
                    serverConfig.Security = security;
                    //指紋不為空 則使用指紋,否則使用路徑訪問證書
                    if (certificateThumbprint != string.Empty)
                    {
                        serverConfig.Certificate = new SuperSocket.SocketBase.Config.CertificateConfig
                        {
                            StoreName = certificateStoreName,
                            StoreLocation = System.Security.Cryptography.X509Certificates.StoreLocation.LocalMachine,
                            Thumbprint = certificateThumbprint
                        };
                    }
                    else
                    {
                        serverConfig.Certificate = new SuperSocket.SocketBase.Config.CertificateConfig
                        {
                            FilePath = certificatePath,
                            Password = certificatePwd
                        };
                    }
                    socketServerFactory = new SocketServerFactory();
                }
                isSetuped = this.WebSocket.Setup(new RootConfig(),serverConfig, socketServerFactory);
                if (isSetuped)
                {
                    _Logger.Info("Setup Success...");
                }
                else
                {
                    _Logger.Error("Failed to setup!");
                }
                this.WebSocket.NewSessionConnected += NewSessionConnected;
                this.WebSocket.NewMessageReceived += NewMessageReceived;
                this.WebSocket.SessionClosed += SessionClosed;
                isSetuped = this.WebSocket.Start();
                if (isSetuped)
                {
                    _Logger.Info("Start Success...");
                    _Logger.Info("Server Listen at " + this.WebSocket.Listeners[0].EndPoint.Port.ToString());
                    this._isRunning = true;
                    this._thread = new Thread(new ThreadStart(ProcessMaintainance));
                    this._thread.Start();
                }
                else
                {
                    _Logger.Error("Failed to start!");
                }
            }
            catch (Exception ex)
            {
                _Logger.Error(ex.ToString());
            }
            return isSetuped;
        }
這里我使用配置文件

源碼地址鏈接:下載

該文章在 2019/11/12 17:33:27 編輯過
關鍵字查詢
相關文章
正在查詢...
點晴ERP是一款針對中小制造業的專業生產管理軟件系統,系統成熟度和易用性得到了國內大量中小企業的青睞。
點晴PMS碼頭管理系統主要針對港口碼頭集裝箱與散貨日常運作、調度、堆場、車隊、財務費用、相關報表等業務管理,結合碼頭的業務特點,圍繞調度、堆場作業而開發的。集技術的先進性、管理的有效性于一體,是物流碼頭及其他港口類企業的高效ERP管理信息系統。
點晴WMS倉儲管理系統提供了貨物產品管理,銷售管理,采購管理,倉儲管理,倉庫管理,保質期管理,貨位管理,庫位管理,生產管理,WMS管理系統,標簽打印,條形碼,二維碼管理,批號管理軟件。
點晴免費OA是一款軟件和通用服務都免費,不限功能、不限時間、不限用戶的免費OA協同辦公管理系統。
Copyright 2010-2025 ClickSun All Rights Reserved

黄频国产免费高清视频,久久不卡精品中文字幕一区,激情五月天AV电影在线观看,欧美国产韩国日本一区二区
婷婷射亚洲娱乐中文网 | 中文字幕不卡一区2021 | 永久在线亚洲观看 | 最新国产一级特黄Av | 亚洲综合另类小说色区一 | 思思99热思思久久最新精品 |