本文使用QT的網絡模塊來建立一個網絡聊天室程序,主要包括如下功能:服務器
一、基於TCP的可靠鏈接(QTcpServer、QTcpSocket)網絡
二、一個服務器,多個客戶端app
三、服務器接收到某個客戶端的請求以及發送信息,經該信息重定向發給其它客戶端socket
最終實現一個共享聊天內容的聊天室!oop
開發測試環境:QT5.12.0 + Qt Creator 4.8.0 + MinGW7.3測試
代碼以下:ui
一、服務器 QtInstantMessagingServerthis
基於Console的應用程序,由於這裏不須要界面。spa
QT += core network
QT -= gui.net
1
2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 |
#ifndef
SERVER_H #define SERVER_H #include <QObject> #include <QTcpServer> #include <QTcpSocket> #include <QDebug> #include <QVector> class Server : public QObject { Q_OBJECT public : explicit Server(QObject *parent = nullptr); void startServer(); void sendMessageToClients(QString message); signals: public slots: void newClientConnection(); void socketDisconnected(); void socketReadyRead(); void socketStateChanged(QAbstractSocket::SocketState state); private : QTcpServer* chatServer; QVector<QTcpSocket*>* allClients; }; #endif // SERVER_H |
1
2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 |
#include
"Server.h"
Server::Server(QObject *parent) : QObject(parent) { } void Server::startServer() { // store all the connected clients allClients = new QVector<QTcpSocket*>; // created a QTcpServer object called chatServer chatServer = new QTcpServer(); // limit the maximum pending connections to 10 clients. chatServer->setMaxPendingConnections( 10 ); // The chatServer will trigger the newConnection() signal whenever a client has connected to the server. connect(chatServer, SIGNAL(newConnection()), this , SLOT(newClientConnection())); // made it constantly listen to port 8001. if (chatServer->listen(QHostAddress::Any, 8001 )) { qDebug() << "Server has started. Listening to port 8001." ; } else { qDebug() << "Server failed to start. Error: " + chatServer->errorString(); } } void Server::sendMessageToClients(QString message) { if (allClients->size() > 0 ) { // we simply loop through the allClients array and pass the message data to all the connected clients. for ( int i = 0 ; i < allClients->size(); i++) { if (allClients->at(i)->isOpen() && allClients->at(i)->isWritable()) { allClients->at(i)->write(message.toUtf8()); } } } } void Server::newClientConnection() { // Every new client connected to the server is a QTcpSocket object, // which can be obtained from the QTcpServer object by calling nextPendingConnection(). QTcpSocket* client = chatServer->nextPendingConnection(); // You can obtain information about the client // such as its IP address and port number by calling peerAddress() and peerPort(), respectively. QString ipAddress = client->peerAddress().toString(); int port = client->peerPort(); // connect the client's disconnected(),readyRead() and stateChanged() signals to its respective slot function. // 一、When a client is disconnected from the server, the disconnected() signal will be triggered connect(client, &QTcpSocket::disconnected, this , &Server::socketDisconnected); // 二、whenever a client is sending in a message to the server, the readyRead() signal will be triggered. connect(client, &QTcpSocket::readyRead, this , &Server::socketReadyRead); // 三、 connected another signal called stateChanged() to the socketStateChanged() slot function. connect(client, &QTcpSocket::stateChanged, this , &Server::socketStateChanged); // store each new client into the allClients array for future use. allClients->push_back(client); qDebug() << "Socket connected from " + ipAddress + ":" + QString::number(port); } // When a client is disconnected from the server, the disconnected() signal will be triggered void Server::socketDisconnected() { // displaying the message on the server console whenever it happens, and nothing more. QTcpSocket* client = qobject_cast<QTcpSocket*>(QObject::sender()); QString socketIpAddress = client->peerAddress().toString(); int port = client->peerPort(); qDebug() << "Socket disconnected from " + socketIpAddress + ":" + QString::number(port); } // whenever a client is sending in a message to the server, the readyRead() signal will be triggered. void Server::socketReadyRead() { // use QObject::sender() to get the pointer of the object that emitted the readyRead signal // and convert it to the QTcpSocket class so that we can access its readAll() function. QTcpSocket* client = qobject_cast<QTcpSocket*>(QObject::sender()); QString socketIpAddress = client->peerAddress().toString(); int port = client->peerPort(); QString data = QString(client->readAll()); qDebug() << "Message: " + data + " (" + socketIpAddress + ":" + QString::number(port) + ")" ; // redirect the message, just passing the message to all connected clients. sendMessageToClients(data); } // This function gets triggered whenever a client's network state has changed, // such as connected, disconnected, listening, and so on. void Server::socketStateChanged(QAbstractSocket::SocketState state) { QTcpSocket* client = qobject_cast<QTcpSocket*>(QObject::sender()); QString socketIpAddress = client->peerAddress().toString(); int port = client->peerPort(); QString desc; // simply print out a relevant message according to its new state if (state == QAbstractSocket::UnconnectedState) desc = "The socket is not connected." ; else if (state == QAbstractSocket::HostLookupState) desc = "The socket is performing a host name lookup." ; else if (state == QAbstractSocket::ConnectingState) desc = "The socket has started establishing a connection." ; else if (state == QAbstractSocket::ConnectedState) desc = "A connection is established." ; else if (state == QAbstractSocket::BoundState) desc = "The socket is bound to an address and port." ; else if (state == QAbstractSocket::ClosingState) desc = "The socket is about to close (data may still be waiting to be written)." ; else if (state == QAbstractSocket::ListeningState) desc = "For internal use only." ; qDebug() << "Socket state changed (" + socketIpAddress + ":" + QString::number(port) + "): " + desc; } |
1
2 3 4 5 6 7 8 9 10 11 12 13 |
#include
<QCoreApplication> #include "Server.h" int main( int argc, char *argv[]) { QCoreApplication a(argc, argv); Server* myServer = new Server(); myServer->startServer(); return a.exec(); } |
二、客戶端QtInstantMessagingClient
基於Widget的應用程序,客戶端須要一個友好的界面,父類QMainWindow,MainWindow.ui定義界面以下:
能夠給不一樣的客戶端取個名字,如「Michael」、「James」等等,點擊「Connect」按鈕鏈接服務端,此時Label變爲「Disconnect」。
QT += core gui network
1
2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 |
#ifndef
MAINWINDOW_H #define MAINWINDOW_H #include <QMainWindow> #include <QDebug> #include <QTcpSocket> namespace Ui { class MainWindow; } class MainWindow : public QMainWindow { Q_OBJECT public : explicit MainWindow(QWidget *parent = nullptr); ~MainWindow(); private slots: void on_connectButton_clicked(); void socketConnected(); void socketDisconnected(); void socketReadyRead(); void on_sendButton_clicked(); private : Ui::MainWindow* ui; bool connectedToHost; QTcpSocket* socket; void printMessage(QString message); }; #endif // MAINWINDOW_H |
1
2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 |
#include
"MainWindow.h"
#include "ui_MainWindow.h" MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent), ui( new Ui::MainWindow) { ui->setupUi( this ); connectedToHost = false ; } MainWindow::~MainWindow() { delete ui; } void MainWindow::on_connectButton_clicked() { if (!connectedToHost) { // create a QTcpSocket object called socket and make it connect to a host at 127.0.0.1 on port 8801. socket = new QTcpSocket(); // connected the socket object to its respective slot functions when connected(),disconnected(), and readReady() signals were triggered. // This is exactly the same as the server code connect(socket, SIGNAL(connected()), this , SLOT(socketConnected())); connect(socket, SIGNAL(disconnected()), this , SLOT(socketDisconnected())); connect(socket, SIGNAL(readyRead()), this , SLOT(socketReadyRead())); // Since this is only for testing purposes, we will connect the client to our test server, // which is located on the same computer. // If you're running the server on another computer, // you may change the IP address to a LAN or WAN address, depending on your need. socket->connectToHost( "127.0.0.1" , 8001 ); } else { QString name = ui->nameInput->text(); socket->write( "<font color=\" Orange\ ">" + name.toUtf8() + " has left the chat room.</font>" ); socket->disconnectFromHost(); } } void MainWindow::socketConnected() { qDebug() << "Connected to server." ; printMessage( "<font color=\" Green\ ">Connected to server.</font>" ); QString name = ui->nameInput->text(); socket->write( "<font color=\" Purple\ ">" + name.toUtf8() + " has joined the chat room.</font>" ); ui->connectButton->setText( "Disconnect" ); connectedToHost = true ; } void MainWindow::socketDisconnected() { qDebug() << "Disconnected from server." ; printMessage( "<font color=\" Red\ ">Disconnected from server.</font>" ); ui->connectButton->setText( "Connect" ); connectedToHost = false ; } void MainWindow::socketReadyRead() { ui->chatDisplay->append(socket->readAll()); } void MainWindow::printMessage(QString message) { ui->chatDisplay->append(message); } void MainWindow::on_sendButton_clicked() { QString name = ui->nameInput->text(); QString message = ui->messageInput->text(); socket->write( "<font color=\" Blue\ ">" + name.toUtf8() + "</font>: " + message.toUtf8()); ui->messageInput->clear(); } |
1
2 3 4 5 6 7 8 9 10 11 12 13 |
#include
"MainWindow.h"
#include <QApplication> int main( int argc, char *argv[]) { QApplication a(argc, argv); MainWindow w; w.show(); return a.exec(); } |
構建成功後,將生成的QtInstantMessagingServer.exe以及QtInstantMessagingClient.exe置於.\Qt\Qt5.12.0\5.12.0\mingw73_64\bin目錄下(該目錄下能夠雙擊exe直接運行!)