欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

QT基于TCP實(shí)現(xiàn)網(wǎng)絡(luò)聊天室程序

 更新時(shí)間:2022年08月22日 11:26:04   作者:心若向陽(yáng),何謂悲傷  
這篇文章主要為大家詳細(xì)介紹了QT基于TCP實(shí)現(xiàn)網(wǎng)絡(luò)聊天室程序,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下

本文實(shí)例為大家分享了QT學(xué)習(xí):基于TCP的網(wǎng)絡(luò)聊天室程序,供大家參考,具體內(nèi)容如下

TCP與UDP的差別如圖:

一、TCP工作原理

如下圖所示,TCP能夠?yàn)閼?yīng)用程序提供可靠的通信連接,使一臺(tái)計(jì)算機(jī)發(fā)出的字節(jié)流無(wú)差錯(cuò) 地送達(dá)網(wǎng)絡(luò)上的其他計(jì)算機(jī)。因此,對(duì)可靠性要求高的數(shù)據(jù)通信系統(tǒng)往往使用TCP傳輸數(shù)據(jù),但在正式收發(fā)數(shù)據(jù)前,通信雙方必須首先建立連接。

二、TCP編程模型

下面介紹基于TCP的經(jīng)典編程模型,TCP客戶端與服務(wù)器間的交互時(shí)序如下圖所示:

三、TCP服務(wù)器端編程實(shí)例

TCP服務(wù)器端的具體實(shí)現(xiàn)如下:

建立工程TcpServer.pro,文件代碼如下。

(1)頭文件“tcpserver.h”中聲明了需要的各種控件,TcpServer繼承自QDialog,實(shí)現(xiàn)了服務(wù)器端的對(duì)話框顯示與控制。其具體代碼如下:

#include <QDialog>?
#include <QListWidget>?
#include <QLabel>?
#include <QLineEdit>?
#include <QPushButton>?
#include <QGridLayout>?
class TcpServer : public QDialog?
{?
Q_OBJECT?
public:?
TcpServer(QWidget *parent = 0,Qt::WindowFlags f=0);?
~TcpServer();?
private:?
QListWidget *ContentListWidget;?
QLabel *PortLabel;?
QLineEdit *PortLineEdit;?
QPushButton *CreateBtn;?
QGridLayout *mainLayout;?
};?

(2)在源文件“tcpserver.cpp”中,TcpServer類的構(gòu)造函數(shù)主要實(shí)現(xiàn)窗體各控件的創(chuàng)建、布局等,其具體代碼如下:

#include "tcpserver.h"?
TcpServer::TcpServer(QWidget *parent,Qt::WindowFlags f) : QDialog(parent,f)?
{?
setWindowTitle(tr("TCP Server"));?
ContentListWidget = new QListWidget;?
PortLabel = new QLabel(tr("端口:"));?
PortLineEdit = new QLineEdit;?
CreateBtn = new QPushButton(tr("創(chuàng)建聊天室"));?
mainLayout = new QGridLayout(this);?
mainLayout->addWidget(ContentListWidget,0,0,1,2);?
mainLayout->addWidget(PortLabel,1,0);?
mainLayout->addWidget(PortLineEdit,1,1);?
mainLayout->addWidget(CreateBtn,2,0,1,2);?
}?

(3)服務(wù)器端界面如下圖所示:

以上完成了服務(wù)器端界面的設(shè)計(jì),下面將詳細(xì)完成聊天室的服務(wù)器端功能。

(1)在工程文件“TcpServer.pro”中添加如下語(yǔ)句:

QT += network 

(2)在工程“TcpServer.pro”中添加C++類文件“tcpclientsocket.h”及“tcpclientsocket.cpp”,TcpClientSocket 繼承自QTcpSocket,創(chuàng)建一個(gè)TCP套接字,以便在服務(wù)器端實(shí)現(xiàn)與客戶端程序的通信。
頭文件“tcpclientsocket.h”的具體代碼如下:

