怎么用QT实现TCP网络聊天室

2023-06-12 16:38:40 qt 网络 聊天室

今天小编给大家分享一下怎么用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();
}

最终实现效果:

怎么用QT实现TCP网络聊天室

相关文章