QT实现TCP/UDP通信

服务器端:

客户端:

服务器:

widget.h

cpp 复制代码
#ifndef WIDGET_H
#define WIDGET_H

#include <QWidget>
#include <QTcpServer>
#include <QTcpSocket>
#include <QList>
#include <QMessageBox>
#include <QDebug>


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_startButton_clicked();
    void newConnection_slot();
    void readyRead_slot();

private:
    Ui::Widget *ui;
    QTcpServer *server;//定义服务器指针
    QList<QTcpSocket*> socketList;//定义存放客户端信息的容器

};
#endif // WIDGET_H

widget.cpp

cpp 复制代码
#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_startButton_clicked()
{
    if(ui->startButton->text()== "启动服务器")
    {
        //执行启动服务器动作

        //获取ui界面的端口号  ???
        quint16 port = ui->portEdit->text().toUInt();

        //启动监听
        //函数原型 bool listen(const QHostAddress &address = QHostAddress::any,quint16 port = 0);
        //1->要监听的ip地址 写any 表示监听该主机上所有的网络接口
        //2->要监听的端口号 不指定 系统默认随机绑定一个端口号

        if(!server->listen(QHostAddress::Any,port))
        {
            QMessageBox::critical(this,"错误","服务器启动失败");
            return;
        }

        //程序启动至此表示服务器启动成功
        QMessageBox::information(this,"成功","服务器启动成功");

        //将行编辑器设置为不可用
        ui->portEdit->setEnabled(false);
        //将按钮文本内容设置为关闭服务器
        ui->startButton->setText("关闭服务器");

        //此时 如果有客户端发来请求 那么该服务器会自动发射一个newConnection信号
        //将该信号连接到自定槽函数中处理后续操作
        connect(server,&QTcpServer::newConnection,this,&Widget::newConnection_slot);
    }
    else
    {
        //执行关闭服务器操作
        server->close();
        //将行编辑器设置成为可用状态
        ui->portEdit->setEnabled(true);
        //将按钮文本内容设置为启动服务器
        ui->startButton->setText("启动服务器");
    }
}

void Widget::newConnection_slot()
{
    qDebug()<<"有新的客户端发来连接请求";

    //可以通过nextPendingConnection函数获取最新连接的客户端套接字的地址
    //函数原型 QTcpSocket *nextPendingConnection();
    //无参函数
    //返回值 最新的连接的套接字地址

    QTcpSocket *s = server->nextPendingConnection();
    //将套接字地址放入链表中

    socketList.push_back(s);
    //一个服务器对应多个客户端 已经建立连接
    //如果有客户端发来消息 自动发射readyRead信号
    connect(s,&QTcpSocket::readyRead,this,&Widget::readyRead_slot);
}
void Widget::readyRead_slot()
{
    //遍历链表中的所有客户端 如果客户端状态未连接 则直接移除出链表
    for (int i=0 ;i<socketList.size();i++)
    {
        //判断当前套接字 socketList[i] 是否失效
        //函数原型 SocketState state() const;
        //返回值 返回当前调节子状态
        //返回结果为0 表示未连接
        if(socketList[i]->state()==0)
        {
            socketList.removeAt(i);
        }
    }

    //遍历所有客户端 判断客户端中是否有数据刻度 如果有数据可读 则表示该客户端发来的消息
    for (int i =0;i<socketList.count();i++)
    {
        //判断当前 客户端中是否有数据可读
        //函数原型 qint64 byteAvailable() const overrde
        //参数无
        //返回值为当前客户端套接字中的待读数据 如果没有数据 则返回0
        if(socketList[i]->bytesAvailable()  !=0)
        {
            //读取当前套接字的内容
            QByteArray msg = socketList[i]->readAll();

            //将接受的消息展示到ui界面上
            ui->msgListWidget->addItem(QString::fromLocal8Bit(msg));

            //将收到的消息 全部发给其他客户端
            for (int j=0;j<socketList.length();j++)
            {
                if (i!=j)
                {
                    socketList[j]->write(msg);
                }
            }
        }
    }
}

客户端

widget.h

cpp 复制代码
#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_connectBtn_clicked();
    void connected_slot();           //自定义处理connected信号的槽函数
    void readyRead_slot();           //自定义处理readyRead信号的槽函数
    void disconnected_slot();        //自定义处理disconnected信号的槽函数
    void on_sendBtn_clicked();
    void on_disConnectBtn_clicked();


private:
    Ui::Widget *ui;
    //定义通信用的变量
    QTcpSocket *client;         //定义套接字指针
    QString userName;           //用户名


};
#endif // WIDGET_H

widget.cpp

cpp 复制代码
#include "widget.h"
#include "ui_widget.h"


