本文使用QT的网络模块来创建一个网络聊天室程序,主要包括以下功能:
1、基于TCP的可靠连接(QTcpServer、QTcpSocket)
2、一个服务器,多个客户端
3、服务器接收到某个客户端的请求以及发送信息,经该信息重定向发给其它客户端
最终实现一个共享聊天内容的聊天室!
开发测试环境:QT5.12.0 + Qt Creator 4.8.0 + MinGW7.3
代码如下:
1、服务器 QtInstantMessagingServer
基于Console的应用程序,因为这里不需要界面。
QT += core network
QT -= gui
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 |
#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 |
Server.cpp
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. // 1、When a client is disconnected from the server, the disconnected() signal will be triggered connect(client, &QTcpSocket::disconnected, this, &Server::socketDisconnected); // 2、whenever a client is sending in a message to the server, the readyRead() signal will be triggered. connect(client, &QTcpSocket::readyRead, this, &Server::socketReadyRead); // 3、 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; } |
Main.cpp
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(); } |
2、客户端QtInstantMessagingClient
基于Widget的应用程序,客户端需要一个友好的界面,父类QMainWindow,MainWindow.ui定义界面如下:
可以给不同的客户端取个名字,如“Michael”、“James”等等,点击“Connect”按钮连接服务端,此时Label变为“Disconnect”。
QT += core gui network
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 |
#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 |
MainWindow.cpp
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(); } |
Main.cpp
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置于.QtQt5.12.05.12.0mingw73_64in目录下(该目录下可以双击exe直接运行!)