QT实现TCP网络聊天室

 更新时间:2022年08月19日 15:47:19   作者:Soaring丶  
这篇文章主要为大家详细介绍了QT实现TCP网络聊天室,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下

本文实例为大家分享了QT实现TCP网络聊天室的具体代码,供大家参考,具体内容如下

服务器:

serverdialog.h

#ifndef SERVERDIALOG_H
#define SERVERDIALOG_H

#include <QDialog>
#include <QTcpServer>
#include <QTcpSocket>
#include <QDebug>
#include <QTimer>

namespace Ui {
class ServerDialog;
}

class ServerDialog : public QDialog
{
    Q_OBJECT

public:
    explicit ServerDialog(QWidget *parent = 0);
    ~ServerDialog();

private slots:
    //创建服务器按钮对应的槽函数
    void on_pushButton_clicked();
    //响应客户端连接请求的槽函数
    void onNewConnection();
    //接收客户端聊天消息的槽函数
    void onReadyRead();
    //转发聊天消息给其它客户端的槽函数
    void sendMessage(const QByteArray&);
    //定时器检查客户端套接字是否为正常连接状态的槽函数
    void onTimeout(void);
private:
    Ui::ServerDialog *ui;
    QTcpServer tcpServer;//TCP服务器
    quint16 port;//服务器端口,quint16-->unsigned short
    QList<QTcpSocket*> tcpClientList;//容器:保存和客户端通信的套接字
    QTimer timer;//定时器,定时检查容器中和客户端通信的套接字是否为正常连接状态
};


#endif // SERVERDIALOG_H

serverdialog.cpp

#include "serverdialog.h"
#include "ui_serverdialog.h"

ServerDialog::ServerDialog(QWidget *parent) :
    QDialog(parent),
    ui(new Ui::ServerDialog)
{
    ui->setupUi(this);
}

ServerDialog::~ServerDialog()
{
    delete ui;
}
//创建服务器按钮对应的槽函数
void ServerDialog::on_pushButton_clicked()
{
    //获取服务器端口
    port = ui->lineEdit->text().toShort();
    //设置监听服务器的IP和端口
    //QHostAddress::Any ==> QHostAddress("0.0.0.0");
    if(tcpServer.listen(QHostAddress::Any,port)==false){
        qDebug() << "创建服务器失败";
        return;
    }
    else{
        qDebug() << "创建服务器成功";
        //当有客户端向服务器发送连接请求时,发送信号newConnection
        connect(&tcpServer,SIGNAL(newConnection()),
                this,SLOT(onNewConnection()));
        //禁用端口输入和创建服务器按钮
        ui->lineEdit->setEnabled(false);
        ui->pushButton->setEnabled(false);

        //定时器到时发送信号:timeout
        connect(&timer,SIGNAL(timeout()),this,SLOT(onTimeout()));
        //开启定时器,每隔3秒检查一次
        timer.start(3000);

    }
}
//响应客户端连接请求的槽函数
void ServerDialog::onNewConnection()
{
    //获取和客户端通信的套接字
    QTcpSocket* tcpClient =  tcpServer.nextPendingConnection();
    //保存套接字到容器中
    tcpClientList.append(tcpClient);
    //当客户端给服务器发送消息时,tcpClient发送信号readyRead
    connect(tcpClient,SIGNAL(readyRead()),
            this,SLOT(onReadyRead()));
}
//接收客户端聊天消息的槽函数
void ServerDialog::onReadyRead()
{
    //遍历检查哪个客户端有消息
    for(int i=0;i<tcpClientList.size();i++){
        //at(i):获取容器中第i套接字(QTcpSocket*)
        //bytesAvailable:获取当前套接字等待读取消息的字节数,如果为0表示没有消息,如果
        //大于0说明有消息.
        if(tcpClientList.at(i)->bytesAvailable()){
            //读取消息并保存
            QByteArray buf = tcpClientList.at(i)->readAll();
            //显示消息
            ui->listWidget->addItem(buf);
            //回滚到最底部(最新消息)
            ui->listWidget->scrollToBottom();
            //转发消息给其它客户端
            sendMessage(buf);
        }
    }
}
//转发聊天消息给其它客户端的槽函数
void ServerDialog::sendMessage(const QByteArray& msg)
{
    for(int i=0;i<tcpClientList.size();i++){
        tcpClientList.at(i)->write(msg);
    }
}
//定时器检查客户端套接字是否为正常连接状态的槽函数
void ServerDialog::onTimeout(void)
{
    for(int i=0;i<tcpClientList.size();i++){
        //state():获取第i个套接字的连接状态
        //UnconnectedState:表示未连接(断开连接)
        if(tcpClientList.at(i)->state() ==
                QAbstractSocket::UnconnectedState){
            //从容器中将断开连接的套接字移除
            tcpClientList.removeAt(i);
            --i;
        }
    }
}