#include <QTcpSocket>?
#include <QObject>?
class TcpClientSocket : public QTcpSocket?
{?
Q_OBJECT //添加宏(Q_OBJECT)是為了實(shí)現(xiàn)信號(hào)與槽的通信?
public:?
TcpClientSocket(QObject *parent=0);?
signals:?
void updateClients(QString,int);?
void disconnected(int);?
protected slots:?
void dataReceived();?
void slotDisconnected();?
};?

(3)在源文件“tcpclientsocket.cpp”中,構(gòu)造函數(shù)(TcpClientSocket)的內(nèi)容(指定了信號(hào)與槽的
連接關(guān)系)如下:

#include "tcpclientsocket.h"?
TcpClientSocket::TcpClientSocket(QObject *parent)?
{?
connect(this,SIGNAL(readyRead()),this,SLOT(dataReceived())); // readyRead()是QIODevice的signal,由 QTcpSocket繼承而來(lái)。QIODevice是所有輸入/輸出設(shè)備的一個(gè)抽象類,其中定義了基本的接口,在Qt中, QTcpSocket也被看成一個(gè)QIODevice,readyRead()信號(hào)在有數(shù)據(jù)到來(lái)時(shí)發(fā)出。 ?
connect(this,SIGNAL(disconnected()),this,SLOT(slotDisconnected())); // disconnected()信號(hào)在斷開(kāi)連接時(shí)發(fā)出。?
}?

在源文件“tcpclientsocket.cpp”中,dataReceived()函數(shù)的具體代碼如下:

void TcpClientSocket::dataReceived()?
{?
while(bytesAvailable()>0)?
{?
int length = bytesAvailable();?
char buf[1024];?
read(buf,length);?
QString msg=buf;?
emit updateClients(msg,length);?
}?
}

在源文件“tcpclientsocket.cpp”中,槽函數(shù)slotDisconnected()的具體代碼如下:

void TcpClientSocket::slotDisconnected()?
{?
emit disconnected(this->socketDescriptor());?
}?

(4)在工程“TcpServer.pro”中添加C++類文件“server.h”及“server.cpp”,Server繼承自QTcpServer,實(shí)現(xiàn)一 個(gè)TCP協(xié)議的服務(wù)器。利用QTcpServer,開(kāi)發(fā)者可以監(jiān)聽(tīng)到指定端口的TCP連接。其具體代碼如下:

#include <QTcpServer>?
#include <QObject>?
#include "tcpclientsocket.h" //包含TCP的套接字?
class Server : public QTcpServer?
{?
Q_OBJECT?
//添加宏(Q_OBJECT)是為了實(shí)現(xiàn)信號(hào)與槽的通信?
public:?
Server(QObject *parent=0,int port=0);?
QList<TcpClientSocket*> tcpClientSocketList;?
signals:?
void updateServer(QString,int);?
public slots:?
void updateClients(QString,int);?
void slotDisconnected(int);?
protected:?
void incomingConnection(int socketDescriptor);?
};?

(5)在源文件“server.cpp”中,構(gòu)造函數(shù)(Server)的具體內(nèi)容如下:

#include "server.h"?
Server::Server(QObject *parent,int port):QTcpServer(parent)?
{?
listen(QHostAddress::Any,port);?
}

其中,listen(QHostAddress::Any,port)在指定的端口對(duì)任意地址進(jìn)行監(jiān)聽(tīng)。
QHostAddress定義了幾種特殊的IP地址,如QHostAddress::Null表示一個(gè)空地址;
QHostAddress::LocalHost表示IPv4的本機(jī)地址127.0.0.1;
QHostAddress::LocalHostIPv6表示IPv6的本機(jī)地址;
QHostAddress::Broadcast表示廣播地址255.255.255.255;
QHostAddress::Any表示IPv4的任意地址0.0.0.0;
QHostAddress::AnyIPv6表示IPv6的任意地址。

在源文件“server.cpp”中,當(dāng)出現(xiàn)一個(gè)新的連接時(shí),QTcpSever觸發(fā)incomingConnection()函數(shù),參數(shù)

socketDescriptor指定了連接的Socket描述符,其具體代碼如下:

