<em>Mac</em>Book项目 2009年学校开始实施<em>Mac</em>Book项目,所有师生配备一本<em>Mac</em>Book,并同步更新了校园无线网络。学校每周进行电脑技术更新,每月发送技术支持资料,极大改变了教学及学习方式。因此2011
2021-06-01 09:32:01
本文範例為大家分享了QT學習:基於TCP的網路聊天室程式,供大家參考,具體內容如下
TCP與UDP的差別如圖:
如下圖所示,TCP能夠為應用程式提供可靠的通訊連線,使一臺計算機發出的位元組流無差錯 地送達網路上的其他計算機。因此,對可靠性要求高的資料通訊系統往往使用TCP傳輸資料,但在正式收發資料前,通訊雙方必須首先建立連線。
下面介紹基於TCP的經典程式設計模型,TCP使用者端與伺服器間的互動時序如下圖所示:
TCP伺服器端的具體實現如下:
建立工程TcpServer.pro,檔案程式碼如下。
(1)標頭檔案“tcpserver.h”中宣告了需要的各種控制元件,TcpServer繼承自QDialog,實現了伺服器端的對話方塊顯示與控制。其具體程式碼如下:
#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類別建構函式主要實現表單各控制元件的建立、佈局等,其具體程式碼如下:
#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("建立聊天室")); 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)伺服器端介面如下圖所示:
以上完成了伺服器端介面的設計,下面將詳細完成聊天室的伺服器端功能。
(1)在工程檔案“TcpServer.pro”中新增如下語句:
QT += network
(2)在工程“TcpServer.pro”中新增C++類別檔案“tcpclientsocket.h”及“tcpclientsocket.cpp”,TcpClientSocket 繼承自QTcpSocket,建立一個TCP通訊端,以便在伺服器端實現與使用者端程式的通訊。
標頭檔案“tcpclientsocket.h”的具體程式碼如下:
#include <QTcpSocket> #include <QObject> class TcpClientSocket : public QTcpSocket { Q_OBJECT //新增宏(Q_OBJECT)是為了實現訊號與槽的通訊 public: TcpClientSocket(QObject *parent=0); signals: void updateClients(QString,int); void disconnected(int); protected slots: void dataReceived(); void slotDisconnected(); };
(3)在原始檔“tcpclientsocket.cpp”中,建構函式(TcpClientSocket)的內容(指定了訊號與槽的
連線關係)如下:
#include "tcpclientsocket.h" TcpClientSocket::TcpClientSocket(QObject *parent) { connect(this,SIGNAL(readyRead()),this,SLOT(dataReceived())); // readyRead()是QIODevice的signal,由 QTcpSocket繼承而來。QIODevice是所有輸入/輸出裝置的一個抽象類,其中定義了基本的介面,在Qt中, QTcpSocket也被看成一個QIODevice,readyRead()訊號在有資料到來時發出。 connect(this,SIGNAL(disconnected()),this,SLOT(slotDisconnected())); // disconnected()訊號在斷開連線時發出。 }
在原始檔“tcpclientsocket.cpp”中,dataReceived()函數的具體程式碼如下:
void TcpClientSocket::dataReceived() { while(bytesAvailable()>0) { int length = bytesAvailable(); char buf[1024]; read(buf,length); QString msg=buf; emit updateClients(msg,length); } }
在原始檔“tcpclientsocket.cpp”中,槽函數slotDisconnected()的具體程式碼如下:
void TcpClientSocket::slotDisconnected() { emit disconnected(this->socketDescriptor()); }
(4)在工程“TcpServer.pro”中新增C++類別檔案“server.h”及“server.cpp”,Server繼承自QTcpServer,實現一 個TCP協定的伺服器。利用QTcpServer,開發者可以監聽到指定埠的TCP連線。其具體程式碼如下:
#include <QTcpServer> #include <QObject> #include "tcpclientsocket.h" //包含TCP的通訊端 class Server : public QTcpServer { Q_OBJECT //新增宏(Q_OBJECT)是為了實現訊號與槽的通訊 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”中,建構函式(Server)的具體內容如下:
#include "server.h" Server::Server(QObject *parent,int port):QTcpServer(parent) { listen(QHostAddress::Any,port); }
其中,listen(QHostAddress::Any,port)在指定的埠對任意地址進行監聽。
QHostAddress定義了幾種特殊的IP地址,如QHostAddress::Null表示一個空地址;
QHostAddress::LocalHost表示IPv4的本機地址127.0.0.1;
QHostAddress::LocalHostIPv6表示IPv6的本機地址;
QHostAddress::Broadcast表示廣播地址255.255.255.255;
QHostAddress::Any表示IPv4的任意地址0.0.0.0;
QHostAddress::AnyIPv6表示IPv6的任意地址。
在原始檔“server.cpp”中,當出現一個新的連線時,QTcpSever觸發incomingConnection()函數,引數
socketDescriptor指定了連線的Socket描述符,其具體程式碼如下:
void Server::incomingConnection(int socketDescriptor) { TcpClientSocket *tcpClientSocket=new TcpClientSocket(this); //建立一個新的TcpClientSocket與使用者端通訊。 connect(tcpClientSocket,SIGNAL(updateClients(QString,int), this,SLOT(updateClients(QString,int))); //連線TcpClientSocket的updateClients訊號。 connect(tcpClientSocket,SIGNAL(disconnected(int)),this, SLOT(slotDisconnected(int))); //連線 TcpClientSocket的disconnected訊號。 tcpClientSocket->setSocketDescriptor(socketDescriptor); //將新建立的TcpClient Socket的通訊端描述符指定為引數socketDescriptor。 tcpClientSocketList.append(tcpClientSocket); //將tcpClientSocket加入使用者端通訊端列表以便管理。 }
在原始檔“server.cpp”中,updateClients()函數將任意使用者端發來的資訊進行廣播,保證聊天室的所有成員均能看到其他人的發言。其具體程式碼如下:
void Server::updateClients(QString msg,int length) { emit updateServer(msg,length); //發出updateServer訊號,用來通知伺服器對話方塊更新相應的顯示狀態。 for(int i=0;i<tcpClientSocketList.count();i++) //實現資訊的廣播,tcpClientSocketList中儲存了所有與伺服器相連的TcpClientSocket物件。 { QTcpSocket *item = tcpClientSocketList.at(i); if(item->write(msg.toLatin1(),length)!=length) { continue; } } }
在原始檔“server.cpp”中,slotDisconnected()函數實現從tcpClientSocketList列表中將斷開連線的
TcpClientSocket物件刪除的功能。其具體程式碼如下:
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”中新增如下內容:
#include "server.h" private: int port; Server *server; public slots: void slotCreateServer(); void updateServer(QString,int);
(7)在原始檔“tcpserver.cpp”中,在建構函式中新增如下程式碼:
port=8010; PortLineEdit->setText(QString::number(port)); connect(CreateBtn,SIGNAL(clicked()),this,SLOT(slotCreateServer()));
其中,槽函數slotCreateServer()用於建立一個TCP伺服器,具體內容如下:
void TcpServer::slotCreateServer() { server = new Server(this,port); //建立一個Server物件 connect(server,SIGNAL(updateServer(QString,int)),this, SLOT(updateServer(QString,int))); CreateBtn->setEnabled(false); }
槽函數updateServer()用於更新伺服器上的資訊顯示,具體內容如下:
void TcpServer::updateServer(QString msg,int length) { ContentListWidget->addItem(msg.left(length)); }
(8)此時,工程中新增了很多檔案,工程檔案中的內容已經被改變,需要重新在工程檔案
“TcpServer.pro”中新增:
QT += network
此時,執行伺服器端工程“TcpServer.pro”編譯通過。單擊“建立聊天室”按鈕,便開通了一個TCP聊天室的伺服器,如下圖所示:
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("傳送")); userNameLabel = new QLabel(tr("使用者名稱:")); userNameLineEdit = new QLineEdit; serverIPLabel = new QLabel(tr("伺服器地址:")); serverIPLineEdit = new QLineEdit; portLabel = new QLabel(tr("埠:")); portLineEdit = new QLineEdit; enterBtn= new QPushButton(tr("進入聊天室")); 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)使用者端介面如下圖所示:
以上完成了使用者端介面的設計,下面將完成使用者端的真正聊天功能。
(1)在使用者端工程檔案“TcpClient.pro”中新增如下語句:
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>
在其建構函式中新增如下程式碼:
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);
在以上程式碼中,槽函數slotEnter()實現了進入和離開聊天室的功能。具體程式碼如下:
void TcpClient::slotEnter() { if(!status) //status表示當前的狀態,true表示已經進入聊天室,false表示已經離開聊天室。 這裡根據status的狀態決定是執行「進入」還是「離開」的操作。 { /* 完成輸入合法性檢驗 */ QString ip = serverIPLineEdit->text(); if(!serverIP->setAddress(ip))//用來判斷給定的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(); /* 建立了一個QTcpSocket類物件,並將訊號/槽連線起來 */ 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伺服器端連線,連線成功後發出connected() 訊號 status=true; } else { int length=0; QString msg=userName+tr(":Leave Chat Room");//構造一條離開聊天室的訊息。 if((length=tcpSocket->write(msg.toLatin1(),msg.length()))!=msg.length()) //通知伺服器端以上 構造的訊息 { return; } tcpSocket->disconnectFromHost(); //與伺服器斷開連線,斷開連線後發出disconnected()訊號。 status=false; //將status狀態復位 } }
在原始檔“tcpclient.cpp”中,槽函數slotConnected()為connected()訊號的響應槽,當與伺服器連線成功後,使用者端構造一條進入聊天室的訊息,並通知伺服器。其具體程式碼如下:
void TcpClient::slotConnected() { sendBtn->setEnabled(true); enterBtn->setText(tr("離開")); int length=0; QString msg=userName+tr(":Enter Chat Room"); if((length=tcpSocket->write(msg.toLatin1(),msg.length()))!=msg.length()) { return; } }
在原始檔“tcpclient.cpp”中,槽函數slotSend()的具體程式碼如下:
void TcpClient::slotSend() { if(sendLineEdit->text()=="") { return; } QString msg=userName+":"+sendLineEdit->text(); tcpSocket->write(msg.toLatin1(),msg.length()); sendLineEdit->clear(); }
在原始檔“tcpclient.cpp”中,槽函數slotDisconnected()的具體內容如下:
void TcpClient::slotDisconnected() { sendBtn->setEnabled(false); enterBtn->setText(tr("進入聊天室")); }
當有資料到來時,觸發原始檔“tcpclient.cpp”的dataReceived()函數,從通訊端中將有效資料取出並顯示,其程式碼如下:
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)此時執行使用者端“TcpClient.pro”工程,結果如下圖所示:
最後,同時執行伺服器和使用者端程式,執行結果如下圖所示,這裡演示的是系統中登入了兩 個使用者的狀態。
以上就是本文的全部內容,希望對大家的學習有所幫助,也希望大家多多支援it145.com。
相關文章
<em>Mac</em>Book项目 2009年学校开始实施<em>Mac</em>Book项目,所有师生配备一本<em>Mac</em>Book,并同步更新了校园无线网络。学校每周进行电脑技术更新,每月发送技术支持资料,极大改变了教学及学习方式。因此2011
2021-06-01 09:32:01
综合看Anker超能充系列的性价比很高,并且与不仅和iPhone12/苹果<em>Mac</em>Book很配,而且适合多设备充电需求的日常使用或差旅场景,不管是安卓还是Switch同样也能用得上它,希望这次分享能给准备购入充电器的小伙伴们有所
2021-06-01 09:31:42
除了L4WUDU与吴亦凡已经多次共事,成为了明面上的厂牌成员,吴亦凡还曾带领20XXCLUB全队参加2020年的一场音乐节,这也是20XXCLUB首次全员合照,王嗣尧Turbo、陈彦希Regi、<em>Mac</em> Ova Seas、林渝植等人全部出场。然而让
2021-06-01 09:31:34
目前应用IPFS的机构:1 谷歌<em>浏览器</em>支持IPFS分布式协议 2 万维网 (历史档案博物馆)数据库 3 火狐<em>浏览器</em>支持 IPFS分布式协议 4 EOS 等数字货币数据存储 5 美国国会图书馆,历史资料永久保存在 IPFS 6 加
2021-06-01 09:31:24
开拓者的车机是兼容苹果和<em>安卓</em>,虽然我不怎么用,但确实兼顾了我家人的很多需求:副驾的门板还配有解锁开关,有的时候老婆开车,下车的时候偶尔会忘记解锁,我在副驾驶可以自己开门:第二排设计很好,不仅配置了一个很大的
2021-06-01 09:30:48
不仅是<em>安卓</em>手机,苹果手机的降价力度也是前所未有了,iPhone12也“跳水价”了,发布价是6799元,如今已经跌至5308元,降价幅度超过1400元,最新定价确认了。iPhone12是苹果首款5G手机,同时也是全球首款5nm芯片的智能机,它
2021-06-01 09:30:45