tcp的聊天室

2023-12-13 21:48:36

注意:要加库文件,服务端客户端都要加 network

客户端的头文件

#ifndef WIDGET_H
#define WIDGET_H

#include <QWidget>
#include <QTcpSocket>//客户端类
#include <QMessageBox>

QT_BEGIN_NAMESPACE
namespace Ui { class Widget; }
QT_END_NAMESPACE

class Widget : public QWidget
{
    Q_OBJECT

public:
    Widget(QWidget *parent = nullptr);
    ~Widget();

private slots:

    void on_sendbtn_clicked();

    void on_disconnectbtn_clicked();
    void on_connectbtn_clicked();
public slots:
    void connected_slot();
    void readyRead_slot();
    void disconnected_slot();

private:
    Ui::Widget *ui;
    //定义客户端指针
    QTcpSocket *socket;

    //定义存储用户名
    QString username;
};
#endif // WIDGET_H

客户端主函数

#include "widget.h"

#include <QApplication>

int main(int argc, char *argv[])
{
    QApplication a(argc, argv);
    Widget w;
    w.show();
    return a.exec();
}

?客户端构造函数

#include "widget.h"
#include "ui_widget.h"

Widget::Widget(QWidget *parent)
    : QWidget(parent)
    , ui(new Ui::Widget)
{
    ui->setupUi(this);
    //初始化界面
    //设置按钮不可以状态
    ui->msgEdit->setEnabled(false);
    ui->sendbtn->setEnabled(false);
    ui->disconnectbtn->setEnabled(false);

    //给客户端指针实例化空间
    socket=new QTcpSocket(this);

    connect(socket,&QTcpSocket::connected,this,&Widget::connected_slot);

    //此时客户端和服务器已经建立连接,如果服务器发来数据,那么客户端就会自动发射 一个readyRead()信号
    //我们可以将该信号连接到自定义的槽函数中,读取服务器端的数据,又由只需要连接一次,在构造函数写连接

    connect(socket,&QTcpSocket::readyRead,this,&Widget::readyRead_slot);

    //如果成功与服务器断开连接,那么该客户端就会自动发送一个disconnected信号
    //我们就可以将该信号连接到自定义函数中,处理逻辑代码,由于只要连接一次,所有在构造函数中连接

    connect(socket,&QTcpSocket::disconnected,this,&Widget::disconnected_slot);
}

Widget::~Widget()
{
    delete ui;
}

//连接服务器按钮对应的槽函数处理
void Widget::on_connectbtn_clicked()
{
    //获取ui界面上的ip和端口号
    QString ip=ui->ipEdit->text();
    quint16 port =ui->portEdit->text().toUInt();
    //将客户连接到服务器
    //参数1:主机地址
    //参数2:端口号
    socket->connectToHost(ip,port);

    //如果成功连接服务器,那么客户端将发送一个conneted信号
    //我们就可以将该信号连接到自定义的槽函数中处理逻辑代码,由于只需要连接一次,所有我们在构造函数中写连接

}


void Widget::connected_slot()
{
    QMessageBox::information(this,"","连接服务器成!");

    //告诉服务器,用户上线
    username =ui->userEdit->text();

    QString msg=username+": 进入聊天室";

    //将信息发送给服务器
    socket->write(msg.toLocal8Bit());

    //将ui界面上的组件进行相关设置

    //可用状态
    ui->msgEdit->setEnabled(true);
    ui->sendbtn->setEnabled(true);
    ui->disconnectbtn->setEnabled(true);

    //不可以状态
    ui->userEdit->setEnabled(false);
    ui->ipEdit->setEnabled(false);
    ui->portEdit->setEnabled(false);
    ui->connectbtn->setEnabled(false);

    //此时客户端和服务器已经建立连接,如果服务器发来数据,那么客户端就会自动发射 一个readyRead()信号
    //我们可以将该信号连接到自定义的槽函数中,读取服务器端的数据,又由只需要连接一次,在构造函数写连接

}

void Widget::readyRead_slot()
{
    //将服务器端的数据读取出来
    QByteArray msg =socket->readAll();

    //将数据放入ui界面上
    ui->msgWidget->addItem(QString::fromLocal8Bit(msg));
}


//disconnected信号对应的槽函数的实现
void Widget::disconnected_slot()
{
    QMessageBox::information(this,"","断开服务器成功");
    ui->msgEdit->setEnabled(false);
    ui->sendbtn->setEnabled(false);
    ui->disconnectbtn->setEnabled(false);

    ui->userEdit->setEnabled(true);
    ui->ipEdit->setEnabled(true);
    ui->portEdit->setEnabled(true);
    ui->connectbtn->setEnabled(true);

}

