【QT】网络模块案例——聊天室

案例——网络聊天室

服务端
  • chat-room-server.pro

    QT       += core gui network
    
    greaterThan(QT_MAJOR_VERSION, 4): QT += widgets
    
    CONFIG += c++11
    
    SOURCES += \
        main.cpp \
        dialog.cpp
    
    HEADERS += \
        dialog.h
    
    FORMS += \
        dialog.ui
    
  • dialog.ui

    <?xml version="1.0" encoding="UTF-8"?>
    <ui version="4.0">
     <class>Dialog</class>
     <widget class="QDialog" name="Dialog">
      <property name="geometry">
       <rect>
        <x>0</x>
        <y>0</y>
        <width>526</width>
        <height>372</height>
       </rect>
      </property>
      <property name="font">
       <font>
        <family>Inconsolata</family>
        <pointsize>16</pointsize>
       </font>
      </property>
      <property name="windowTitle">
       <string>聊天室服务端</string>
      </property>
      <layout class="QVBoxLayout" name="verticalLayout">
       <item>
        <layout class="QHBoxLayout" name="horizontalLayout">
         <item>
          <spacer name="horizontalSpacer">
           <property name="orientation">
            <enum>Qt::Horizontal</enum>
           </property>
           <property name="sizeHint" stdset="0">
            <size>
             <width>40</width>
             <height>20</height>
            </size>
           </property>
          </spacer>
         </item>
         <item>
          <widget class="QListWidget" name="listWidget">
           <property name="minimumSize">
            <size>
             <width>480</width>
             <height>270</height>
            </size>
           </property>
           <property name="maximumSize">
            <size>
             <width>480</width>
             <height>270</height>
            </size>
           </property>
          </widget>
         </item>
         <item>
          <spacer name="horizontalSpacer_2">
           <property name="orientation">
            <enum>Qt::Horizontal</enum>
           </property>
           <property name="sizeHint" stdset="0">
            <size>
             <width>40</width>
             <height>20</height>
            </size>
           </property>
          </spacer>
         </item>
        </layout>
       </item>
       <item>
        <layout class="QGridLayout" name="gridLayout">
         <item row="0" column="0">
          <widget class="QLabel" name="portLabel">
           <property name="text">
            <string> 服务器端口:</string>
           </property>
          </widget>
         </item>
         <item row="0" column="1">
          <widget class="QLineEdit" name="portEdit">
           <property name="text">
            <string>8080</string>
           </property>
          </widget>
         </item>
        </layout>
       </item>
       <item>
        <widget class="QPushButton" name="createButton">
         <property name="text">
          <string>创建服务器</string>
         </property>
        </widget>
       </item>
      </layout>
     </widget>
     <resources/>
     <connections/>
    </ui>
    

    在这里插入图片描述

  • dialog.h

    #ifndef DIALOG_H
    #define DIALOG_H
    
    #include <QDialog>
    #include <QTcpServer>
    #include <QTcpSocket>
    #include <QTimer>
    
    QT_BEGIN_NAMESPACE
    namespace Ui { class Dialog; }
    QT_END_NAMESPACE
    
    class Dialog : public QDialog {
    Q_OBJECT
    public:
        Dialog(QWidget* parent = nullptr);
        ~Dialog();
    private slots:
        // 创建服务器按钮点击槽函数
        void on_createButton_clicked();
        // 响应客户端连接请求槽函数
        void onNewConnection();
        // 接收欧客户端消息槽函数
        void onReadyRead();
        // 通信断开检查槽函数
        void onTimeout();
    private:
        // 转发聊天消息到其它客户端
        void sendMessage(const QByteArray& buf);
    private:
        Ui::Dialog* ui;
        QTcpServer tcpServer;
        quint16 port;
        QList<QTcpSocket*> tcpClientList;
        QTimer timer;
    };
    
    #endif // DIALOG_H
    
  • dialog.cpp

    #include "dialog.h"
    #include "ui_dialog.h"
    
    Dialog::Dialog(QWidget* parent) : QDialog(parent), ui(new Ui::Dialog) {
        ui->setupUi(this);
        connect(&tcpServer, SIGNAL(newConnection()), this, SLOT(onNewConnection()));
        connect(&timer, SIGNAL(timeout()), SLOT(onTimeout()));
    }
    
    Dialog::~Dialog() {
        delete ui;
    }
    
    // 创建服务器按钮点击槽函数
    void Dialog::on_createButton_clicked() {
        port = ui->portEdit->text().toShort();
        if (tcpServer.listen(QHostAddress::Any, port)) {
            qDebug() << "创建服务器成功";
            ui->createButton->setEnabled(false);
            ui->portEdit->setEnabled(false);
            // 开启定时器
            timer.start(3000);
        } else {
            qDebug() << "创建服务器失败";
        }
    }
    
    // 响应客户端连接请求槽函数
    void Dialog::onNewConnection() {
        // 后去和客户端通信的套接字
        QTcpSocket* tcpSocket = tcpServer.nextPendingConnection();
        // 保存套接字到容器
        tcpClientList.append(tcpSocket);
        // 当客户端向服务器发送消息时通信套接字发送 readyRead() 信号
        connect(tcpSocket, SIGNAL(readyRead()), this, SLOT(onReadyRead()));
    
    }
    
    // 接收欧客户端消息槽函数
    void Dialog::onReadyRead() {
        // 遍历容器获取哪个客户端给服务器发送了消息
        for (int i = 0; i < tcpClientList.size(); i++) {
            // 返回 0 表示没有消息
            if (tcpClientList.at(i)->bytesAvailable() != 0) {
                // 读取消息
                QByteArray buf = tcpClientList.at(i)->readAll();
                // 显示消息
                ui->listWidget->addItem(buf);
                ui->listWidget->scrollToBottom();
                // 转发消息给所有在线客户端
                sendMessage(buf);
            }
        }
    }
    
    // 通信断开检查槽函数
    void Dialog::onTimeout() {
        // 遍历检查容器中保存的通信套接字是否已经断开连接,如果是则删除
        for (int i = 0; i < tcpClientList.size(); i++) {
            if (tcpClientList.at(i)->state() == QAbstractSocket::UnconnectedState) {
                tcpClientList.removeAt(i);
                i--;
            }
        }
    }
    
    // 转发聊天消息到其它客户端
    void Dialog::sendMessage(const QByteArray& buf) {
        for (int i = 0; i < tcpClientList.size(); i++) {
            tcpClientList.at(i)->write(buf);
        }
    }
    