main.cpp

#include "serverdialog.h"
#include <QApplication>

int main(int argc, char *argv[])
{
    QApplication a(argc, argv);
    ServerDialog w;
    w.show();

    return a.exec();
}

客户端:

clientdialog:

#ifndef CLIENTDIALOG_H
#define CLIENTDIALOG_H

#include <QDialog>
//QT += network
#include <QTcpSocket>
#include <QHostAddress>
#include <QMessageBox>

namespace Ui {
class ClientDialog;
}

class ClientDialog : public QDialog
{
    Q_OBJECT

public:
    explicit ClientDialog(QWidget *parent = 0);
    ~ClientDialog();

private slots:
    //发送按钮对应的槽函数
    void on_sendButton_clicked();
    //连接服务器按钮对应的槽函数
    void on_connectButton_clicked();
    //和服务器连接成功时执行的槽函数
    void onConnected(void);
    //和服务器断开连接时执行的槽函数
    void onDisconnected(void);
    //接收服务器转发聊天消息的槽函数
    void onReadyRead(void);
    //网络通信异常时执行的槽函数
    void onError(void);
private:
    Ui::ClientDialog *ui;
    bool status;//客户端状态标记,true:在线,false:离线状态
    QTcpSocket tcpSocket;//和服务器通信的tcp套接字
    QHostAddress serverIp;//服务器地址
    quint16 serverPort;//服务器端口
    QString username;//聊天室昵称
};

#endif // CLIENTDIALOG_H

clientdialog.cpp

#include "clientdialog.h"
#include "ui_clientdialog.h"

ClientDialog::ClientDialog(QWidget *parent) :
    QDialog(parent),
    ui(new Ui::ClientDialog)
{
    ui->setupUi(this);
    status = false;
    //和服务器连接成功时,tcpSocket发送信号:connected
    connect(&tcpSocket,SIGNAL(connected()),
            this,SLOT(onConnected()));
    //和服务器断开连接时,tcpSocket发送信号:disconnected
    connect(&tcpSocket,SIGNAL(disconnected()),
            this,SLOT(onDisconnected()));
    //收到聊天消息时,tcpSocket发送信号:readyRead
    connect(&tcpSocket,SIGNAL(readyRead()),
            this,SLOT(onReadyRead()));
    //网络通信异常时,tcpSocket发送信号:error
    connect(&tcpSocket,SIGNAL(error(QAbstractSocket::SocketError)),
            this,SLOT(onError()));
}

ClientDialog::~ClientDialog()
{
    delete ui;
}
//发送按钮对应的槽函数
void ClientDialog::on_sendButton_clicked()
{
    //获取用户输入的消息
    QString msg = ui->messageEdit->text();
    if(msg == ""){
        return;
    }
    msg = username + ":" + msg;
    //发送消息
    tcpSocket.write(msg.toUtf8());
    //清空已输入的消息
    ui->messageEdit->clear();
}
//连接服务器按钮对应的槽函数
void ClientDialog::on_connectButton_clicked()
{
    if(status == false){//如果当前是离线状态,则连接服务器
        //获取服务器IP
        if(serverIp.setAddress(ui->serverIpEdit->text())==false){
            //critical():表示错误的消息提示框
            QMessageBox::critical(this,"Error","IP地址错误");
            return;
        }
        //获取服务器端口
        serverPort = ui->serverportEdit->text().toShort();
        if(serverPort < 1024){
            QMessageBox::critical(this,"Error","端口格式错误");
            return;
        }
        //获取聊天室昵称
        username = ui->usernameEdit->text();
        if(username == ""){
            QMessageBox::critical(this,"Error","聊天室昵称不能为空");
            return;
        }
        //向服务器发送连接请求:
        //如果成功,发送信号:connected;
        //如果失败,发送信号:error
        tcpSocket.connectToHost(serverIp,serverPort);
    }
    else{//如果当前是在线状态,则断开和服务器连接
        //向服务器发送离开聊天室的消息
        QString msg = username + ":离开了聊天室";
        //toUtf8():将QString(unicode)转换QByteArray(utf-8)
        tcpSocket.write(msg.toUtf8());
        //断开连接
        //断开后发送信号:disconnected
        tcpSocket.disconnectFromHost();
    }
}
//和服务器连接成功时执行的槽函数
void ClientDialog::onConnected(void)
{
    status = true;//设置状态标记:在线
    ui->sendButton->setEnabled(true);//恢复"发送"按钮为正常可用状态
    ui->serverIpEdit->setEnabled(false);//禁用ip输入
    ui->serverportEdit->setEnabled(false);//禁用端口输入
    ui->usernameEdit->setEnabled(false);//禁用昵称输入
    ui->connectButton->setText("离开聊天室");//修改连接服务器按钮文本
    //向服务器发送进入聊天室提示消息
    QString msg = username + ":进入了聊天室";
    //toUtf8():将QString(unicode)转换QByteArray(utf-8)
    tcpSocket.write(msg.toUtf8());
}
//和服务器断开连接时执行的槽函数
void ClientDialog::onDisconnected(void)
{
    status = false;//设置离线状态
    ui->sendButton->setEnabled(false);//禁用"发送"按钮
    ui->serverIpEdit->setEnabled(true);//恢复ip输入
    ui->serverportEdit->setEnabled(true);//恢复端口输入
    ui->usernameEdit->setEnabled(true);//恢复昵称输入
    ui->connectButton->setText("连接服务器");//修改离开聊天室按钮文本
}
//接收服务器转发聊天消息的槽函数
void ClientDialog::onReadyRead(void)
{
    //bytesAvailable():获取等待读取消息的字节数
    if(tcpSocket.bytesAvailable()){
        //读取消息
        QByteArray buf = tcpSocket.readAll();
        //显示消息到界面
        ui->listWidget->addItem(buf);
        ui->listWidget->scrollToBottom();
    }
}
//网络通信异常时执行的槽函数
void ClientDialog::onError(void)
{
    //errorString():获取网络通信异常原因的字符串
    QMessageBox::critical(this,"Error",tcpSocket.errorString());
}

