本文使用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
#include
#include
#include
#include
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
#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
#include
#include
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." );
socket->disconnectFromHost();
}
}
void MainWindow::socketConnected()
{
qDebug() << "Connected to server." ;
printMessage( "<font color=\" Green\ ">Connected to server." );
QString name = ui->nameInput->text();
socket->write( "<font color=\" Purple\ ">" + name.toUtf8() + " has joined the chat room." );
ui->connectButton->setText( "Disconnect" );
connectedToHost = true ;
}
void MainWindow::socketDisconnected()
{
qDebug() << "Disconnected from server." ;
printMessage( "<font color=\" Red\ ">Disconnected from server." );
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() + ": " + message.toUtf8());
ui->messageInput->clear();
}
Main.cpp
1
2
3
4
5
6
7
8
9
10
11
12
13
#include "MainWindow.h"
#include
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直接运行!)