void Server::incomingConnection(int socketDescriptor)?
{?
TcpClientSocket *tcpClientSocket=new TcpClientSocket(this); //創(chuàng)建一個(gè)新的TcpClientSocket與客戶端通信。 ?
connect(tcpClientSocket,SIGNAL(updateClients(QString,int), this,SLOT(updateClients(QString,int))); //連接TcpClientSocket的updateClients信號(hào)。 ?
connect(tcpClientSocket,SIGNAL(disconnected(int)),this, SLOT(slotDisconnected(int))); //連接 TcpClientSocket的disconnected信號(hào)。?
tcpClientSocket->setSocketDescriptor(socketDescriptor); //將新創(chuàng)建的TcpClient Socket的套接字描述符指定為參數(shù)socketDescriptor。?
tcpClientSocketList.append(tcpClientSocket); //將tcpClientSocket加入客戶端套接字列表以便管理。?
}

在源文件“server.cpp”中,updateClients()函數(shù)將任意客戶端發(fā)來(lái)的信息進(jìn)行廣播,保證聊天室的所有成員均能看到其他人的發(fā)言。其具體代碼如下:

void Server::updateClients(QString msg,int length)?
{?
emit updateServer(msg,length); //發(fā)出updateServer信號(hào),用來(lái)通知服務(wù)器對(duì)話框更新相應(yīng)的顯示狀態(tài)。
for(int i=0;i<tcpClientSocketList.count();i++) //實(shí)現(xiàn)信息的廣播,tcpClientSocketList中保存了所有與服務(wù)器相連的TcpClientSocket對(duì)象。?
{?
QTcpSocket *item = tcpClientSocketList.at(i);?
if(item->write(msg.toLatin1(),length)!=length)?
{?
continue;?
}?
}?
}?

在源文件“server.cpp”中,slotDisconnected()函數(shù)實(shí)現(xiàn)從tcpClientSocketList列表中將斷開(kāi)連接的
TcpClientSocket對(duì)象刪除的功能。其具體代碼如下:

void Server::slotDisconnected(int descriptor)?
{?
for(int i=0;i<tcpClientSocketList.count();i++)?
{?
QTcpSocket *item = tcpClientSocketList.at(i);?
if(item->socketDescriptor()==descriptor)?
{?
tcpClientSocketList.removeAt(i);?
return;?
}?
}
return;?
}

(6)在頭文件“tcpserver.h”中添加如下內(nèi)容:

#include "server.h"?
private:?
int port;?
Server *server;?
public slots:?
void slotCreateServer();?
void updateServer(QString,int);

(7)在源文件“tcpserver.cpp”中,在構(gòu)造函數(shù)中添加如下代碼:

port=8010;?
PortLineEdit->setText(QString::number(port));?
connect(CreateBtn,SIGNAL(clicked()),this,SLOT(slotCreateServer()));?

其中,槽函數(shù)slotCreateServer()用于創(chuàng)建一個(gè)TCP服務(wù)器,具體內(nèi)容如下:

void TcpServer::slotCreateServer()?
{?
server = new Server(this,port); //創(chuàng)建一個(gè)Server對(duì)象?
connect(server,SIGNAL(updateServer(QString,int)),this,?
SLOT(updateServer(QString,int)));?
CreateBtn->setEnabled(false);?
}?

槽函數(shù)updateServer()用于更新服務(wù)器上的信息顯示,具體內(nèi)容如下:

void TcpServer::updateServer(QString msg,int length)?
{?
ContentListWidget->addItem(msg.left(length));?
}

(8)此時(shí),工程中添加了很多文件,工程文件中的內(nèi)容已經(jīng)被改變,需要重新在工程文件
“TcpServer.pro”中添加:

QT += network 

此時(shí),運(yùn)行服務(wù)器端工程“TcpServer.pro”編譯通過(guò)。單擊“創(chuàng)建聊天室”按鈕,便開(kāi)通了一個(gè)TCP聊天室的服務(wù)器,如下圖所示:

四、TCP客戶端編程實(shí)例

TCP客戶端編程具體步驟如下:

建立工程“TcpClient.pro”,文件代碼如下。

(1)在頭文件“tcpclient.h”中,TcpClient類繼承自QDialog類,聲明了需要的各種控件,其具體代碼如下:

#include <QDialog>?
#include <QListWidget>?
#include <QLineEdit>?
#include <QPushButton>?
#include <QLabel>?
#include <QGridLayout>?
class TcpClient : public QDialog?
{?
Q_OBJECT?
public:?
TcpClient(QWidget *parent = 0,Qt::WindowFlags f=0);?
~TcpClient();?
private:?
QListWidget *contentListWidget;?
QLineEdit *sendLineEdit;?
QPushButton *sendBtn;?
QLabel *userNameLabel;?
QLineEdit *userNameLineEdit;?
QLabel *serverIPLabel;?
QLineEdit *serverIPLineEdit;?
QLabel *portLabel;?
QLineEdit *portLineEdit;?
QPushButton *enterBtn;?
QGridLayout *mainLayout;?
};

(2)源文件“tcpclient.cpp”的具體代碼如下:

#include "tcpclient.h"?
TcpClient::TcpClient(QWidget *parent,Qt::WindowFlags f)?
: QDialog(parent,f)?
{?
setWindowTitle(tr("TCP Client"));?
contentListWidget = new QListWidget;?
sendLineEdit = new QLineEdit;?
sendBtn = new QPushButton(tr("發(fā)送"));?
userNameLabel = new QLabel(tr("用戶名:"));?
userNameLineEdit = new QLineEdit;?
serverIPLabel = new QLabel(tr("服務(wù)器地址:"));?
serverIPLineEdit = new QLineEdit;?
portLabel = new QLabel(tr("端口:"));?
portLineEdit = new QLineEdit;?
enterBtn= new QPushButton(tr("進(jìn)入聊天室"));?
mainLayout = new QGridLayout(this);?
mainLayout->addWidget(contentListWidget,0,0,1,2);?
mainLayout->addWidget(sendLineEdit,1,0);?
mainLayout->addWidget(sendBtn,1,1);?
mainLayout->addWidget(userNameLabel,2,0);?
mainLayout->addWidget(userNameLineEdit,2,1);?
mainLayout->addWidget(serverIPLabel,3,0);?
mainLayout->addWidget(serverIPLineEdit,3,1);?
mainLayout->addWidget(portLabel,4,0);?
mainLayout->addWidget(portLineEdit,4,1);?
mainLayout->addWidget(enterBtn,5,0,1,2);?
}

(3)客戶端界面如下圖所示:

以上完成了客戶端界面的設(shè)計(jì),下面將完成客戶端的真正聊天功能。

(1)在客戶端工程文件“TcpClient.pro”中添加如下語(yǔ)句:

QT += network 

(2)在頭文件“tcpclient.h”中添加如下代碼:

#include <QHostAddress>?
#include <QTcpSocket>?
private:?
bool status;?
int port;?
QHostAddress *serverIP;?
QString userName;?
QTcpSocket *tcpSocket;?
public slots:?
void slotEnter();?
void slotConnected();?
void slotDisconnected();?
void dataReceived();?
void slotSend();?

(3)在源文件“tcpclient.cpp”中添加頭文件:

#include <QMessageBox>?
#include <QHostInfo>?

在其構(gòu)造函數(shù)中添加如下代碼:

status = false;?
port = 8010;?
portLineEdit->setText(QString::number(port));?
serverIP =new QHostAddress();?
connect(enterBtn,SIGNAL(clicked()),this,SLOT(slotEnter()));?
connect(sendBtn,SIGNAL(clicked()),this,SLOT(slotSend()));?
sendBtn->setEnabled(false);

在以上代碼中,槽函數(shù)slotEnter()實(shí)現(xiàn)了進(jìn)入和離開(kāi)聊天室的功能。具體代碼如下:

void TcpClient::slotEnter()
{
? ? if(!status)?? ?//status表示當(dāng)前的狀態(tài),true表示已經(jīng)進(jìn)入聊天室,false表示已經(jīng)離開(kāi)聊天室。 這里根據(jù)status的狀態(tài)決定是執(zhí)行“進(jìn)入”還是“離開(kāi)”的操作。
? ? {
?? ??? ?/* 完成輸入合法性檢驗(yàn) */
? ? ? ? QString ip = serverIPLineEdit->text();
? ? ? ? if(!serverIP->setAddress(ip))//用來(lái)判斷給定的IP地址能否被正確解析。
? ? ? ? {
? ? ? ? ? ? QMessageBox::information(this,tr("error"),tr("server ip address error!"));
? ? ? ? ? ? return;
? ? ? ? }
? ? ? ? if(userNameLineEdit->text()=="")
? ? ? ? {
? ? ? ? ? ? QMessageBox::information(this,tr("error"),tr("User name error!"));
? ? ? ? ? ? return;
? ? ? ? }
? ? ? ? userName=userNameLineEdit->text();
?? ??? ?/* 創(chuàng)建了一個(gè)QTcpSocket類對(duì)象,并將信號(hào)/槽連接起來(lái) */
? ? ? ? tcpSocket = new QTcpSocket(this);
? ? ? ? connect(tcpSocket,SIGNAL(connected()),this,SLOT (slotConnected()));
? ? ? ? connect(tcpSocket,SIGNAL(disconnected()),this,SLOT (slotDisconnected()));
? ? ? ? connect(tcpSocket,SIGNAL(readyRead()),this,SLOT (dataReceived()));
? ? ? ? tcpSocket->connectToHost(*serverIP,port);?? ?//與TCP服務(wù)器端連接,連接成功后發(fā)出connected() 信號(hào)
? ? ? ? status=true;
? ? }
? ? else
? ? {
? ? ? ? int length=0;
? ? ? ? QString msg=userName+tr(":Leave Chat Room");//構(gòu)造一條離開(kāi)聊天室的消息。
? ? ? ? if((length=tcpSocket->write(msg.toLatin1(),msg.length()))!=msg.length())?? ??? ??? ??? ??? ??? ??? ??? ??? ??? ??? ??? ?//通知服務(wù)器端以上 構(gòu)造的消息
? ? ? ? {
? ? ? ? ? ? return;
? ? ? ? }
? ? ? ? tcpSocket->disconnectFromHost();?? ??? ??? ??? ??? ?//與服務(wù)器斷開(kāi)連接,斷開(kāi)連接后發(fā)出disconnected()信號(hào)。
? ? ? ? status=false; //將status狀態(tài)復(fù)位
? ? }
}

在源文件“tcpclient.cpp”中,槽函數(shù)slotConnected()為connected()信號(hào)的響應(yīng)槽,當(dāng)與服務(wù)器連接成功后,客戶端構(gòu)造一條進(jìn)入聊天室的消息,并通知服務(wù)器。其具體代碼如下:

void TcpClient::slotConnected()?
{?
sendBtn->setEnabled(true);?
enterBtn->setText(tr("離開(kāi)"));?
int length=0;?
QString msg=userName+tr(":Enter Chat Room");?
if((length=tcpSocket->write(msg.toLatin1(),msg.length()))!=msg.length())?
{?
return;?
}?
}?

在源文件“tcpclient.cpp”中,槽函數(shù)slotSend()的具體代碼如下:

void TcpClient::slotSend()?
{?
if(sendLineEdit->text()=="")?
{?
return;?
}
QString msg=userName+":"+sendLineEdit->text();?
tcpSocket->write(msg.toLatin1(),msg.length());?
sendLineEdit->clear();?
}

在源文件“tcpclient.cpp”中,槽函數(shù)slotDisconnected()的具體內(nèi)容如下:

void TcpClient::slotDisconnected()?
{?
sendBtn->setEnabled(false);?
enterBtn->setText(tr("進(jìn)入聊天室"));?
}?

當(dāng)有數(shù)據(jù)到來(lái)時(shí),觸發(fā)源文件“tcpclient.cpp”的dataReceived()函數(shù),從套接字中將有效數(shù)據(jù)取出并顯示,其代碼如下:

void TcpClient::dataReceived()?
{?
while(tcpSocket->bytesAvailable()>0)?
{?
QByteArray datagram;?
datagram.resize(tcpSocket->bytesAvailable());?
tcpSocket->read(datagram.data(),datagram.size());?
QString msg=datagram.data();?
contentListWidget->addItem(msg.left(datagram.size()));?
}?
}?

(4)此時(shí)運(yùn)行客戶端“TcpClient.pro”工程,結(jié)果如下圖所示:

最后,同時(shí)運(yùn)行服務(wù)器和客戶端程序,運(yùn)行結(jié)果如下圖所示,這里演示的是系統(tǒng)中登錄了兩 個(gè)用戶的狀態(tài)。

以上就是本文的全部?jī)?nèi)容,希望對(duì)大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。