客户端
  • dialog.ui

    <?xml version="1.0" encoding="UTF-8"?>
    <ui version="4.0">
     <class>Dialog</class>
     <widget class="QDialog" name="Dialog">
      <property name="geometry">
       <rect>
        <x>0</x>
        <y>0</y>
        <width>526</width>
        <height>491</height>
       </rect>
      </property>
      <property name="font">
       <font>
        <family>Inconsolata</family>
        <pointsize>16</pointsize>
       </font>
      </property>
      <property name="windowTitle">
       <string>聊天室客户端</string>
      </property>
      <layout class="QVBoxLayout" name="verticalLayout">
       <item>
        <layout class="QHBoxLayout" name="horizontalLayout">
         <item>
          <spacer name="horizontalSpacer">
           <property name="orientation">
            <enum>Qt::Horizontal</enum>
           </property>
           <property name="sizeHint" stdset="0">
            <size>
             <width>40</width>
             <height>20</height>
            </size>
           </property>
          </spacer>
         </item>
         <item>
          <widget class="QListWidget" name="listWidget">
           <property name="minimumSize">
            <size>
             <width>480</width>
             <height>270</height>
            </size>
           </property>
           <property name="maximumSize">
            <size>
             <width>480</width>
             <height>270</height>
            </size>
           </property>
          </widget>
         </item>
         <item>
          <spacer name="horizontalSpacer_2">
           <property name="orientation">
            <enum>Qt::Horizontal</enum>
           </property>
           <property name="sizeHint" stdset="0">
            <size>
             <width>40</width>
             <height>20</height>
            </size>
           </property>
          </spacer>
         </item>
        </layout>
       </item>
       <item>
        <layout class="QGridLayout" name="gridLayout">
         <item row="0" column="1">
          <widget class="QLineEdit" name="hostEdit">
           <property name="text">
            <string>127.0.0.1</string>
           </property>
          </widget>
         </item>
         <item row="2" column="1">
          <widget class="QLineEdit" name="nicknameEdit">
           <property name="text">
            <string/>
           </property>
          </widget>
         </item>
         <item row="2" column="0">
          <widget class="QLabel" name="nicknameLabel">
           <property name="text">
            <string>聊天室昵称:</string>
           </property>
          </widget>
         </item>
         <item row="0" column="0">
          <widget class="QLabel" name="hostLabel">
           <property name="text">
            <string>服务器地址:</string>
           </property>
          </widget>
         </item>
         <item row="1" column="0">
          <widget class="QLabel" name="portLabel">
           <property name="text">
            <string>服务器端口:</string>
           </property>
          </widget>
         </item>
         <item row="1" column="1">
          <widget class="QLineEdit" name="portEdit">
           <property name="text">
            <string>8080</string>
           </property>
          </widget>
         </item>
        </layout>
       </item>
       <item>
        <widget class="QPushButton" name="connectButton">
         <property name="text">
          <string>连接服务器</string>
         </property>
        </widget>
       </item>
       <item>
        <layout class="QHBoxLayout" name="horizontalLayout_2">
         <item>
          <widget class="QLineEdit" name="messageEdit"/>
         </item>
         <item>
          <widget class="QPushButton" name="sendButton">
           <property name="enabled">
            <bool>false</bool>
           </property>
           <property name="text">
            <string>发送</string>
           </property>
          </widget>
         </item>
        </layout>
       </item>
      </layout>
     </widget>
     <resources/>
     <connections/>
    </ui>
    

    在这里插入图片描述

  • dialog.h

    #ifndef DIALOG_H
    #define DIALOG_H
    
    #include <QDialog>
    #include <QTcpSocket>
    #include <QHostAddress>
    #include <QMessageBox>
    
    QT_BEGIN_NAMESPACE
    namespace Ui { class Dialog; }
    QT_END_NAMESPACE
    
    class Dialog : public QDialog {
    Q_OBJECT
    public:
        Dialog(QWidget* parent = nullptr);
        ~Dialog();
    private slots:
        void on_connectButton_clicked();
        void on_sendButton_clicked();
        void onConnected();
        void onDisconnected();
        void onReadyRead();
        // 网络异常槽函数
        void onErrorOccurred(QAbstractSocket::SocketError);
    private:
        Ui::Dialog* ui;
        bool alive;
        QTcpSocket tcpSocket;
        QHostAddress host;
        quint16 port;
        QString nickname;
    };
    
    #endif // DIALOG_H
    
  • dialog.cpp

    #include "dialog.h"
    #include "ui_dialog.h"
    
    Dialog::Dialog(QWidget* parent) : QDialog(parent), ui(new Ui::Dialog) {
        ui->setupUi(this);
        alive = false;
        connect(&tcpSocket, SIGNAL(connected()), this, SLOT(onConnected()));
        connect(&tcpSocket, SIGNAL(disconnected()), this, SLOT(onDisconnected()));
        connect(&tcpSocket, SIGNAL(readyRead()), this, SLOT(onReadyRead()));
        connect(&tcpSocket, SIGNAL(errorOccurred(QAbstractSocket::SocketError)), this, SLOT(onErrorOccurred(QAbstractSocket::SocketError)));
    }
    
    Dialog::~Dialog() {
        delete ui;
    }
    
    void Dialog::on_connectButton_clicked() {
        // 如果当前是离线状态,则建立和服务器的连接,否则端口和服务器的连接
        if (!alive) {
            host.setAddress(ui->hostEdit->text());
            port = ui->portEdit->text().toShort();
            nickname = ui->nicknameEdit->text();
            tcpSocket.connectToHost(host, port);
        } else {
            QString message = "系统: " + nickname + "离开了聊天室";
            tcpSocket.write(message.toUtf8());
            tcpSocket.disconnectFromHost();
        }
    }
    
    
    void Dialog::on_sendButton_clicked() {
        QString message = ui->messageEdit->text();
        if (message == "") {
            return;
        }
        message = nickname + ": " + message;
        tcpSocket.write(message.toUtf8());
        ui->messageEdit->clear();
    }
    
    void Dialog::onConnected() {
        alive = true;
        ui->sendButton->setEnabled(true);
        ui->hostEdit->setEnabled(false);
        ui->portEdit->setEnabled(false);
        ui->nicknameEdit->setEnabled(false);
        ui->connectButton->setText("离开聊天室");
        // 发送系统提示消息
        QString message = "系统: " + nickname + "进入了聊天室";
        tcpSocket.write(message.toUtf8());
    }
    
    void Dialog::onDisconnected() {
        alive = false;
        ui->sendButton->setEnabled(false);
        ui->hostEdit->setEnabled(true);
        ui->portEdit->setEnabled(true);
        ui->nicknameEdit->setEnabled(true);
        ui->connectButton->setText("连接服务器");
    }
    
    void Dialog::onReadyRead() {
        if (tcpSocket.bytesAvailable() != 0) {
            QByteArray buf = tcpSocket.readAll();
            ui->listWidget->addItem(buf);
            ui->listWidget->scrollToBottom();
        }
    }
    
    // 网络异常槽函数
    void Dialog::onErrorOccurred(QAbstractSocket::SocketError) {
        QMessageBox::critical(this, "Error", tcpSocket.errorString());
    }
    
效果

在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值