[Qt] tcp服务器连接多个客户端的实现

2023-11-12

[Qt]tcp服务器连接多个客户端的实现

~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

要求:数据按字节接收,以1~255个字节循环发送
编译环境:Qt 5.9.5

客户端的实现:

代码如下:
TcpClient.h:
#ifndef TCPCLIENT_H
#define TCPCLIENT_H

#include <QWidget>
#include <QTcpSocket>
#include <iostream>
#include <string>
using namespace std;


namespace Ui {
class TcpClient;
}

class TcpClient : public QWidget
{
    Q_OBJECT

public:
    explicit TcpClient(QWidget *parent = 0);
    ~TcpClient();

private slots:
    void on_pushButtonEnter_clicked();
    void slotconnectedsuccess();  //处理连接成功的信号
    void slotreceive();   //接收服务器传过来的信息
    void on_pushButtonSend_clicked();
    void slotdisconnected();  //处理离开聊天室的信号


private:
    Ui::TcpClient *ui;
    bool status;     //用来判断是否进入了服务器
    size_t strSize;
    int port;
    QHostAddress *serverIP;
    QString userName;
    QTcpSocket *tcpsocket;
};

#endif // TCPCLIENT_H

TcpClient.cpp:
#include "tcpclient.h"
#include "ui_tcpclient.h"
#include <QHostAddress>
#include <QMessageBox>

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

    //将进入服务器的标志位置为false
    status = false;
    port = 8888;
    ui->lineEditServerPort->setText(QString::number(port));

    serverIP = new QHostAddress();

    //未连接内不能发送信息
    ui->pushButtonSend->setEnabled(false);
}

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

//进入服务器
void TcpClient::on_pushButtonEnter_clicked()
{
    //首先判断这个用户是不是在服务器中
    if(status == false) {  //不在服务器中就和服务器进行连接
        QString ip = ui->lineEditServerIp->text();
        if(!serverIP->setAddress(ip)) {   //判断IP地址是否可以被正确解析
            QMessageBox::warning(this, "错误", "IP地址不正确");
            return;
        }
        if(ui->lineEditUserName->text() == "") {
            QMessageBox::warning(this, "错误", "用户名不能为空");
            return;
        }
        userName = ui->lineEditUserName->text();

        tcpsocket = new QTcpSocket(this);
        tcpsocket->connectToHost(*serverIP, port);

        //和服务器连接成功能会触发connected信号
        connect(tcpsocket, &QTcpSocket::connected, this, &TcpClient::slotconnectedsuccess);

        //接收到服务器的信息就会触发readyRead信号
        connect(tcpsocket, &QTcpSocket::readyRead, this, &TcpClient::slotreceive);

        //将进入服务器的标志位置为true;
        status = true;
    }
    else {   //已经进入了服务器
        //int length = 0;
        QString msg = userName + ":disconnected";
        tcpsocket->write(msg.toUtf8().data());
        tcpsocket->disconnectFromHost();
        status = false;
        //离开服务器就会触发disconnected信号
        connect(tcpsocket, &QTcpSocket::disconnected, this, &TcpClient::slotdisconnected);
    }
}

//用来处理连接成功的信号
void TcpClient::slotconnectedsuccess()
{
    ui->textEdit->append("连接成功");

    //进入服务器可以发送信息了
    ui->pushButtonSend->setEnabled(true);
    //将连接服务器的按钮改为离开服务器
    ui->pushButtonEnter->setText("离开服务器");

    QString msg = userName + ": connected";
    tcpsocket->write(msg.toUtf8().data());
}


void TcpClient::slotreceive()
{
    QByteArray array = tcpsocket->readAll();

    ui->textEdit->append(array.data());
}

void TcpClient::on_pushButtonSend_clicked()
{
    if(ui->lineEditSend->text() == "") {
        return;
    }

    //获取编辑区内容
    QString str = ui->lineEditSend->text();

    //加头信息
    qint64 textSize = str.size();

    //userName = userName + ": ";

    QString strHead = QString("%1##%2##%3").arg(userName + ": ").arg(textSize).arg(str);

    tcpsocket->write(strHead.toUtf8().data());

    ui->lineEditSend->clear();
}