//发送按钮对应的槽函数
void Widget::on_sendbtn_clicked()
{
    //获取ui界面上的数据
    QString msg=ui->msgEdit->text();

    //整合信息
    msg=username+":"+msg;

    //将数据发送给服务器
    socket->write(msg.toLocal8Bit());

    //清空发送框里的内容
    ui->msgEdit->clear();
}

//端口服务器按钮的槽函数处理
void Widget::on_disconnectbtn_clicked()
{
    //告诉大家我走了
    QString msg =username+":离开聊天室";

    //信息发送给服务器
    socket->disconnectFromHost();

    //如果成功与服务器断开连接,那么该客户端就会自动发送一个disconnected信号
    //我们就可以将该信号连接到自定义函数中,处理逻辑代码,由于只要连接一次,所有在构造函数中连接
}

客户端ui界面

?

服务端头文件

#ifndef WIDGET_H
#define WIDGET_H

#include <QWidget>
#include <QTcpServer>
#include <QList> //链表容器
#include <QTcpSocket> //客户端的类
#include <QMessageBox> //消息对话框类

QT_BEGIN_NAMESPACE
namespace Ui { class Widget; }
QT_END_NAMESPACE

class Widget : public QWidget
{
    Q_OBJECT

public:
    Widget(QWidget *parent = nullptr);
    ~Widget();

private slots:
    void on_staetBtn_clicked();
    void newConnection_solt(); //newconnection信号对应的槽函数
    void readyRead_slot();

private:
    Ui::Widget *ui;
    //定义服务器指针
    QTcpServer *server;


    //定义客户端容器
    QList<QTcpSocket *> socketList;

};
#endif // WIDGET_H

服务端主函数

#include "widget.h"

#include <QApplication>

int main(int argc, char *argv[])
{
    QApplication a(argc, argv);
    Widget w;
    w.show();
    return a.exec();
}

服务的构造函数

#include "widget.h"
#include "ui_widget.h"

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

    //给服务器指针实例化空间
    server =new QTcpServer(this);


}

Widget::~Widget()
{
    delete ui;
}

//启动服务器按钮 对应的槽函数
void Widget::on_staetBtn_clicked()
{
    //获取ui界面上的端口号
    quint16 port = ui->portEdit->text().toInt();

   //将服务器设置监听状态
   //函数原型:bool
   //参数1:主机地址 可以是任意
    //参数2:端口号
    if(server->listen(QHostAddress::Any,port))
    {
        QMessageBox::information(this,"","启动服务器成功!");
    }
    else
    {
        QMessageBox::information(this,"","启动服务器失败!");
    }

    //此时说明服务器已经进入监听状态,如果有客户端发来连接请求,那么服务器端就会自动发射newconnection
    //将该信号连接到自定义的槽函数中,获取客户端的套接字
    connect(server,&QTcpServer::newConnection,this,&Widget::newConnection_solt);
}

//newConnection信号对应槽函数处理
void Widget::newConnection_solt()
{
    qDebug() <<"有新的用户连接" ;

    //获取最新连接的客户端套接字
    //函数原型:virtual QTcpSocket *nextPendingConnection();
    //返回值:是客户端套接字的指针
    QTcpSocket *s =server->nextPendingConnection();

    //将套接字放入容器中
    socketList.push_back(s);

    //程序运行至此,说明服务端和客户端已经建立起联系,如果客户端发来数据,那么客户端就会自动发射readyrRead()信号
    //我们就可以将信号连接自定义的槽函数中,读取客户端的数据
    connect(s,&QTcpSocket::readyRead,this,&Widget::readyRead_slot);


}
//readyRead信号对应槽函数声明
void Widget::readyRead_slot()
{
    //移除无效客户端
    //count :在容器中的所有元素个数
    for(int i=0;i<socketList.count();i++)
    {
        //函数原型:socketstate state() const
        if(socketList.at(i)->state()==0)
        {
            //移除
            socketList.removeAt(i);//将下表为i的 客户端移除

        }
    }

    //遍历有效客户端,寻找哪个客户端有数据待读
    for (int i=0;i<socketList.count();i++)
    {
        //函数原型:
        //判断是否有数据
        if(socketList.at(i)->bytesAvailable()!=0)
        {
            //读取套接字中的数据
            QByteArray msg = socketList.at(i)->readAll();

            //将数据放在ui界面上
            ui->msgWidget->addItem(QString::fromLocal8Bit(msg));

            //将数据广播给(发送)给所有客户端
            for(int j=0;j<socketList.count();j++)
            {
                //将数据写入到套接字中
                socketList.at(j)->write(msg);
            }

        }
    }
}

服务端ui界面

?

文章来源:https://blog.csdn.net/2201_75550624/article/details/134981181
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。