当前位置: 首页 > article >正文

【QT】实现TCP服务器,客户端之间的通信

         使用QT完成了服务器端和客户端的简单搭建,和纯C语言还是有一定的区别,实现了服务器能够接收客户端连接,能够接收客户端发来的信息显示在窗口中

目录

 1、服务器端代码

2、客户端代码

 输出结果如下:


1、服务器端代码

 widget.h

#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_startSerBtn_clicked();


    void newConnection_slot();     //自定义处理newConnection槽函数
    void readRead_solt();          //

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


};
#endif // WIDGET_H

 widget.h

#include "widget.h"

#include <QApplication>

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

 widget.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_startSerBtn_clicked()
{
    if(ui->startSerBtn->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->startSerBtn->setText("关闭服务器");

        //如果有客户端发来连接请求,那么该服务器就会自动发射一个newConnection的信号
        //我们可以将该信号连接到自定义的槽函数中,处理后续操作
        connect(server, &QTcpServer::newConnection, this, &Widget::newConnection_slot);
    }
    else
    {
        //执行关闭服务器的动作
        server->close();


        //将行编辑器设置成可用状态
        ui->portEdit->setEnabled(true);

        //将按钮文本内容设置为启动服务器
        ui->startSerBtn->setText("启动服务器");

    }
}

//定义存放客户端信息的容器
void Widget::newConnection_slot()
{
    qDebug()<<"有新的客户端发来连接请求";

    //可以通过成员函数 newPendingConnection函数获取最新连接的客户端的套接字的地址
    //函数原型:virtual QTcpSocket *nextPendingConnection();
    //无参函数
    //返回值:最新的一个链接的套接字地址
    QTcpSocket *s = server->nextPendingConnection();

    //将该套接字放入客户端链表中
    socketList.push_back(s);

    //程序执行至此,一个服务器可以对应多个客户端,已经建立了连接
    //此时,如果有某个客户端发来数据,那么该客户端就会自动发射一个readyRead的信号
    //我们可以将该信号连接到自定义的槽函数中处理相关逻辑
    connect(s, &QTcpSocket::readyRead, this, &Widget::readRead_solt);
}

//自定义处理readyRead信号的槽函数的实现
void Widget::readRead_solt()
{
    //1、遍历链表中的所有客户端,如果客户端的状态为未连接,则直接移除出链表
    for(int i = 0; i < socketList.size(); i++)
    {
        //判断当前套接字 socketList[i] 是否失效
        //函数原型:SocketState state() const;
        //功能:返回当前套接字的状态
        //返回结果为0时,表示该套接字是未连接状态
        if(socketList[i]->state() == 0)
        {
            //将该套接字移除出链表
            socketList.removeAt(i);
        }
    }

    //2、遍历所有客户端,判断客户端中是否有数据可读,如果有数据可读,则表示是该客户端发来的信息
    for(int i=0; i<socketList.count(); i++)
    {
        //判断当前客户端是否有客户端可读
        //函数原型:qint64 bytesAvailable() const override;
        //参数无
        //返回值:返回当前客户端套接字中的待读数据,如果没有数据,则返回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);
                }
            }
        }
    }
}

2、客户端代码

 widget.h

#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 connect_slot();              //自定义处理connected的信号
    void on_connectButton_clicked();

private:
    Ui::Widget *ui;

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



};
#endif // WIDGET_H

 main.cpp

#include "widget.h"

#include <QApplication>

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

widegt.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);
}

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


void Widget::on_connectButton_clicked()
{
    //获取ui界面上的数据
    QString ip = ui->ipEdit->text();      //ip地址
    quint16 port = ui->portEdit->text().toUInt();    //端口号
    userName = ui->nameEdit->text();                 //用户名

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

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

void Widget::connect_slot()
{
    QMessageBox::information(this, "成功", "连接成功");
    //将相关组件禁用
    ui->ipEdit->setEnabled(false);
    ui->nameEdit->setEnabled(false);
    ui->portEdit->setEnabled(false);
    ui->connectButton->setEnabled(false);

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

 输出结果如下:

可以看到能够成功连接到客户端


http://www.kler.cn/news/305928.html

相关文章:

  • 国风编曲:了解国风 民族调式 五声音阶 作/编曲思路 变化音 六声、七声调式
  • 【开源免费】基于SpringBoot+Vue.JS在线旅游网站(JAVA毕业设计)
  • 威胁建模网络与云威胁
  • SQL进阶的技巧:如何实现某列的累计乘积?
  • Codeforces Round 921 (Div. 2) A~D
  • 英飞凌MCU第五代高性能CAPSENSE技术PSoC4000T
  • Leetcode 二叉树中根遍历
  • 力扣-96.不同的二叉搜索树 题目详解
  • Android Radio2.0——动态列表回调(七)
  • tcp、http和rpc
  • WebSocket详细介绍
  • OPEN AI o1已经像人类一样思考了。。。
  • 【iOS】present和push
  • 【AcWing】快速排序的Go实现
  • yolo训练出现Could not load library libcudnn_cnn_train.so.8问题及解决方法
  • 从大脑图谱/ROI中提取BOLD信号
  • 简单易懂的方式来解释机器学习(ML)和深度学习(DL)的区别与联系
  • 通信工程学习:什么是DWDM密集波分复用
  • 小众语言ruby在苹果中的初步应用
  • self-play RL学习笔记
  • 【开源免费】基于SpringBoot+Vue.JS购物商城网站(JAVA毕业设计)
  • ImDisk Toolkit将一部分RAM模拟成硬盘分区
  • 更新20240915机器视觉海康Visionmaster学习步骤
  • 解决tiktoken库调用get_encoding时SSL超时
  • Redis 与数据库数据一致性保证详解
  • MySQL——数据库的高级操作(二)用户管理(5)如何解决 root 用户密码丢失
  • 【QT】自制一个简单的时钟(跟随系统时间)
  • 9.15javaweb项目总结
  • vs code: pnpm : 无法加载文件 C:\Program Files\nodejs\pnpm.ps1,因为在此系统上禁止运行脚本
  • 【计网】从零开始使用UDP进行socket编程 --- 服务端业务实现