void TcpClient::slotdisconnected()
{
    ui->pushButtonSend->setEnabled(false);
    ui->pushButtonEnter->setText("连接服务器");
}

在这里插入图片描述

服务端的实现:

tcpserver.h:
#ifndef TCPSERVER_H
#define TCPSERVER_H

#include <QWidget>
#include "server.h"

namespace Ui {
class TcpServer;
}

class TcpServer : public QWidget
{
    Q_OBJECT

public:
    explicit TcpServer(QWidget *parent = 0);
    ~TcpServer();

protected slots:
    void slotupdateserver(QString, int);  //接收server发过来的信号就更新界面信息

private slots:
    void on_Button_waitConnect_clicked();

private:
    Ui::TcpServer *ui;

    int port;
    Server *server;

};

#endif // TCPSERVER_H

tcpserver.cpp:
#include "tcpserver.h"
#include "server.h"
#include "ui_tcpserver.h"
#include <QDebug>

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

    port = 8888;
}

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


void TcpServer::on_Button_waitConnect_clicked()
{
    server  = new Server(this, port);

    connect(server, &Server::updateserver, this, &TcpServer::slotupdateserver);


    ui->textEdit->append("创建成功,可以连接");

    ui->Button_waitConnect->setEnabled(false);
}


void TcpServer::slotupdateserver(QString msg, int length)
{
    ui->textEdit->append(msg);
}

重写incomingconnected() , 监听
server.h
#ifndef SERVER_H
#define SERVER_H

#include <QTcpServer>
#include <QObject>
#include <QList>
#include "tcpclientsocket.h"


class Server : public QTcpServer
{
    Q_OBJECT
public:
    explicit Server(QObject *parent = 0, int port = 0);
    QList<TcpClientSocket*> tcpclientsocketlist;

protected:
    virtual void incomingConnection(qintptr socketDescriptor);  //只要出现一个新的连接,就会自动调用这个函数

protected slots:
    void slotupdateserver(QString, int);    //用来处理tcpclient发过来的信号
    void slotclientdisconnect(int);

signals:
    void updateserver(QString, int);    //发送信号给界面, 让界面更新信息

};

#endif // SERVER_H

server.cpp:
#include "server.h"
#include "tcpclientsocket.h"
#include <QHostAddress>
#include <QDebug>

Server::Server(QObject *parent, int port) : QTcpServer(parent)
{
    listen(QHostAddress::Any, port);   //监听

}

void Server::incomingConnection(qintptr socketDescriptor)
{
    //只要有新连接就生成一个新的套接字
    TcpClientSocket *tcpclientsocket = new TcpClientSocket(this);
    tcpclientsocket->setSocketDescriptor(socketDescriptor);

    //将新创建的套接字加入到客户端套接字列表中
    tcpclientsocketlist.append(tcpclientsocket);

    //接收到tcpclientsocket发过来的更新界面的信号
    connect(tcpclientsocket, &TcpClientSocket::updateserver, this, &Server::slotupdateserver);
    connect(tcpclientsocket, &TcpClientSocket::clientDisconnected, this, &Server::slotclientdisconnect);

    //return emit  QTcpServer::newConnection();
}