main.cpp

#include "clientdialog.h"
#include <QApplication>

int main(int argc, char *argv[])
{
    QApplication a(argc, argv);
    ClientDialog w;
    w.show();

    return a.exec();
}

最终实现效果:

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持脚本之家。

相关文章

  • C++使用循环计算标准差的代码实现

    C++使用循环计算标准差的代码实现

    在C++中,计算标准差可以使用循环来实现,本文给大家介绍了一个示例代码,演示了如何使用循环计算标准差,文中示例代码介绍的非常详细,具有一定的参考价值,需要的朋友可以参考下
    2023-12-12
  • C语言图书管理系统简洁版

    C语言图书管理系统简洁版

    这篇文章主要为大家详细介绍了C语言图书管理系统简洁版,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2018-01-01
  • C++设计模式之桥接模式

    C++设计模式之桥接模式

    这篇文章主要介绍了C++设计模式之桥接模式,本文讲解了什么是桥接模式、为什么要使用桥接模式、什么时候使用桥接模式等内容,需要的朋友可以参考下
    2014-09-09
  • C++ 数据结构超详细讲解顺序表

    C++ 数据结构超详细讲解顺序表

    程序中经常需要将一组数据元素作为整体管理和使用,需要创建这种元素组,用变量记录它们,传进传出函数等。一组数据中包含的元素个数可能发生变化,顺序表则是将元素顺序地存放在一块连续的存储区里,元素间的顺序关系由它们的存储顺序自然表示
    2022-03-03
  • C++ 多重继承和虚拟继承对象模型、效率分析

    C++ 多重继承和虚拟继承对象模型、效率分析

    本文简单介绍多态和多重继承、虚拟继承的基本概念。随后重点分析了C++中对象模型之间的差异和运行效率
    2014-08-08
  • QT利用QPainter绘制三维饼状图

    QT利用QPainter绘制三维饼状图

    这篇文章主要为大家详细介绍了如何利用QPainter实现三维饼状图的绘制,由于Qt中没有三维饼状图的绘制组件,因此只能自行绘制,感兴趣的可以动手尝试一下
    2022-06-06
  • C++实现合并两个排序的链表

    C++实现合并两个排序的链表

    这篇文章主要为大家详细介绍了C++实现合并两个排序的链表,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2019-03-03
  • Swift编程中的泛型解析

    Swift编程中的泛型解析

    这篇文章主要介绍了Swift编程中的泛型解析,是Swift入门学习中的基础知识,需要的朋友可以参考下
    2015-11-11
  • C语言中格式化输出符号%d、%c、%p、%x等详解

    C语言中格式化输出符号%d、%c、%p、%x等详解

    格式化输出在C语言中非常常用,提供了多种用法来控制输出的格式,下面这篇文章主要给大家介绍了关于C语言中格式化输出符号%d、%c、%p、%x等的相关资料,文中通过代码介绍的非常详细,需要的朋友可以参考下
    2024-06-06
  • C++实现LeetCode(205.同构字符串)

    C++实现LeetCode(205.同构字符串)

    这篇文章主要介绍了C++实现LeetCode(205.同构字符串),本篇文章通过简要的案例,讲解了该项技术的了解与使用,以下就是详细内容,需要的朋友可以参考下
    2021-07-07

最新评论