相關(guān)文章

  • C++ LeetCode1805字符串不同整數(shù)數(shù)目

    C++ LeetCode1805字符串不同整數(shù)數(shù)目

    這篇文章主要為大家介紹了C++ LeetCode1805字符串不同整數(shù)數(shù)目,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2022-12-12
  • C++ Explicit關(guān)鍵字詳細(xì)解析

    C++ Explicit關(guān)鍵字詳細(xì)解析

    以下是對(duì)C++中Explicit關(guān)鍵字的用法進(jìn)行了詳細(xì)的介紹,需要的朋友可以過(guò)來(lái)參考下,希望對(duì)大家有所幫助
    2013-09-09
  • 基于opencv實(shí)現(xiàn)車(chē)道線檢測(cè)

    基于opencv實(shí)現(xiàn)車(chē)道線檢測(cè)

    這篇文章主要為大家詳細(xì)介紹了基于opencv實(shí)現(xiàn)車(chē)道線檢測(cè),文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2021-02-02
  • C/C++中字符串流詳解及其作用介紹

    C/C++中字符串流詳解及其作用介紹

    這篇文章主要介紹了C/C++中字符串流詳解及其作用,本文給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下
    2021-09-09
  • 深入淺析OpenCV?copyTo()函數(shù)

    深入淺析OpenCV?copyTo()函數(shù)

    在Mat矩陣類的成員函數(shù)中copyTo(roi , mask)函數(shù)是非常有用的一個(gè)函數(shù),尤其是后面的mask可以實(shí)現(xiàn)蒙版的功能,下面通過(guò)本文給大家介紹下OpenCV copyTo()函數(shù)的相關(guān)知識(shí),感興趣的朋友一起看看吧
    2022-01-01
  • c語(yǔ)言之如何求e的近似值

    c語(yǔ)言之如何求e的近似值

    這篇文章主要介紹了c語(yǔ)言之如何求e的近似值問(wèn)題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2022-12-12
  • C++空間命名的使用

    C++空間命名的使用

    本文主要介紹了C++空間命名的使用,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧
    2023-01-01
  • VS?Code+msys2配置Windows系統(tǒng)下C/C++開(kāi)發(fā)環(huán)境

    VS?Code+msys2配置Windows系統(tǒng)下C/C++開(kāi)發(fā)環(huán)境

    我們?cè)趙indows10中使用VS Code做C++程序開(kāi)發(fā)過(guò)程中,需要安裝MSYS2和MinGW,下面這篇文章主要給大家介紹了關(guān)于VS?Code+msys2配置Windows系統(tǒng)下C/C++開(kāi)發(fā)環(huán)境的相關(guān)資料,需要的朋友可以參考下
    2022-12-12
  • C++實(shí)現(xiàn)賓館房間管理系統(tǒng)

    C++實(shí)現(xiàn)賓館房間管理系統(tǒng)

    這篇文章主要為大家詳細(xì)介紹了C++實(shí)現(xiàn)賓館房間管理系統(tǒng),文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2022-05-05
  • C語(yǔ)言遞歸實(shí)現(xiàn)掃雷游戲

    C語(yǔ)言遞歸實(shí)現(xiàn)掃雷游戲

    這篇文章主要為大家詳細(xì)介紹了C語(yǔ)言遞歸實(shí)現(xiàn)掃雷游戲,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2021-07-07

最新評(píng)論