void Server::slotupdateserver(QString msg, int length)
{
    //将这个信号发送给界面
    emit updateserver(msg, length);

    QString userName = msg.section("##", 0, 0);
    QString text = msg.section("##", 1, 1);

    //将收到的信号发送给每个客户端,从套接字列表中找到需要接收的套接字
    for(int i = 0; i < tcpclientsocketlist.count(); i++) {
        QTcpSocket *item = tcpclientsocketlist.at(i);

        QString sendInfo = userName + " ";
        item->write(sendInfo.toUtf8().data());
        //处理数据,按格式发送
        QString str = text;
        string buf = str.toLatin1().data();
        string sendText;   //存放要发送的数据
        size_t j = 0;
        while(j < buf.size()) {
            for(size_t i = 1; i <= 11; i++) {
                if((j + i) <= buf.size()) {

                    if(i == 11) {
                        i = 1;
                    }

                    sendText = buf.substr(j, i);
                    //发送数据
                    sendText =  sendText + "~~~";
                    //tcpsocket->write(sendText.data());

                    item->write(sendText.data());
                }
                else {
                    sendText = buf.substr(j, (buf.size() - j));
                    //发送数据
                    sendText = sendText + "~~~";
                    item->write(sendText.data());
                    break;
                }
                j += i;
//                if(i == 255) {
//                    i = 1;
//                }
                sendText.clear();
            }
            break;
        }




    }
}

void Server::slotclientdisconnect(int descriptor)
{
    for(int i = 0; i < tcpclientsocketlist.count(); i++) {
        QTcpSocket *item = tcpclientsocketlist.at(i);
        //如果有客户端断开连接,将列表中的套接字删除
        if(item->socketDescriptor() == descriptor) {
            tcpclientsocketlist.removeAt(i);
            return;
        }
    }
    return;
}

通信
tcpsocket.h:
#ifndef TCPCLIENTSOCKET_H
#define TCPCLIENTSOCKET_H

#include <QTcpSocket>
#include <iostream>
#include <string>
using namespace std;


class TcpClientSocket : public QTcpSocket
{
    Q_OBJECT
public:
    explicit TcpClientSocket(QObject *parent = nullptr);

protected slots:
    void recvdata();  //处理readyRead信号读取数据
    void slotclientdisconneceted();  //客户端断开触发disconnected,处理这个信号

signals:
    void updateserver(QString, int);  //用来告诉tcpserver需要更新界面
    void clientDisconnected(int);   //告诉server有客户端断开

public slots:

private:
    size_t strSize;
};

#endif // TCPCLIENTSOCKET_H

tcpsocket.cpp:
#include "tcpclientsocket.h"
#include <QDebug>

TcpClientSocket::TcpClientSocket(QObject *parent) : QTcpSocket(parent)
{
    //客户端发过来的数据触发readyRead信号
    connect(this, &TcpClientSocket::readyRead, this, &TcpClientSocket::recvdata);
    connect(this, &TcpClientSocket::disconnected, this, &TcpClientSocket::slotclientdisconneceted);
}

void TcpClientSocket::recvdata()
{
    QByteArray array = readAll();
    int length = array.size();

    QString userName = QString(array).section("##", 0, 0);

    strSize = QString(array).section("##", 1, 1).toInt();
    //正文内容
    string strText = QString(array).section("##", 2, 2).toLatin1().data();
    string buf;
    for(size_t i = 0; i < strText.size(); i++) {
        buf.append(strText.substr(i, 1));
    }
    if(strSize != buf.size()) {
        qDebug() << "读取出错";
        return;
    }

    if(buf.size() == 0) {
        QString msg = userName;
        emit updateserver(msg, length);
    }
    else {
        QString msg = userName + "##" + buf.data();
        emit updateserver(msg, length);
    }

}

void TcpClientSocket::slotclientdisconneceted()
{
    emit clientDisconnected(this->socketDescriptor());
}

界面布局

在这里插入图片描述

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

