本文使用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置于.\Qt\Qt5.12.0\5.12.0\mingw73_64\bin目录下(该目录下可以双击exe直接运行!)