Qt NetWork即时通讯网络聊天室(基于TCP)

Stella981
• 阅读 713

本文使用QT的网络模块来创建一个网络聊天室程序,主要包括以下功能:

1、基于TCP的可靠连接(QTcpServer、QTcpSocket)

2、一个服务器,多个客户端

Qt NetWork即时通讯网络聊天室(基于TCP)

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定义界面如下:

Qt NetWork即时通讯网络聊天室(基于TCP)

可以给不同的客户端取个名字,如“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直接运行!)

 Qt NetWork即时通讯网络聊天室(基于TCP)

Qt NetWork即时通讯网络聊天室(基于TCP)

点赞
收藏
评论区
推荐文章
blmius blmius
3年前
MySQL:[Err] 1292 - Incorrect datetime value: ‘0000-00-00 00:00:00‘ for column ‘CREATE_TIME‘ at row 1
文章目录问题用navicat导入数据时,报错:原因这是因为当前的MySQL不支持datetime为0的情况。解决修改sql\mode:sql\mode:SQLMode定义了MySQL应支持的SQL语法、数据校验等,这样可以更容易地在不同的环境中使用MySQL。全局s
Wesley13 Wesley13
3年前
java将前端的json数组字符串转换为列表
记录下在前端通过ajax提交了一个json数组的字符串,在后端如何转换为列表。前端数据转化与请求varcontracts{id:'1',name:'yanggb合同1'},{id:'2',name:'yanggb合同2'},{id:'3',name:'yang
皕杰报表之UUID
​在我们用皕杰报表工具设计填报报表时,如何在新增行里自动增加id呢?能新增整数排序id吗?目前可以在新增行里自动增加id,但只能用uuid函数增加UUID编码,不能新增整数排序id。uuid函数说明:获取一个UUID,可以在填报表中用来创建数据ID语法:uuid()或uuid(sep)参数说明:sep布尔值,生成的uuid中是否包含分隔符'',缺省为
待兔 待兔
4个月前
手写Java HashMap源码
HashMap的使用教程HashMap的使用教程HashMap的使用教程HashMap的使用教程HashMap的使用教程22
Jacquelyn38 Jacquelyn38
3年前
2020年前端实用代码段,为你的工作保驾护航
有空的时候,自己总结了几个代码段,在开发中也经常使用,谢谢。1、使用解构获取json数据let jsonData  id: 1,status: "OK",data: 'a', 'b';let  id, status, data: number   jsonData;console.log(id, status, number )
Stella981 Stella981
3年前
Android So动态加载 优雅实现与原理分析
背景:漫品Android客户端集成适配转换功能(基于目标识别(So库35M)和人脸识别库(5M)),导致apk体积50M左右,为优化客户端体验,决定实现So文件动态加载.!(https://oscimg.oschina.net/oscnet/00d1ff90e4b34869664fef59e3ec3fdd20b.png)点击上方“蓝字”关注我
Wesley13 Wesley13
3年前
00:Java简单了解
浅谈Java之概述Java是SUN(StanfordUniversityNetwork),斯坦福大学网络公司)1995年推出的一门高级编程语言。Java是一种面向Internet的编程语言。随着Java技术在web方面的不断成熟,已经成为Web应用程序的首选开发语言。Java是简单易学,完全面向对象,安全可靠,与平台无关的编程语言。
Wesley13 Wesley13
3年前
34.TCP取样器
阅读文本大概需要3分钟。1、TCP取样器的作用   TCP取样器作用就是通过TCP/IP协议来连接服务器,然后发送数据和接收数据。2、TCP取样器详解!(https://oscimg.oschina.net/oscnet/32a9b19ba1db00f321d22a0f33bcfb68a0d.png)TCPClien
Wesley13 Wesley13
3年前
MySQL部分从库上面因为大量的临时表tmp_table造成慢查询
背景描述Time:20190124T00:08:14.70572408:00User@Host:@Id:Schema:sentrymetaLast_errno:0Killed:0Query_time:0.315758Lock_
Python进阶者 Python进阶者
10个月前
Excel中这日期老是出来00:00:00,怎么用Pandas把这个去除
大家好,我是皮皮。一、前言前几天在Python白银交流群【上海新年人】问了一个Pandas数据筛选的问题。问题如下:这日期老是出来00:00:00,怎么把这个去除。二、实现过程后来【论草莓如何成为冻干莓】给了一个思路和代码如下:pd.toexcel之前把这