[Qt] tcp服务器连接多个客户端的实现 的相关文章

  • ACTF新生赛2020 frequency

    ACTF新生赛2020 frequency 1 题目概述 2 解题过程 根据题目名称frequency与文件内容猜测应该是字频方向 如果打开文档是空白的 就搜索 显示隐藏内容 a2draGxmY290bnRpdWZwZ2hodGN3dWpr
  • IDEA gradle项目出现java.lang.OutOfMemoryError: GC overhead limit exceeded 之类的错误

    试了很多很多方法都不管用 最后在gradle目录下添加gradle properties文件得以解决内容如下 项目所在目录的磁盘空间一定要比下面的配置大才行 org gradle daemon true org gradle configu
  • 高手手把手教你组件封装步骤

    我将在这个博客中更新各种组件的封装 轮播图 骨架屏 复选框 对话框 面包屑组件 看到就是学到 偷着乐吧 轮播图封装详解 注册一个vue文件 在src的components中注册一个Carousel vue 在vue use中注册全局组件 i
  • obs上传文件到服务器,文件上传到obs

    文件上传到obs 内容精选 换一换 设备上报文件上传结果 Topic oc devices device id sys events upServiceEvent定义表paras参数列表 从OBS导入数据到集群之前 需要提前准备数据源文件
  • Vue3——Axios(网络请求库)

    文章目录 求一键三连 前言 认识axios 使用Axios 常见请求演示 配置选项 baseURL all 创建axios实例 请求拦截 掌握 对axios的类的封装 掌握 老师封装好的一个更完全的axios库 求一键三连 希望大家看完觉得
  • Web3j签名与验签

    闲时 给大家讲讲本人在做项目过程中涉及到的区块链签名相关业务 希望对做区块链行业的同行有所帮助 主要是针对Java程序员围绕web3j库进行区块链以太坊开发 本地签名与验签 代码实例 Description Web3j签名验签 public
  • 什么叫基本表?什么是视图?二者的区别和联系是什么?

    2019独角兽企业重金招聘Python工程师标准 gt gt gt 视图 在SQL中 视图是外模式一级数据结构的基本单位 它是从一个或几个基本表中导出的 表 是从现有基本表中抽取若干子集组成用户的 专用表 基本表 基本表的定义指建立基本关系
  • minio怎么连接文件服务器,【FastAPI基础】17.2、接入docker minio文件服务器操作文件,附源码...

    引言 最近工作中有机会接触FastAPI这个框架 所以就把官方文档看了一遍 对框架的各个特性及使用方法做了总结 会逐步的发出来 希望对您有用 如果您之前接触过python的其他框架 看起来会非常简单和顺畅 其实就是很简单 废话不多说 直接上
  • [Error] invalid operands to binary ^ (have ‘double‘ and ‘float‘)

    C C 中不能直接使用 在C C 中不能使用 来表示指数 只能用 如果想使用指数 只能建立循环多次相乘或者直接用乘法写出多个 下面是我的代码 注释部分为原来使用的指数形式 会报以上错误 或者引用数学函数 在前面加上 include
  • Python3 基本数据类型(List(列表))

    Python3 基本数据类型 List 列表 List 列表 是 Python 中使用最频繁的数据类型 列表可以完成大多数集合类的数据结构实现 列表中元素的类型可以不相同 它支持数字 字符串甚至可以包含列表 所谓嵌套 列表是写在方括号 之间
  • 2023国赛数学建模思路 - 案例:最短时间生产计划安排

    文章目录 0 赛题思路 1 模型描述 2 实例 2 1 问题描述 2 2 数学模型 2 2 1 模型流程 2 2 2 符号约定 2 2 3 求解模型 2 3 相关代码 2 4 模型求解结果 建模资料 0 赛题思路 赛题出来以后第一时间在CS
  • 传奇服务端GOM引擎和GEE引擎区别在哪里?

    关于GameOfMir跟GEE引擎之间的区别简单说Gom引擎支持了BDE数据库 Access数据库 而GEE目前还没有支持BDE数据库 mysql数据库可惜mysql数据库支持还不完善 引擎没有绝对的好坏主要是在使用上操作上功能都差不多今天
  • 【安利】mac免费开源文字识别-使用脚本和快捷指令-可识别中文(利用macOCR)

    前言 在mac上 常用的截图文本识别软件有TextScanner iText等等 但是它们都是付费的 这次我无意发现了利用脚本和苹果快捷指令就可以使用的开源截图文本识别软件 推荐给大家 github地址为 schappim macOCR G
  • word2vec中文相似词计算和聚类的使用说明及c语言源码

    word2vec相关基础知识 下载安装参考前文 word2vec词向量中文文本相似度计算 目录 word2vec使用说明及源码介绍 1 下载地址 2 中文语料 3 参数介绍 4 计算相似词语 5 三个词预测语义语法关系 6 关键词聚类 1
  • Django 运行报错 Manager isnt accessible via Category instances

    错误 Manager isn t accessible via Category instances 原因 views handler里面写错 category s Category objects all 正确的写法是 category
  • 如何运用krpano生成第一全景项目

    使用droplet制作 生成一个全景 下载好krpano包后 解压文件夹 可以看到文件夹下有诸多文件 这时打开全景图片 拖入 droplet bat 文件 马上立刻就生成一个全景项目文件了 打开新生成的文件夹 打开tour html文件 用
  • Linux磁盘分区UUID查看和修改

    查看磁盘的UUID 方法1 root ac810 blkid dev sda2 UUID 4e87d18b 3223 4f9d aa48 2bd450ad33d0 TYPE ext4 PARTUUID 337a99de 78d8 4d7d
  • R语言-画散点图

    plot 函数 plot cars dist cars speed y x main XXX 画标题 xlab XXX X坐标轴标题 ylab XXX Y坐标轴标题 xlim c 0 30 设置X轴范围为从0到30 ylim c 0 140
  • sklearn中的fit_transform、获取特征、标签转换

    skearn中有数据预处理的功能 如fit transform 和fit transform fit 求得训练集X的均值 方差 最大值 最小值 这些训练集X固有的属性 transform 在fit的基础上 进行标准化 降维 归一化等操作 f
  • Linux如何检查目录大小

    1 查看当前文件目录各个文件夹大小 du h max depth 1 查看指定目录 du h max depth 1 path 2 查看当前目录下所有目录及子目录大小 du h 代表当前目录下 也可以换成一个明确的路径 h表示用K M G的

