




已閱讀5頁,還剩6頁未讀, 繼續(xù)免費閱讀
版權(quán)說明:本文檔由用戶提供并上傳,收益歸屬內(nèi)容提供方,若內(nèi)容存在侵權(quán),請進行舉報或認領(lǐng)
文檔簡介
/5/1700005.shtmlUnix和Windows跨系統(tǒng)通訊編程2003-05-24 00:00 作者: 張勤李華春 出處: 天極論壇 責任編輯:方舟 摘要本文介紹了套接字(Socket)的基本概念及編程技術(shù),并結(jié)合實例說明在Unix和Windows下如何用套接字實現(xiàn)客戶/服務(wù)器方式的通訊編程。關(guān)鍵詞Berkeley Sockets Windows Sockets 通訊編程 一、 前言隨著Internet的不斷發(fā)展,客戶機/服務(wù) 器模型得到了廣泛的應(yīng)用??蛻魬?yīng)用程序向服務(wù)器程序請求服務(wù)。一個服務(wù)程序通常在一個眾所周知的端口監(jiān)聽對服務(wù)的請求,也就是說,服務(wù)進程一直處于休眠狀 態(tài),直到一個客戶對這個服務(wù)的地址提出了連接請求。在這個時刻,服務(wù)程序被“驚醒”并且為客戶提供服務(wù)或?qū)蛻舻恼埱笞鞒鲞m當?shù)姆磻?yīng)。 Unix最早是由美國貝爾實驗室發(fā)明的一種多用戶、多任務(wù)的通用操作系統(tǒng),由于 Unix具有技術(shù)成熟、可靠性高、網(wǎng)絡(luò)和數(shù)據(jù)庫功能強、伸縮性突出和開發(fā)性好的特色,可滿足各行各業(yè)的實際需要,特別是企業(yè)重要業(yè)務(wù)的需要,已經(jīng)成為主要 的工作平臺和重要的企業(yè)操作平臺,而微軟公司的 Windows操作系統(tǒng)用戶界面友好,安裝、使用也比較方便,應(yīng)用軟件豐富,在個人PC機上成為主流操作系統(tǒng)。因此服務(wù)端使用 Unix,客戶端使用 Windows能充分利用它們各自的優(yōu)點,這也是今后發(fā)展的一個趨勢。同時,金融行業(yè)的中間業(yè)務(wù)發(fā)展迅速,銀行主機采用 Unix系統(tǒng),而各代收代付單位多采用 Windows或 WindowsNT系統(tǒng),在它們之間傳輸數(shù)據(jù)文件也涉及跨系統(tǒng)通訊的問題,通過套接字 Socket能方便地實現(xiàn) Unix和 Windows的跨系統(tǒng)通訊,本文擬就這一問題作一探討。二、 SOCKET簡介TCP/IP是計算機互連最常使用的網(wǎng)絡(luò)通訊協(xié)議, TCP/IP的核心部分由網(wǎng)絡(luò)操作系統(tǒng)的內(nèi)核實現(xiàn),應(yīng)用程序通過編程接口來訪問 TCP/IP,見下圖:圖1應(yīng)用程序與Windows Socket關(guān)系圖七十年代中,美國國防部高研署(DARPA)將TCP/IP的軟件提供給加利福尼亞大學(xué)Berkeley分校后, TCP/IP很快被集成到Unix中,同時出現(xiàn)了許多成熟的TCP/IP應(yīng)用程序接口(API)。這個API稱為Socket接口。今天,SOCKET接 口是TCP/IP網(wǎng)絡(luò)最為通用的API,也是在INTERNET上進行應(yīng)用開發(fā)最為通用的API。九十年代初,由Microsoft 聯(lián)合了其他幾家公司共同制定了一套WINDOWS下的網(wǎng)絡(luò)編程接口,即Windows Sockets規(guī)范。它是Berkeley Sockets的重要擴充,主要是增加了一些異步函數(shù),并增加了符合 Windows 消息驅(qū)動特性的網(wǎng)絡(luò)事件異步選擇機制。 Windows Sockets規(guī)范是一套開放的、支持多種協(xié)議的 Windows下的網(wǎng)絡(luò)編程接口。目前,在實際應(yīng)用中的Windows Sockets規(guī)范主要有1.1版和2.0版。兩者的最重要區(qū)別是1.1版只支持TCP/IP協(xié)議,而2.0版可以支持多協(xié)議,2.0版有良好的向后兼容 性,目前,Windows下的Internet軟件都是基于 WinSock開發(fā)的。Socket實際在計算機中提供了一個通信端 口,可以通過這個端口與任何一個具有Socket接口的計算機通信。應(yīng)用程序在網(wǎng)絡(luò)上傳輸,接收的信息都通過這個Socket接口來實現(xiàn)。在應(yīng)用開發(fā)中就 像使用文件句柄一樣,可以對 Socket句柄進行讀、寫操作。我們將 Socket翻譯為套接字,套接字分為以下三種類型:字節(jié)流套接字(Stream Socket)是最常用的套接字類型,TCP/IP協(xié)議族中的 TCP 協(xié)議使用此類接口。字節(jié)流套接口提供面向連接的(建立虛電路)、無差錯的、發(fā)送先后順序一致的、無記錄邊界和非重復(fù)的網(wǎng)絡(luò)信包傳輸。 數(shù)據(jù)報套接字 (Datagram Socket)TCP/IP協(xié)議族中的UDP協(xié)議使用此類接口,它是無連接的服務(wù),它以獨立的信包進行網(wǎng)絡(luò)傳輸,信包最大長度為32KB,傳輸不保證順 序性、可靠性和無重復(fù)性,它通常用于單個報文傳輸或可靠性不重要的場合。數(shù)據(jù)報套接口的一個重要特點是它保留了記錄邊界。對于這一特點。數(shù)據(jù)報套接口采用 了與現(xiàn)在許多包交換網(wǎng)絡(luò)(例如以太網(wǎng))非常類似的模型。原始數(shù)據(jù)報套接字(Raw Socket)提供對網(wǎng)絡(luò)下層通訊協(xié)議(如IP協(xié)議)的直接訪問,它一般不是提供給普通用戶的,主要用于開發(fā)新的協(xié)議或用于提取協(xié)議較隱蔽的功能。三、 基于SOCKET的應(yīng)用開發(fā)圖2面向連接協(xié)議的SOCKET編程模型面向連接協(xié)議的SOCKET編程模型應(yīng)用最為廣泛,因為面向連接協(xié)議提供了一系列的數(shù)據(jù)糾錯功能,可以保證在網(wǎng)絡(luò)上傳輸?shù)臄?shù)據(jù)及時、無誤地到達對方?;谶B接協(xié)議(字節(jié)流套接字)的服務(wù)是設(shè)計客戶機/服務(wù)器應(yīng)用程序時的標準,其編程模型如下: 盡管Windows Sockets和Berkeley Sockets都是TCP/IP應(yīng)用程序的編程接口,但二者由于分屬不同的系統(tǒng),在某些環(huán)節(jié)仍有一些差別。Windows Sockets API沒有嚴格地堅持Berkeley傳統(tǒng)風格,通常這么做是因為在Windows環(huán)境中實現(xiàn)的難度。1.套接口數(shù)據(jù)類型和錯誤數(shù)值 Windows Sockets規(guī)范中定義了一個新的數(shù)據(jù)類型 SOCKET,這一類型的定義對于將來Windows Sockets規(guī)范的升級是必要的。這一類型的定義保證了應(yīng)用程序向Win32 環(huán)境的可移植性。因為這一類型會自動地從16位升級到32位。 在 UNIX中,所有句柄包括套接口句柄,都是非負的短整數(shù)。 Windows Sockets 句柄則沒有這一限制,除了INVALID_SOCKET 不是一個有效的套接口外,套接口可以取從0到 INVALID_SOCKET1 之間的任意值。因為 SOCKET 類型是unsigned ,所以編譯已經(jīng)存在于UNIX 環(huán)境中的應(yīng)用程序的源代碼可能會導(dǎo)致 signed/unsigned 數(shù)據(jù)類型不匹配的警告。因此,在socket() 例程和accept() 例程返回時,檢查是否有錯誤發(fā)生就不應(yīng)該再使用把返回值和1比較的方法,或判斷返回值是否為負(這兩種方法在BSD 中都是很普通,很合法的途徑)。取而代之的是,一個應(yīng)用程序應(yīng)該使用常量INVALID_SOCKET ,該常量已在WINSOCK.H 中定義。例如:BDS 風格m_hSocket=socket();if(m_hSocket=1) /or m_hSocket0/Windows風格:m_hSocket=socket();if(m_hSocket=INVALID_SOCKET)2.select() 函數(shù)和FD_宏由于一個套接口不再表示為非負的整數(shù),select() 函數(shù)在Windows Sockets API中的實現(xiàn)有一些變化:每一組套接口仍然用 fd_set 類型來代表,但是它并不是一個位掩碼。typedef struct fd_setu_int fd_count;SOCKET fd_array; fd_set;整個組的套接口是用了一個套接口的數(shù)組來實現(xiàn)的。為了避免潛在的危險,應(yīng)用程序應(yīng)該堅持用FD_XXX 宏(FD_SET,FD_ZERO,FD_CLR,FD_ISSET)來設(shè)置,初始化,清除和檢查fd_set 結(jié)構(gòu)。3.錯誤代碼errno,h_errno,WSAGetLastError() Windows Sockets 實現(xiàn)所設(shè)置的錯誤代碼是無法通過 errno 變量得到的。另外對于 getXbyY() 這一類的函數(shù),錯誤代碼無法從h _errno 變量得到,錯誤代碼可以使用 WSAGetLastError()調(diào)用得到,這種改進是為了適應(yīng)多線程程序設(shè)計的需要。WSAGetLastError()允許程序員能夠得到對應(yīng)于每 一線程的最近的錯誤代碼。為了保持與 BSD 的兼容性,應(yīng)用程序可以加入以下一行代碼:define errno WSAGetLastError() 這就保證了用全程的 errno 變量所寫的網(wǎng)絡(luò)程序代碼在單線程環(huán)境中可以正確使用。當然,這樣做有許多明顯的缺點:如果一個源程序?qū)μ捉涌诤头翘捉涌诤瘮?shù)都用 errno 變量來檢查錯誤,那么這種機制將無法工作。此外,一個應(yīng)用程序不可能為 errno 賦一個新的值 (在Windows Sockets 中,WSAGetLastError()函數(shù)可以做到這一點)。例如:BSD風格:retcode=recv();if(retcode=1 errno=EWOULDBLOCK)Windows風格:retcode=recv();if(retcode=1 WSAGetLastError ()=EWOULDBLOCK)雖然為了兼容性原因,錯誤常量與4.3BSD 所提供的一致:應(yīng)用程序應(yīng)該盡可能地使用“WSA”系列錯誤代碼定義。且常量 SOCKET_ERROR是被用來檢查API 調(diào)用失敗的。雖然對這一常量的使用并不是強制性的,但這是推薦的。例如,上面程序更準確應(yīng)該是:retcode=recv()if(retcode=SOCKET_ERRORWSAGetLastError()=WSAEWOULDBLOCK)4.重命名的函數(shù)有兩種原因Berkeley 套接口中的函數(shù)必須重命名,以避免與其他的 API 沖突: close() 和closesocket() 在Berkeley套接口中,套接口出現(xiàn)的形式與標準文件描述字相同,所以 close() 函數(shù)可以用來象關(guān)閉文件一樣關(guān)閉套接口。在Windows Sockets API中,套接字和正常文件句柄不是等同的,例如read(),write() 和close() 在應(yīng)用于套接口后不能保證正確工作。套接口必須使用 closesocket()例程來關(guān)閉,用close() 例程來關(guān)閉套接口是不正確的。 ioctl()和iooctlsocket()Windows Sockets定義ioctlsocket() 例程,用于實現(xiàn) BSD中ioctl() 和fcntl() 的功能。5.阻塞例程和 EINPROGRESS 宏 雖然Windows Sockets 支持關(guān)于套接口的阻塞操作,但是這種應(yīng)用是被強烈反對的,如果程序員被迫使用阻塞模式(例如一個準備移植的已有的程序,BSD 包含了大量的阻塞函數(shù),且其默認的工作方式都是阻塞的),那么他應(yīng)該清楚地知道Windows Sockets中阻塞操作的語義。6.指針所有應(yīng)用程序與Windows Sockets 使用的指針都必須是FAR 指針,為了方便應(yīng)用程序開發(fā)者作用,Windows Sockets規(guī)范定義了數(shù)據(jù)類型LPHOSTENT 。7. Windows Sockets支持的最大套接口數(shù)目 一個Windows Sockets 應(yīng)用程序可以使用的套接口的最大數(shù)目是在編譯時由常量 FD_SETSIZE 決定的。這個常量在 select() 函數(shù)中被用來組建fd_set 結(jié)構(gòu)。在WINSOCK.H 中缺省值是64。如果一個應(yīng)用程序希望能夠使用超過64個套接口,則編程人員必須在每一個源文件包含 WINSOCK.H 前定義確切的FD_SETSIZE值。有一種方法可以完成這項工作,就是在工程項目文件中的編譯器選項上加入這一定義。 FD_SETSIZE定義的值對Windows Sockets實現(xiàn)所支持的套接口的數(shù)目并無任何影響。8.頭文件 為了方便基于 Berkeley 套接口的已有的源代碼的移植, Windows Sockets支持許多 Berkeley頭文件。這些 Berkeley頭文件被包含在WINSOCK.H中。所以一個 Windows Sockets應(yīng)用程序只需簡單的包含 WINSOCK.H就足夠了(這也是一種被推薦使用的方法)。四、 跨系統(tǒng)通訊編程實例下面通過一個實例來具體說明 Socket 在Unix 和Windows 跨系統(tǒng)通訊編程中的應(yīng)用。 Internet 上可以提供一種叫 IRC(Internet Relay Chatting,Internet 在線聊天系統(tǒng))的服務(wù)。使用者通過客戶端的程序登錄到 IRC 服務(wù)器上,就可以與登錄在同一 IRC 服務(wù)器上的客戶進行交談,這也就是平常所說的聊天室。在這里,給出了一個在運行 TCP/IP 協(xié)議的網(wǎng)絡(luò)上實現(xiàn) IRC 服務(wù)的程序。其中,服務(wù)器運行在 SCO Open Server 5.0.5上,客戶端運行在Windows98 或 Windows NT 上。在一臺計算機上運行服務(wù)端程序,同一網(wǎng)絡(luò)的其他計算機上運行客戶端程序,登錄到服務(wù)器上,各個客戶之間就可以聊天了。1.服務(wù)端 服務(wù)端用名為 client 的整型數(shù)組記錄每個客戶的已連接套接口描述字,此數(shù)組中的所有元素都初始化為1。同時服務(wù)器既要處理監(jiān)聽套接口,又要處理所有已連接套接口,因此需要用 到 I/O 復(fù)用。通過 select 函數(shù)內(nèi)核等待多個事件中的任一個發(fā)生,并僅在一個或多個事件發(fā)生或經(jīng)過某指定時間后才喚醒進程。維護一個讀描述字集 rset ,當有客戶到達時,在數(shù)組 client中的第一個可用條目(即值為1的第一個條目)記錄其已連接套接口的描述字。同時把這個已連接描述字加到讀描述字集rset 中,變量maxi 是當前使用的數(shù)組 client 的最大下標,而變量 maxfd1 是函數(shù) select第一個參數(shù)的當前值。如下圖:(假設(shè)有兩個客戶與服務(wù)器建立連接) 圖3第二客戶建立連接后的TCP服務(wù)器圖4第二客戶建立連接后的數(shù)據(jù)結(jié)構(gòu)服務(wù)器在指定的端口上監(jiān)聽,每當一個套接口接收到信息,都將會把接收到的信息發(fā)送給每一個Client 。其主要源程序如下:int main(int argc,char argv)int i,maxi,maxfd,listenfd,connfd,sockfd;int nready,client;ssize_t n;fd_set rset,allset;char lineMAXLNE;socklen_t clilen;struct sockaddr_in cliaddr,servaddr;const int on=1;listenfd=Socket(AF_INET,SOCK_STREAM,0);if(setsockopt (listenfd,SOL_SOCKET,SO_REUSEADDR,on,sizeof(on)=1);err_ret(setsockopt error);bzero(servaddr,sizeof(servaddr);servaddr.sin_family =AF_INET;servaddr.sin_addr.s_addr =htonl(INADDR_ANY);servaddr.sin_port =htons(SERV_PORT);Bind(listenfd,(SA)servaddr,sizeof(servaddr);Listen(listenfd,LISTENQ);maxfd =listenfd; /初始化maxi = 1; /最大下標for(i=0;iFD_SETSIZE;i)clienti=1; /1表示可用FD_ZERO(allset);FD_SET(listenfd,allset);for(;)rset=allset; /結(jié)構(gòu)賦值 nready=Select(maxfd1,rset,NULL,NULL,NULL); if (FD_ISSET(listenfd,rset) /有用戶連接clilen=sizeof(cliaddr); connfd=Accept(listenfd,(SA)cliaddr,clilen);for(i=0;iFD_SETSIZE;i) if(clienti 0 clienti=connfd;/保存套接字 break;if(i=FD_SETSIZE); err_quit(too many clients);FD_SET (connfd,allset); /增加套接字到讀描述字集if(connfdmaxfd);maxfd=connfd;if(imaxi);maxi=i;if(nready=0)continue;for (i=0;i=maxi;i) /檢查所有用戶連接 if(sockfd=clienti)0)continue; if(FD_ISSET(sockfd,rset)if(n=Readline(sockfd,line,MAXLINE)=0)/用戶關(guān)閉連接 Close(sockfd); FD_CLR(sockfd,allset); clienti=1 elseprintf(s,line);Broadcast(client,maxi,line,n);if (nready=0)break;/將聊天內(nèi)容發(fā)送到所有已連接的用戶Broadcast(int client,int maxi,charstr,size_t n)int i;int sockfd;for(i=0;imaxi,i) if(sockfd=clienti)0) continue;writen(sockfd,str,n);2.客戶端 為了實現(xiàn)非阻塞通信,利用異步選擇函數(shù) WSAAsynSelect() 將網(wǎng)絡(luò)事件與 WinSock 消息聯(lián)系起來,由該函數(shù)注冊一些用戶感興趣的網(wǎng)絡(luò)事件(如接收緩沖區(qū)滿,允許發(fā)送數(shù)據(jù),請求連接等)。當這些被注冊的網(wǎng)絡(luò)事件發(fā)生時,應(yīng)用程序的相應(yīng)函數(shù) 將接收到有關(guān)消息。應(yīng)用程序在使用Windows Sockets DLL 之前必須先調(diào)用啟動函數(shù)WSAStartup() ,該函數(shù)的功能有兩點:一是由應(yīng)用程序指定所要求Windows Sockets DLL 版本;二是獲得系統(tǒng) Windows Sockets DLL的一些技術(shù)細節(jié)。每一個WSAStartup()函數(shù)必須和一個WSACleanup()函數(shù)對應(yīng),當應(yīng)用程序終止時,必須調(diào)用 WSACleanup()將自己從OLL 中注銷??蛻舳顺绦蛴?VC6.0在Windows98 操作系統(tǒng)下設(shè)計,程序主框架由 AppWizard 生成,客戶端核心代碼在 CTalkDialog類中。只有一個 socket 變量m_hSocket 與服務(wù)端進行連接。連接建立好后,通過此 SOCKET發(fā)送和接收信息。手工加入CTalkDialog:OnSockConnect() ,完成基本套接字編程,為客戶程序申請一個套接字,并將該套接字與指定服務(wù)器綁定,然后向服務(wù)器發(fā)出連接請求,啟動異步選擇函數(shù)等待服務(wù)器的響應(yīng)。void CTalkDialog:OnSockConnect() struct sockaddr_in servaddr; WSADATA wsaData; if(WSAStartup(WINSOCK_VERSION,wsaData); MessageBox(Could not load Windows Sockets DLL,NULL,MB_Ok); return;m_hSocket=socket(AF_INET,SOCK_STREAM,0); memset(servaddr,0,sizeof(servaddr); servaddr.sin_family=AF_INET; /m_iPort,m_csIP 為通過注冊對話框返回的端口號和IP地址servaddr.sin_port=htons(m_iport);servaddr.sin_addr.S_un.S_addr=inet_addr(m_csIP);if (connect(m_hSocket,(SA) servaddr,sizeof (servaddr)!=0) AfxMessageBox(連接服務(wù)器失敗!);GetDlgItem(IDC_BUTTON_OUT)EnableWindow(FALSE); GetDlgItem(IDC_BUTTON_IN)EnableWindowTRUE GetDlgItem(IDC_EDITSEND)EnableWindow(FALSE); UpdateData(FALSE);else GetDlgItem(IDC_BUTTON_IN)EnableWindow(FALSE);GetDlgItem(IDC_BUTTON_OUT)EnableWindow(TRUE); GetDlgItem(IDC_EDITSEND)EnableWindow(TRUE); int iErrorCode=WSAAsyncSelect(m_hSocket,m_hWnd,WM_SOCKET_READ, FD_READ); if(iErrorCode=SOCKET_ERROR)MessageBox(WSAAsyncSelect failed on socket,NULL,MB_OK);手工加入CTalkDialog:OnSockRead(), 響應(yīng)WinSock 發(fā)來的消息LRESULT CTalkDialog:OnSockRead(WPARAM wParamLPARAM 1Param)int iRead;int iBufferLength;int iEnd;int iSpaceRemain;char chIncomingData100;iBufferLength=iSpaceRemain=sizeof (chIncomingData); iEnd=0; iSpaceRemain=iEnd; iRead=recv(m_hSocket,(LPSTR)(chIncomingDataiEnd),iSpaceRemain,0); iEnd=iRead; if (iRead=SOCKET_ERROR) AfxMessageBox(接收數(shù)據(jù)錯誤!); chIncomingDataiEnd=0; if(lstrlen(chIncomingData)!=0)m_csRecv=m_csRecvchIncomingData;GetDlgItem(IDC_EDIT_RECV)SetWindowText(LPCSTR)m_csRecv);CEdit
溫馨提示
- 1. 本站所有資源如無特殊說明,都需要本地電腦安裝OFFICE2007和PDF閱讀器。圖紙軟件為CAD,CAXA,PROE,UG,SolidWorks等.壓縮文件請下載最新的WinRAR軟件解壓。
- 2. 本站的文檔不包含任何第三方提供的附件圖紙等,如果需要附件,請聯(lián)系上傳者。文件的所有權(quán)益歸上傳用戶所有。
- 3. 本站RAR壓縮包中若帶圖紙,網(wǎng)頁內(nèi)容里面會有圖紙預(yù)覽,若沒有圖紙預(yù)覽就沒有圖紙。
- 4. 未經(jīng)權(quán)益所有人同意不得將文件中的內(nèi)容挪作商業(yè)或盈利用途。
- 5. 人人文庫網(wǎng)僅提供信息存儲空間,僅對用戶上傳內(nèi)容的表現(xiàn)方式做保護處理,對用戶上傳分享的文檔內(nèi)容本身不做任何修改或編輯,并不能對任何下載內(nèi)容負責。
- 6. 下載文件中如有侵權(quán)或不適當內(nèi)容,請與我們聯(lián)系,我們立即糾正。
- 7. 本站不保證下載資源的準確性、安全性和完整性, 同時也不承擔用戶因使用這些下載資源對自己和他人造成任何形式的傷害或損失。
最新文檔
- 《官疾病的影像診斷》課件
- 2025合同買賣協(xié)議書
- 浙江國企招聘2025臺州灣新區(qū)招聘8人筆試參考題庫附帶答案詳解
- 2025版權(quán)許可使用合同范本
- 樂器零售商財務(wù)管理考核試卷
- 水電工程科技創(chuàng)新與成果轉(zhuǎn)化考核試卷
- 學(xué)前教育的幼兒表演與展示考核試卷
- 管道工程國際標準比較與借鑒考核試卷
- 硅冶煉廠的智能化生產(chǎn)調(diào)度考核試卷
- 汽輪機設(shè)備狀態(tài)監(jiān)測與預(yù)測性維護考核試卷
- 2025購銷合同(電子產(chǎn)品)范文
- 基于全生命周期的綠色建筑成本影響因素研究
- 2025年普法知識競賽題庫及答案(共80題)
- 心力衰竭護理查房 課件
- 【課時練基礎(chǔ)作業(yè)】人教版四年級數(shù)學(xué)下冊第四單元《期中計算能力測試》(含答案)
- 樹木修剪合同協(xié)議
- 2025年蘭州市九年級診斷考試(一診)物理試卷
- 【初中地理】西亞課件-2024-2025學(xué)年人教版(2024)七年級地理下冊
- 2024年4月27日福建省事業(yè)單位《綜合基礎(chǔ)知識》真題及答案
- 農(nóng)民工工資專用賬戶管理制度
- 藥物治療管理MTM
評論
0/150
提交評論