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

输出结果如下:

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

相关推荐
一个很帅的帅哥1 分钟前
实现浏览器的下拉加载功能(类似知乎)
开发语言·javascript·mysql·mongodb·node.js·vue·express
极客先躯10 分钟前
java和kotlin 可以同时运行吗
android·java·开发语言·kotlin·同时运行
shiming887914 分钟前
Python数据分析与可视化
开发语言·python·数据分析
kid_sup21 分钟前
C语言错题本
c语言·开发语言
凯哥是个大帅比31 分钟前
ubuntu20.04 GLIBC从2.35降级到2.31
linux
iHero34 分钟前
【Ubuntu】在 Ubuntu 22.04.3 LTS 安装 davfs2 通过 Nextcloud WebDAV 挂载到 Ubuntu 的目录上
linux·ubuntu·nextcloud
心勤则明35 分钟前
Netty配置SSL证书加密
服务器·https·ssl
清园暖歌35 分钟前
Ubuntu 不重装系统增加交换空间大小
linux·运维·ubuntu·交换空间
黎相思36 分钟前
操作系统迁移(CentOs -> Ubuntu)
linux·ubuntu·gitee·centos
写bug如流水40 分钟前
在Ubuntu 20.04上安装pgAdmin 4
linux·运维·ubuntu