随机推荐

  • 基于opencv-python的人脸识别和鸟图识别

    目录 图像识别 人脸识别 鸟图识别 一 人脸识别 即使是地铁挤压的人脸 也是有尊严的 值得被检测 经过 OpenCV 的努力 成功检测 左图是正常被识别的人脸 中图由于挤地铁人脸已不可识别 右图OpenCV单应性变换后 拯救了被miss的人
  • Docker部署Jupyter

    Docker部署Jupyter Docker安装 拉取镜像 docker pull jupyter base notebook 创建挂载目录 mkdir p opt jupyter jovyan chmod 777 opt jupyter
  • 利用 Python PyPDF2库轻松提取PDF文本(及其他高级操作)

    当需要从PDF文件中提取文本时 Python中的PyPDF2库是一个非常有用的工具 无论您是需要分析PDF文档中的内容还是需要在文档中搜索特定的信息 PyPDF2都可以帮助您轻松实现这些任务 在本文中 我们将探讨如何使用PyPDF2库提取P
  • Python编写时钟程序(运行结果在“视频“选项里)(适合学过编程2年00个月~2年11个月的人(中级篇))

    1 初始设置 from datetime import 导入相关库 from turtle import from time import sleep setup 675 675 设置窗口大小 hideturtle 隐藏海龟 mode lo
  • ElasticSearch 7.7.0 高级篇-搜索技术

    前言 有了前面的理论知识和上机实操的经验 那么下面我们将使用程序开发es 当然本篇说白了就是前面知识的总结和回顾 一 ES不分词 exact value 搜索 1 1 实战体验term filter各种不分词搜索 term filter q
  • LdapTemplate对AD域进行查询1000条记录限制,实测有效!

    公司的一个项目要从AD上取数据 但是用SpringLdap获取所有用户的时候会默认显示1000条数据 ldapTemplate search query base OU xxxx DC xxxx where objectclass is p
  • 数据压缩实验之 JPEG原理分析及JPEG解码器的调试

    文章目录 1 实验名称 2 实验目的 3 主要设备 4 实验内容 4 1 JPEG文件格式 4 2 JPEG编解码原理 4 2 1 编码流程 4 2 2 解码流程 5 实验步骤 5 1 逐步调试JPEG解码器程序 5 1 1 理解程序设计的
  • 传统机器学习和深度学习

    传统机器学习 传统机器学习从一些观测 训练 样本出发 试图发现不能通过原理分析获得的规律 实现对未来数据行为或趋势的准确预测 相关算法包括 逻辑回归 隐马尔科夫方法 支持向量机方法 K 近邻方法 三层人工神经网络方法 Adaboost算法
  • 计算机网络 二、网络协议

    一 定义 网络协议是计算机网络中互相通信的对等实体间交换信息时必须要遵守的规则的集合 二 划分层次 三 计算机的体系结构 3 1 具有五层协议的体系结构 具有五层协议的体系结构具 有五层协议的体系结构 1 应用层 application l
  • JNDI和数据库连接池

    1 JNDI 含义 JNDI Java Naming and Directory Interface java命名与目录的接口 是一个有关应用程序设计API 为开发人员提供了查找和访问各种命名和目录服务的通用 统一的接口 使用JNDI的步骤
  • React hooks认知及使用

    Hook介绍 Hook 是 React 16 8 的新增特性 它是完全可选的 并且100 向后兼容 它可以让你使用函数组件的方式 运用类组件以及 react 其他的一些特性 比如管理状态 生命周期钩子等 从概念上讲 React 组件一直更像
  • Jenkins插件开发之突破防火墙

    背景 因为防火墙限制 Jenkins服务端配置完成后 从远程url访问往往受阻 然鹅 并不是任何环境都允许你直接粗暴的将防火墙关闭 解决思路之一 可以单独设置一条规则 开放端口 步骤演示 环境 windows server 2022 1 打
  • Nginx的静态代理

    Nginx的静态代理 Nginx的web请求的处理机制 Nginx结合多进程和异步机制对外提供服务 异步机制使用的是异步非阻塞机制 即AIO Nginx的master进程会生成多个worker进程 master进程负责管理这些worker进
  • 操作系统实验4 使用命名管道实现进程通信

    文章目录 实验4 使用命名管道实现进程通信 一 实验目的 二 实验准备 三 实验内容 一 实验内容 二 主要代码 部分代码 PipeServer cpp PipeClienr cpp 四 实验结果与总结 遇到的问题及解决办法 必看 实验4
  • Open3D 进阶(4)高斯混合点云聚类

    目录 一 算法原理 1 原理概述 2 实现流程 3 参考文献 二 代码实现 三 结果展示 四 测试数据 本文由CSDN点云侠原创 原文链接 爬虫网站自重 一 算法原理 1 原理概述 高斯混合聚类 GMM 算法假设数据点是由一个或多个高斯分布
  • python用户名和密码登录_python编写登录接口

    python编写登录接口 一 需求 编写登录接口 1 输入用户名和密码登录 2 输错三次锁定账户 3 下次登录还是上次的账户 提示锁定 直接退出 用到文件读写 4 成功 后显示登录成功 二 需求流程图 三 代码示例 例1 bin bash
  • 解决Windows jmeter Non HTTP response message: Address already in use: connect 错误

    jMeter报错 Response code Non HTTP response code java net BindException Response message Non HTTP response message Address
  • Xgboost算法——Kaggle案例

    作者简介Introduction 苏高生 西南财经大学统计学硕士毕业 现就职于中国电信 主要负责企业存量客户大数据分析 数据建模 研究方向 机器学习 最喜欢的编程语言 R语言 没有之一 E mail sugs01 outlook com 零
  • 图像上的算术运算(opencv-python学习(2))

    图像上的算术运算 图像加法 图像融合 按位运算 图像加法 通过OpenCV函数 cv add 通过numpy操作 res img1 img2 OpenCV加法是饱和运算 而Numpy加法是模运算 x np uint8 250 y np ui
  • [Qt] tcp服务器连接多个客户端的实现

    Qt tcp服务器连接多个客户端的实现 要求 数据按字节接收 以1 255个字节循环发送 编译环境 Qt 5 9 5 客户端的实现 代码如下 TcpClient h ifndef TCPCLIENT H define TCPCLIENT H