• <ins id="pjuwb"></ins>
    <blockquote id="pjuwb"><pre id="pjuwb"></pre></blockquote>
    <noscript id="pjuwb"></noscript>
          <sup id="pjuwb"><pre id="pjuwb"></pre></sup>
            <dd id="pjuwb"></dd>
            <abbr id="pjuwb"></abbr>

            天行健 君子當自強而不息

            創建游戲內核(24)

             

            本篇是創建游戲內核(23)的續篇,涉及到的DirectPlay基礎知識請參閱使用DirectPlay進行網絡互聯(2)使用DirectPlay進行網絡互聯(3)

             

            使用NETWORK_SERVER處理服務器

            可以使用類NETWORK_SERVER來處理網絡服務器端,這個類用于初始化DirectPlay服務器對象,主持游戲會話,處理收到和 要發送的網絡消息。

            來看看它的定義:

            //====================================================================================
            // This class encapsulate server how to communicate with client.
            //====================================================================================
            typedef class NETWORK_SERVER
            {
            public:
                NETWORK_SERVER();
                
            virtual ~NETWORK_SERVER();

                IDirectPlay8Server* get_server();

                BOOL init();
                BOOL shutdown();

                BOOL host(
            const GUID* guid_adapter, long port, const char* session_name, 
                    
            const char* session_pwd = NULL, long max_players = 0);

                BOOL disconnect();
                BOOL is_connected();

                BOOL send_data(DPNID player_id, 
            void* data, ulong size, ulong flags = 0);
                BOOL send_text(DPNID player_id, 
            char* text, ulong flags = 0);

                BOOL disconnect_player(
            long player_id);

                BOOL get_ip(
            char* ip, ulong player_id = 0);
                BOOL get_name(
            char* name, ulong player_id);
                
            long get_port();
                BOOL get_session_name(
            char* session_name);
                BOOL get_session_pwd(
            char* session_pwd);
                
            long get_max_players();
                
            long get_cur_players_num();    

            protected:
                
            static HRESULT WINAPI _network_msg_handler(PVOID user_context, DWORD msg_id, PVOID msg_buffer);

                
            virtual BOOL _add_player_to_group(DPNMSG_ADD_PLAYER_TO_GROUP* msg) { return TRUE; }
                
            virtual BOOL _async_op_complete(DPNMSG_ASYNC_OP_COMPLETE* msg) { return TRUE; }
                
            virtual BOOL _client_info(DPNMSG_CLIENT_INFO* msg) { return TRUE; }
                
            virtual BOOL _connect_complete(DPNMSG_CONNECT_COMPLETE* msg) { return TRUE; }
                
            virtual BOOL _create_group(DPNMSG_CREATE_GROUP* msg) { return TRUE; }
                
            virtual BOOL _create_player(DPNMSG_CREATE_PLAYER* msg) { return TRUE; }
                
            virtual BOOL _destroy_group(DPNMSG_DESTROY_GROUP* msg) { return TRUE; }
                
            virtual BOOL _destroy_player(DPNMSG_DESTROY_PLAYER* msg) { return TRUE; }
                
            virtual BOOL _enum_hosts_query(DPNMSG_ENUM_HOSTS_QUERY* msg) { return TRUE; }
                
            virtual BOOL _enum_hosts_response(DPNMSG_ENUM_HOSTS_RESPONSE* msg) { return TRUE; }
                
            virtual BOOL _group_info(DPNMSG_GROUP_INFO* msg) { return TRUE; }
                
            virtual BOOL _host_migrate(DPNMSG_HOST_MIGRATE* msg) { return TRUE; }
                
            virtual BOOL _indicate_connect(DPNMSG_INDICATE_CONNECT* msg) { return TRUE; }
                
            virtual BOOL _indicated_connect_aborted(DPNMSG_INDICATED_CONNECT_ABORTED* msg) { return TRUE; }
                
            virtual BOOL _peer_info(DPNMSG_PEER_INFO* msg) { return TRUE; }
                
            virtual BOOL _receive(DPNMSG_RECEIVE* msg) { return TRUE; }
                
            virtual BOOL _remove_player_from_group(DPNMSG_REMOVE_PLAYER_FROM_GROUP* msg) { return TRUE; }
                
            virtual BOOL _return_buffer(DPNMSG_RETURN_BUFFER* msg) { return TRUE; }
                
            virtual BOOL _send_complete(DPNMSG_SEND_COMPLETE* msg) { return TRUE; }
                
            virtual BOOL _server_info(DPNMSG_SERVER_INFO* msg) { return TRUE; }
                
            virtual BOOL _terminate_session(DPNMSG_TERMINATE_SESSION* msg) { return TRUE; }

            protected:
                IDirectPlay8Server* _server;
                BOOL                _is_connected;
                
            long                _port;

                
            char                _session_name[MAX_PATH];
                
            char                _session_pwd[MAX_PATH];

                
            long                _max_players;
                
            long                _cur_players_num;    
            } *NETWORK_SERVER_PTR;

            類的實現:
            //---------------------------------------------------------------------------------
            // Constructor, zero member data.
            //---------------------------------------------------------------------------------
            NETWORK_SERVER::NETWORK_SERVER()
            {
                memset(
            this, 0, sizeof(*this) - 4);
            }

            //---------------------------------------------------------------------------------
            // Destructor, shutdown server.
            //---------------------------------------------------------------------------------
            NETWORK_SERVER::~NETWORK_SERVER()
            {
                shutdown();
            }

            //---------------------------------------------------------------------------------
            // Shutdown server.
            //---------------------------------------------------------------------------------
            BOOL NETWORK_SERVER::shutdown()
            {
                
            if(! disconnect())
                    
            return FALSE;

                _server = NULL;
                
            return TRUE;
            }

            //---------------------------------------------------------------------------------
            // Disconnect server to all clients.
            //---------------------------------------------------------------------------------
            BOOL NETWORK_SERVER::disconnect()
            {
                
            if(_server)
                {
                    
            // closes the open connection to a session
                    if(FAILED(_server->Close(0)))
                        
            return FALSE;
                }

                _is_connected       = FALSE;
                _port               = 0;
                _session_name[0]    = NULL;
                _session_pwd[0]     = NULL;
                _max_players        = 0;
                _cur_players_num    = 0;

                
            return TRUE;
            }

            //---------------------------------------------------------------------------------
            // Return pointer to IDirectPlay8Server object.
            //---------------------------------------------------------------------------------
            IDirectPlay8Server* NETWORK_SERVER::get_server()
            {
                
            return _server;
            }

            //---------------------------------------------------------------------------------
            // Create IDirectPlay8Server object.
            //---------------------------------------------------------------------------------
            BOOL NETWORK_SERVER::init()
            {
                
            // shutdown old server object first
                shutdown();

                
            // create the server object
                if(FAILED(CoCreateInstance(CLSID_DirectPlay8Server, NULL, CLSCTX_INPROC, IID_IDirectPlay8Server,
                                           (
            void**) &_server)))
                {
                    
            return FALSE;
                }

                
            return TRUE;
            }

            //---------------------------------------------------------------------------------
            // Host server.
            //---------------------------------------------------------------------------------
            BOOL NETWORK_SERVER::host(const GUID* guid_adapter, long port, 
                                      
            const char* session_name, const char* session_pwd, long max_players)
            {
                
            // disconnect from current connection
                disconnect();

                _port = port;

                
            // error checking
                if(_server == NULL || session_name == NULL || port == 0)
                    
            return FALSE;

                
            // initialize the server object
                if(FAILED(_server->Initialize((PVOID)this, _network_msg_handler, 0)))
                    
            return FALSE;

                IDirectPlay8Address* dp_address;

                
            // create an address object
                if(FAILED(CoCreateInstance(CLSID_DirectPlay8Address, NULL, CLSCTX_INPROC, IID_IDirectPlay8Address,
                                           (
            void**) &dp_address)))
                    
            return FALSE;

                DPN_APPLICATION_DESC app_desc;
                WCHAR w_session_name[MAX_PATH], w_session_pwd[MAX_PATH];

                
            // set protocol
                if(FAILED(dp_address->SetSP(&CLSID_DP8SP_TCPIP)))
                    
            goto fail;

                
            // set the port - must not be 0
                if(FAILED(dp_address->AddComponent(DPNA_KEY_PORT, &_port, sizeof(DWORD), DPNA_DATATYPE_DWORD)))
                    
            goto fail;

                
            // set adapter (if any)
                if(guid_adapter)
                {
                    
            if(FAILED(dp_address->AddComponent(DPNA_KEY_DEVICE, guid_adapter, sizeof(GUID), DPNA_DATATYPE_GUID)))
                        
            goto fail;
                }

                
            // record the session name and password
                strcpy(_session_name, session_name);
                mbstowcs(w_session_name, session_name, MAX_PATH);

                
            if(session_pwd)
                {
                    strcpy(_session_pwd, session_pwd);
                    mbstowcs(w_session_pwd, session_pwd, MAX_PATH);
                }

                
            // setup the application description structure

                ZeroMemory(&app_desc, 
            sizeof(DPN_APPLICATION_DESC));

                app_desc.dwSize          = 
            sizeof(DPN_APPLICATION_DESC);
                app_desc.dwFlags         = DPNSESSION_CLIENT_SERVER;
                app_desc.guidApplication = g_app_guid;
                app_desc.pwszSessionName = w_session_name;

                
            if(session_pwd)
                {
                    app_desc.pwszPassword = w_session_pwd;
                    app_desc.dwFlags |= DPNSESSION_REQUIREPASSWORD;
                }

                _max_players = max_players;

                app_desc.dwMaxPlayers = max_players;

                
            // start the hosting
                if(FAILED(_server->Host(&app_desc, &dp_address, 1, NULL, NULL, NULL, 0)))
                    
            goto fail;

                dp_address->Release();
                _is_connected = TRUE;

                
            return TRUE;

            fail:
                dp_address->Release();
                
            return FALSE;
            }

            //---------------------------------------------------------------------------------
            // Check whether server is hosting now.
            //---------------------------------------------------------------------------------
            BOOL NETWORK_SERVER::is_connected()
            {
                
            return _is_connected;
            }

            //---------------------------------------------------------------------------------
            // Send data to client.
            //---------------------------------------------------------------------------------
            BOOL NETWORK_SERVER::send_data(DPNID player_id, void* data, ulong size, ulong flags)
            {
                
            // error checking
                if(_server == NULL || size == 0)
                    
            return FALSE;

                DPNHANDLE async_handle;
                DPN_BUFFER_DESC buf_desc;

                buf_desc.dwBufferSize = size;
                buf_desc.pBufferData  = (BYTE*) data;

                
            if(FAILED(_server->SendTo(player_id, &buf_desc, 1, 0, NULL, &async_handle, flags)))
                    
            return FALSE;

                
            return TRUE;
            }

            //---------------------------------------------------------------------------------
            // Send string to client.
            //---------------------------------------------------------------------------------
            BOOL NETWORK_SERVER::send_text(DPNID player_id, char* text, ulong flags)
            {
                
            // error checking
                if(_server == NULL || text == NULL)
                    
            return FALSE;

                
            return send_data(player_id, text, (ulong)strlen(text)+1, flags);
            }

            //---------------------------------------------------------------------------------
            // Disconnect specified player.
            //---------------------------------------------------------------------------------
            BOOL NETWORK_SERVER::disconnect_player(long player_id)
            {
                
            // error checking
                if(_server == NULL)
                    
            return FALSE;

                
            // try to disconnect the specified player
                if(FAILED(_server->DestroyClient(player_id, NULL, 0, 0)))
                    
            return FALSE;

                
            return TRUE;
            }

            //---------------------------------------------------------------------------------
            // Get ip address, include server and client ip address.
            //---------------------------------------------------------------------------------
            BOOL NETWORK_SERVER::get_ip(char* ip, ulong player_id)
            {
                
            // get host ip address if player_id == 0
                if(player_id == 0)
                {
                    WSADATA wsa_data;

                    
            // initiates use of ws2_32.dll by a process
                    if(WSAStartup(MAKEWORD(1, 0), &wsa_data))
                        
            return FALSE;

                    
            char local_name[MAX_PATH];

                    
            // retrieves the standard host name for the local computer
                    gethostname(local_name, MAX_PATH);

                    
            // store information about a given host, such as host name, ip address, and so forth.
                    HOSTENT* host_ent;

                    
            // retrieves host information corresponding to a host name from a host database
                    if((host_ent = gethostbyname(local_name)) == NULL)
                    {
                        
            // terminates use of the ws2_32.dll
                        WSACleanup();
                        
            return FALSE;
                    }

                    
            // get localip
                    char* local_ip = inet_ntoa(*(in_addr*)host_ent->h_addr_list[0]);

                    WSACleanup();

                    
            if(local_ip == NULL)
                        
            return FALSE;

                    strcpy(ip, local_ip);

                    
            return TRUE;
                }

                
            if(_server == NULL || !_is_connected)
                    
            return FALSE;

                IDirectPlay8Address* dp_address;

                
            // retrieve the player information
                if(FAILED(_server->GetClientAddress((DPNID)player_id, &dp_address, 0)))
                    
            return FALSE;

                DWORD size  = MAX_PATH;
                DWORD flags = DPNA_DATATYPE_STRING;

                WCHAR client_address[MAX_PATH];

                
            // retrieves information on the component at the specified key
                if(FAILED(dp_address->GetComponentByName(DPNA_KEY_HOSTNAME, client_address, &size, &flags)))
                {
                    dp_address->Release();
                    
            return FALSE;
                }

                wcstombs(ip, client_address, MAX_PATH);

                dp_address->Release();
               
                
            return TRUE;
            }

            //---------------------------------------------------------------------------------
            // Get player name.
            //---------------------------------------------------------------------------------
            BOOL NETWORK_SERVER::get_name(char* name, ulong player_id)
            {
                
            if(_server == NULL)
                    
            return FALSE;

                DPN_PLAYER_INFO* player_info = NULL;
                DWORD size = 0;

                
            // retrieves the client information set for the specified client

                HRESULT rv = _server->GetClientInfo(player_id, player_info, &size, 0);

                
            if(FAILED(rv) && rv != DPNERR_BUFFERTOOSMALL)
                    
            return FALSE;

                player_info = (DPN_PLAYER_INFO*) 
            new char[size];
                player_info->dwSize = 
            sizeof(DPN_PLAYER_INFO);

                
            if(FAILED(_server->GetClientInfo(player_id, player_info, &size, 0)))
                {
                    delete[] player_info;
                    
            return FALSE;
                }

                wcstombs(name, player_info->pwszName, MAX_PATH);
                delete[] player_info;

                
            return TRUE;
            }

            //---------------------------------------------------------------------------------
            // Return port of server.
            //---------------------------------------------------------------------------------
            long NETWORK_SERVER::get_port()
            {
                
            return _port;
            }

            //---------------------------------------------------------------------------------
            // Get session name.
            //---------------------------------------------------------------------------------
            BOOL NETWORK_SERVER::get_session_name(char* session_name)
            {
                
            if(session_name == NULL)
                    
            return FALSE;

                strcpy(session_name, _session_name);
                
            return TRUE;
            }

            //---------------------------------------------------------------------------------
            // Get session password.
            //---------------------------------------------------------------------------------
            BOOL NETWORK_SERVER::get_session_pwd(char* session_pwd)
            {
                
            if(session_pwd == NULL)
                    
            return FALSE;

                strcpy(session_pwd, _session_pwd);
                
            return TRUE;
            }

            //---------------------------------------------------------------------------------
            // Return max players permitted to login.
            //---------------------------------------------------------------------------------
            long NETWORK_SERVER::get_max_players()
            {
                
            return _max_players;
            }

            //---------------------------------------------------------------------------------
            // Return current player number.
            //---------------------------------------------------------------------------------
            long NETWORK_SERVER::get_cur_players_num()
            {
                
            return _cur_players_num;
            }

            //---------------------------------------------------------------------------------
            // Callback function to handler message for server.
            //---------------------------------------------------------------------------------
            HRESULT WINAPI NETWORK_SERVER::_network_msg_handler(PVOID user_context, DWORD msg_id, PVOID msg_buffer)
            {
                NETWORK_SERVER* server;

                
            if((server = (NETWORK_SERVER*) user_context) == NULL)
                    
            return E_FAIL;

                
            // pass message to user-defined function

                
            switch(msg_id)
                {
                
            case DPN_MSGID_ADD_PLAYER_TO_GROUP:
                    
            if(server->_add_player_to_group((DPNMSG_ADD_PLAYER_TO_GROUP*) msg_buffer))
                        
            return S_OK;        
                    
            break;

                
            case DPN_MSGID_ASYNC_OP_COMPLETE:
                    
            if(server->_async_op_complete((DPNMSG_ASYNC_OP_COMPLETE*) msg_buffer))
                        
            return S_OK;
                    
            break;

                
            case DPN_MSGID_CLIENT_INFO:
                    
            if(server->_client_info((DPNMSG_CLIENT_INFO*) msg_buffer))
                        
            return S_OK;
                    
            break;

                
            case DPN_MSGID_CONNECT_COMPLETE:
                    
            if(server->_connect_complete((DPNMSG_CONNECT_COMPLETE*) msg_buffer))
                        
            return S_OK;
                    
            break;

                
            case DPN_MSGID_CREATE_GROUP:
                    
            if(server->_create_group((DPNMSG_CREATE_GROUP*) msg_buffer))
                        
            return S_OK;
                    
            break;

                
            case DPN_MSGID_CREATE_PLAYER:
                    
            if(server->_create_player((DPNMSG_CREATE_PLAYER*) msg_buffer))
                        
            return S_OK;
                    
            break;

                
            case DPN_MSGID_DESTROY_GROUP:
                    
            if(server->_destroy_group((DPNMSG_DESTROY_GROUP*) msg_buffer))
                        
            return S_OK;
                    
            break;

                
            case DPN_MSGID_DESTROY_PLAYER:
                    
            if(server->_destroy_player((DPNMSG_DESTROY_PLAYER*) msg_buffer))
                        
            return S_OK;
                    
            break;

                
            case DPN_MSGID_ENUM_HOSTS_QUERY:
                    
            if(server->_enum_hosts_query((DPNMSG_ENUM_HOSTS_QUERY*) msg_buffer))
                        
            return S_OK;
                    
            break;

                
            case DPN_MSGID_ENUM_HOSTS_RESPONSE:
                    
            if(server->_enum_hosts_response((DPNMSG_ENUM_HOSTS_RESPONSE*) msg_buffer))
                        
            return S_OK;
                    
            break;

                
            case DPN_MSGID_GROUP_INFO:
                    
            if(server->_group_info((DPNMSG_GROUP_INFO*) msg_buffer))
                        
            return S_OK;
                    
            break;

                
            case DPN_MSGID_HOST_MIGRATE:
                    
            if(server->_host_migrate((DPNMSG_HOST_MIGRATE*) msg_buffer))
                        
            return S_OK;
                    
            break;

                
            case DPN_MSGID_INDICATE_CONNECT:
                    
            if(server->_indicate_connect((DPNMSG_INDICATE_CONNECT*) msg_buffer))
                        
            return S_OK;
                    
            break;

                
            case DPN_MSGID_INDICATED_CONNECT_ABORTED:
                    
            if(server->_indicated_connect_aborted((DPNMSG_INDICATED_CONNECT_ABORTED*) msg_buffer))
                        
            return S_OK;
                    
            break;

                
            case DPN_MSGID_PEER_INFO:
                    
            if(server->_peer_info((DPNMSG_PEER_INFO*) msg_buffer))
                        
            return S_OK;
                    
            break;

                
            case DPN_MSGID_RECEIVE:
                    
            if(server->_receive((DPNMSG_RECEIVE*) msg_buffer))
                        
            return S_OK;
                    
            break;

                
            case DPN_MSGID_REMOVE_PLAYER_FROM_GROUP:
                    
            if(server->_remove_player_from_group((DPNMSG_REMOVE_PLAYER_FROM_GROUP*) msg_buffer))
                        
            return S_OK;
                    
            break;

                
            case DPN_MSGID_RETURN_BUFFER:
                    
            if(server->_return_buffer((DPNMSG_RETURN_BUFFER*) msg_buffer))
                        
            return S_OK;
                    
            break;

                
            case DPN_MSGID_SEND_COMPLETE:
                    
            if(server->_send_complete((DPNMSG_SEND_COMPLETE*) msg_buffer))
                        
            return S_OK;
                    
            break;

                
            case DPN_MSGID_SERVER_INFO:
                    
            if(server->_server_info((DPNMSG_SERVER_INFO*) msg_buffer))
                        
            return S_OK;
                    
            break;

                
            case DPN_MSGID_TERMINATE_SESSION:
                    
            if(server->_terminate_session((DPNMSG_TERMINATE_SESSION*) msg_buffer))
                        
            return S_OK;
                    
            break;
                }

                
            return E_FAIL;
            }

            要使用NETWORK_SERVER類,需要將NETWORK_SERVER類作為基類派生出自己的類。這樣做的原因在于必須重載網絡處理函數以滿足自己具體的功能需要,NETWORK_SERVER類中包含了每條網絡消息,因此派生出來的類絕不會丟失重要的消息。

            要主持游戲會話,需要適配器GUID、會話名稱、可選的密碼以及允許的玩家最大數目(使用0表示不限制玩家數目)。當調用NETWORK_SERVER::host函數時,DirectPlay就會初始化連接并將控制權返回給使用者。現在,假設有消息發過來,那么就應該接收發送過來的消息并在合適的時候處理它們。創建的每個消息處理函數,如果返回TRUE就表明消息被成功處理,FALSE表明有錯誤發生。

            接著我們編寫一段代碼來測試NETWORK_SERVER類:

            /********************************************************************************
            PURPOSE:
                Test for class NETWORK_SERVER.
            ********************************************************************************/


            #include "Core_Global.h"

            #define HOST_PORT   12345

            class SERVER : public NETWORK_SERVER
            {
            private:
                
            virtual BOOL _receive(DPNMSG_RECEIVE* msg)
                {
                    MessageBox(NULL, LPCSTR(msg->pReceiveData), "client message", MB_OK);

                    
            // send data back to client
                    return send_data(msg->dpnidSender, msg->pReceiveData, msg->dwReceiveDataSize, DPNSEND_GUARANTEED);
                }
            };

            class APP : public APPLICATION
            {
            public:
                BOOL init()
                {
                    
            if(! _adapter.init())
                        
            return FALSE;

                    
            // get first adapter guid
                    GUID* guid_adapter = _adapter.get_guid(0);

                    
            if(! _server.init())
                        
            return FALSE;

                    
            if(! _server.host(guid_adapter, HOST_PORT, "test_session"))
                        
            return FALSE;

                    
            return TRUE;
                }

                BOOL frame()
                {
                    Sleep(300);

                    
            return TRUE;
                }

                BOOL shutdown()
                {
                    
            return TRUE;
                }

            private:
                SERVER _server;
                NETWORK_ADAPTER _adapter;
            };

            int WINAPI WinMain(HINSTANCE inst, HINSTANCE, LPSTR cmd_line, int cmd_show)
            {
                APP app;

                
            return app.run();
            }
             

            posted on 2007-10-14 00:12 lovedday 閱讀(404) 評論(0)  編輯 收藏 引用

            公告

            導航

            統計

            常用鏈接

            隨筆分類(178)

            3D游戲編程相關鏈接

            搜索

            最新評論

            天天爽天天爽天天片a久久网| 国内精品综合久久久40p| 久久伊人精品青青草原高清| 国产V亚洲V天堂无码久久久| 日本道色综合久久影院| 欧美激情一区二区久久久| 久久国产色AV免费观看| 麻豆国内精品久久久久久| 久久天天躁狠狠躁夜夜躁2O2O| 久久青草国产手机看片福利盒子| 久久久久久一区国产精品| 99麻豆久久久国产精品免费| 一本久久综合亚洲鲁鲁五月天亚洲欧美一区二区 | 久久热这里只有精品在线观看| 欧美精品久久久久久久自慰| 精品欧美一区二区三区久久久 | 区亚洲欧美一级久久精品亚洲精品成人网久久久久 | 精品多毛少妇人妻AV免费久久| 亚洲午夜久久久久久噜噜噜| 久久亚洲天堂| 久久国产香蕉视频| 91精品国产91热久久久久福利| 亚洲欧美日韩久久精品第一区| 久久久久久A亚洲欧洲AV冫 | 亚洲中文字幕无码久久2017| 久久免费香蕉视频| 国产精品嫩草影院久久| 久久九九亚洲精品| 嫩草影院久久国产精品| 精品999久久久久久中文字幕| 久久久久亚洲AV无码网站| 亚洲精品无码久久千人斩| 久久精品青青草原伊人| 波多野结衣久久一区二区 | 国产成人无码久久久精品一| 久久人人爽人人爽人人片AV不| 免费一级做a爰片久久毛片潮| 久久久久国色AV免费看图片| 热RE99久久精品国产66热| 亚洲v国产v天堂a无码久久| 亚洲国产精品无码久久九九|