Widget::Widget(QWidget *parent)
    : QWidget(parent)
    , ui(new Ui::Widget)
{
    ui->setupUi(this);
    //实例化客户端对象
    client = new QTcpSocket(this);

    //当客户端与服务器建立联系后,如果客户端接受到服务器发来的消息
    //客户端自身就会自动发射一个 readyRead的信号,我们可以将该信号连接到自定义的槽函数中执行相关逻辑
    connect(client, &QTcpSocket::readyRead, this, &Widget::readyRead_slot);

    //如果当前客户端成功连接的服务器,那么该客户端就会自动发射一个connected的信号
    //我们可以将该信号连接到自定义的槽函数中处理相关逻辑
    connect(client, &QTcpSocket::connected, this, &Widget::connected_slot);

    //当客户端断开了与服务器的连接后,该客户端就会自动发射一个disconnected的信号
    //我们可以将该信号连接到自定义的槽函数中处理相关逻辑
    connect(client, &QTcpSocket::disconnected, this, &Widget::disconnected_slot);
}


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


//连接服务器按钮对应的槽函数
void Widget::on_connectBtn_clicked()
{
    //获取ui界面上的数据
    QString ip = ui->ipEdit->text();     //ip地址
    quint16 port = ui->portEdit->text().toUInt();    //端口号
    userName = ui->nameEdit->text();              //用户名

    //调用套接字成员函数,连接服务器
    //函数原型:void connectToHost(const QHostAddress &address, quint16 port, OpenMode mode = ReadWrite);
    //参数1:要被连接的服务器ip地址
    //参数2:服务器的端口号
    //参数3:默认为可读可写
    //返回值:无
    client->connectToHost(ip, port);
}


//处理connected信号的槽函数的定义
void Widget::connected_slot()
{
    QMessageBox::information(this,"成功","连接成功");
    //将相关组件禁用
    ui->ipEdit->setEnabled(false);
    ui->nameEdit->setEnabled(false);
    ui->portEdit->setEnabled(false);
    ui->connectBtn->setEnabled(false);

    //向服务器发送一条消息
    QString msg = userName + ": 进入聊天室";
    client->write( msg.toLocal8Bit() );
}


//自定义处理readyRead信号的槽函数
void Widget::readyRead_slot()
{
    //从套接字中读取数据
    QByteArray msg = client->readAll();

    //将读取下来的数据展示到ui界面上
    ui->msgListWidget->addItem( QString::fromLocal8Bit(msg) );
}

//消息发送按钮对应的槽函数
void Widget::on_sendBtn_clicked()
{
    //组织要发送的消息
    QString msg = userName + ": " + ui->msgEdit->text();

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

    //将消息展示到自己界面上
    //准备一个QListWidgetItem类的对象
    QListWidgetItem *item = new QListWidgetItem(msg);
    item->setTextAlignment(Qt::AlignRight);        //将文本右对齐
    ui->msgListWidget->addItem(item);

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

//断开连接按钮对应的槽函数
void Widget::on_disConnectBtn_clicked()
{
    //执行断开连接的操作
    //准备发送消息给服务器
    QString msg = userName + ": 离开聊天室";
    client->write(msg.toLocal8Bit());

    //断开连接
    client->disconnectFromHost();
}


//自定义处理disconnected信号的槽函数的定义
void Widget::disconnected_slot()
{
    QMessageBox::information(this, "提示", "成功断开与服务器的连接");

    //将相关组件启用
    ui->ipEdit->setEnabled(true);
    ui->nameEdit->setEnabled(true);
    ui->portEdit->setEnabled(true);
    ui->connectBtn->setEnabled(true);
}
相关推荐
小糖学代码5 小时前
LLM系列:1.python入门:3.布尔型对象
linux·开发语言·python
shizhan_cloud5 小时前
Shell 函数的知识与实践
linux·运维
Deng8723473485 小时前
代码语法检查工具
linux·服务器·windows
霍夫曼7 小时前
UTC时间与本地时间转换问题
java·linux·服务器·前端·javascript
月熊8 小时前
在root无法通过登录界面进去时,通过原本的普通用户qiujian如何把它修改为自己指定的用户名
linux·运维·服务器
大江东去浪淘尽千古风流人物9 小时前
【DSP】向量化操作的误差来源分析及其经典解决方案
linux·运维·人工智能·算法·vr·dsp开发·mr
赖small强9 小时前
【Linux驱动开发】NOR Flash 技术原理与 Linux 系统应用全解析
linux·驱动开发·nor flash·芯片内执行
发光小北10 小时前
SG-PNh750-TCP-210(Profinet 从站转 Modbus TCP 网关)
网络·网络协议·tcp/ip
IT运维爱好者11 小时前
【Linux】LVM理论介绍、实战操作
linux·磁盘扩容·lvm
LEEE@FPGA11 小时前
ZYNQ MPSOC linux hello world
linux·运维·服务器