基於TCP的網絡聊天程序
- 下圖是最後效果圖(因爲是剛做好,還沒來的及修改小細節,不過這都是小事);
- 在說明之前需要說明一點關於信號與槽的Qt常識
- signals: 只聲明,不定義! 手動定義的信號,使用 emit ,由自己手動觸發;
- slots:定義的槽可以通過自己定義的信號或者系統信號來被動調用;
好了,我們現在進入正題
1. 程序框架(數據傳輸的過程):
首先,我來說明下整個大框架。我認爲,在本次編程過程中,最重要的部分就是他了,沒有之一!
爲了便於說明下面大家先看這張圖,之後的說明都圍繞這張圖來說明:
- 客戶端方面:首先,客戶端方面包含兩部分。一個是ClientUI,用於佈置客戶端界面;另一個是從QTcpSocket繼承來的子類ClientSocket,通過 ClientSocket + ServerIP + Port 連接到服務器, 成功連接後就可以通過ClientSocket 和服務器端進行通信了(發送數據和接受數據)。ClientUI 和ClientSocket 之間通過信號與槽進行信息傳遞(接收:ClientSocket從服務器接收到數據後,通過信號與槽傳遞給ClientUI,然後ClientUI將信息顯示出來;發送:點擊Send按鈕後,ClientUI將數據通過信號與槽傳遞給ClientSocket,ClientSocket將信息傳輸給服務器端)。
- 服務器端方面:服務器端方面稍複雜一點。一個用於佈置服務器端界面的ServerUI;一個是從QTcpServer繼承來的子類Server,Server 和ServerUI 之間傳輸數據與 ClientSocket 和 ClientUI 之間很類似,不再囉嗦,另外Server 還包含一個Socket的列表,用來記錄連接到該服務器的socket信息。當有新信息收到後,可以通過該列表更新所有連接到該服務器端的客戶端。在我看來,Server端就相當於一個信息中轉站,任意一個客戶端都可以通過Server與其他的客戶端進行通信。
- 客戶端的ClientSocket和服務器端Socket列表中某一socket連接,進行通信。
- 其實明白了以上內容就可以自己動手開始寫了,下面信號與槽接口有需要可作爲參考。
2. 設計UI,確定每個部件所需要實現的功能。
UI的編寫很簡單,只要寫過一個基本就沒什麼問題,如有需要可以參考我之前的一篇博客Qt實例–計算圓的面積.
3. 根據實現功能設計信號與槽:
a.客戶端
//ClientUI:
signals:
void sendMsg(QString); //告訴ClientSocket有活幹了
void connectServer(QString,QString); //讓ClientSocket連接到服務器
void disconnectServer(); //讓ClientSocket從服務器斷開
public slots:
void slotSendMsg(); //UI上send按鈕被點擊後觸發。功能:對信息進行簡單處理,發出sendMsg(QString)信號;
void slotSwitch(); //UI上最下方按鈕被點擊後觸發。
//功能:根據是否已連接Server作出不同反應。
//若已連接,發出disconnectServer()信號,若未連接,發出connectServer(QString,QString);
void slotConnected(); //若ClientSocket成功連接服務器後觸發。 功能:將UI上最下方按鈕文本改成”leave“。
void slotdisconnected(); //若ClientSocket斷開服務器後觸發。 功能:將UI上最下方按鈕文本改成”enter“。
void slotupdateClient(QString); //當ClientSocket發出updateClient()信號時被觸發。 功能:將新信息顯出來。
//ClientSocket
signals:
void updateClient(QString); //讓ClientUI更新新信息;
public slots:
void slotsendMsg(QString); //當ClientUI發出sendMsg(QString)信號後觸發。功能:向Server發送數據
void slotconnectServer(QString,QString); //當ClientUI發出connectServer(QString,QString)信號後觸發。功能:嘗試連接服務器。
void slotdisconnectServer(); //當ClientUI發出disconnectServer()信號後觸發。功能:嘗試斷開與服務器的連接
void slotdataReceive(); //當ClientSocket檢測到有新數據到達時觸發。功能:接收數據並處理,然後觸發updateClient(QString)信號。
b.服務器端
//ServerUI
signals:
void createServer(); //功能:觸發slotCreateServer();
void closeServer(); //功能:觸發Server的SlotClost();
public slots:
void slotServerSwitch(); //當服務器端下方按鈕被點擊時觸發。
//功能:根據是否已經創建服務器,採取不同的方式運行
//若已建好服務器,則發起closeServer()信號
//否則,發起createServer()信號
void slotupdateServerUI(QString); //當updateServerUI(QString)信號發出後被觸發。 功能:將收到信息顯示到服務器端
void slotCreateServer(); //當createServer()信號發出時被觸發。 功能:創建一個Server
//Server
signals:
void updateServerUI(QString); //功能:觸發ServerUI的slotupdateServerUI(QString);
private slots:
void updateClient(QString); //當Client的updateServer(QString)信號發出後被觸發。功能:向所有客戶端寫數據。發出updateServerUI(QString)信號。
void slotdisconnect(int); //當Client的disconnect(int)信號發出後被觸發。 功能:從server的client列表中將特定client刪除。
void slotClose(); //當ServerUI發出closeServer()信號後被觸發。 功能:斷開所有連接,清空client列表,並析構當前Server。
protected:
void incomingConnection(qintptr); //重載函數
//當有socket試圖連接時被觸發,不需要自己寫connect()來連接。
//另外需注意的一點是該函數的參數,Qt4參數是int,Qt5是qintptr。寫錯了永遠不會被調用。。。小心
//Client
signals:
void updateServer(QString); //更新服務器
void disconnect(int); //發出信號讓服務器從client列表中移除該Client,參數爲該Client的套接字描述符(唯一描述一個socket)。
private slots:
void dataReceive(); //當有數據可讀時被觸發,功能:接收並處理數據,然後發出updateServer(QString)信號。
void slotdisconnect(); //Client被斷開時被觸發。功能:獲得該Client的套接字描述符,併發出disconnect(int)信號
4 我的源碼
我的源碼放在github上了,大家有需要可以直接去看我的小項目chatroom