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

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

目录

1、服务器端代码

2、客户端代码

输出结果如下:


1、服务器端代码

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


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

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


};
#endif // WIDGET_H

widget.h

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

#include <QApplication>

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

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_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

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

private:
    Ui::Widget *ui;

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



};
#endif // WIDGET_H

main.cpp

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

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());
}

输出结果如下:

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

相关推荐
良许Linux几秒前
0.96寸OLED显示屏详解
linux·服务器·后端·互联网
努力学习编程的伍大侠1 分钟前
基础排序算法
数据结构·c++·算法
蜜獾云11 分钟前
docker 安装雷池WAF防火墙 守护Web服务器
linux·运维·服务器·网络·网络安全·docker·容器
小屁不止是运维13 分钟前
麒麟操作系统服务架构保姆级教程(五)NGINX中间件详解
linux·运维·服务器·nginx·中间件·架构
数据小爬虫@16 分钟前
如何高效利用Python爬虫按关键字搜索苏宁商品
开发语言·爬虫·python
Hacker_Oldv17 分钟前
WPS 认证机制
运维·服务器·wps
ZJ_.18 分钟前
WPSJS:让 WPS 办公与 JavaScript 完美联动
开发语言·前端·javascript·vscode·ecmascript·wps
Narutolxy23 分钟前
深入探讨 Go 中的高级表单验证与翻译:Gin 与 Validator 的实践之道20241223
开发语言·golang·gin
bitcsljl26 分钟前
Linux 命令行快捷键
linux·运维·服务器
ac.char29 分钟前
在 Ubuntu 下使用 Tauri 打包 EXE 应用
linux·运维·ubuntu