QTday05(TCP的服务端客户端通信)

news2024/10/5 16:31:41

实现聊天室功能

服务端代码:

pro文件需要导入  network

头文件:

#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_startBtn_clicked();
public slots:
    void newConnectSlot();//建立连接的槽函数
    void readyReadSlot();//接收消息的槽函数

private:
    Ui::Widget *ui;

    //实例化服务器对象
    QTcpServer *server;
    //创建存放客户端信息的容器
    QList<QTcpSocket *> socketList;
};
#endif // WIDGET_H

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_startBtn_clicked()
{
    //监听任意ip的指定端口
    bool listen_res=server->listen(QHostAddress::Any,ui->portLine->text().toUInt());
    if(listen_res){
        //监听成功
        QMessageBox::information(this,"提示","设置监听成功",QMessageBox::Ok);

    }else{
        //监听失败
        QMessageBox::information(this,"提示","设置监听失败",QMessageBox::Ok);
        return;
    }
    //等待连接
    connect(server,&QTcpServer::newConnection,this,&Widget::newConnectSlot);
}

void Widget::newConnectSlot()
{
    //接收到newConnect信号之后的槽函数,处理接下来的操作

    //获取客户端的套接字,加入容器
    QTcpSocket *s=server->nextPendingConnection();
    socketList.push_back(s);
    qDebug() << "有新客户连接" << s->peerName() << ";" << s->peerAddress().toString() << ":" << QString::number(s->peerPort()) <<endl;
    //此时如果客户端向服务器发送数据,客户端会发送一个readyRead信号
    connect(s,&QTcpSocket::readyRead,this,&Widget::readyReadSlot);
}

void Widget::readyReadSlot()
{
    //客户端有数据发送,触发改槽函数
    //遍历容器,移除无效客户端,接收有效客户端消息
    for (int i=0;i<socketList.count();i++) {
        //如果是非链接状态就移除
        if(socketList.at(i)->state()==QAbstractSocket::UnconnectedState){
            socketList.removeAt(i);
        }
    }
    for (int i=0;i<socketList.count();i++) {
        //如果有字节,就读取并放到ui界面
        if(socketList.at(i)->bytesAvailable()){
            QByteArray msg=socketList.at(i)->readAll();
            QString msgInfo=socketList.at(i)->peerAddress().toString()+":"+QString::number(socketList.at(i)->peerPort())+":"+QString::fromLocal8Bit(msg);
            ui->listWidget->addItem(msgInfo);
            for (int j=0;j<socketList.count();j++) {
                socketList.at(j)->write(msg);
            }
        }
    }
}

ui:

客户端代码:

头文件

#ifndef WIDGET_H
#define WIDGET_H

#include <QWidget>
#include <QtDebug>
#include <QMessageBox>
#include <QTcpSocket>

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

    void on_disconnectBtn_clicked();

    void on_sendBtn_clicked();

public slots:
    void connnectedSlot();
    void readyReadSlot();
    void disconnectedSlot();

private:
    Ui::Widget *ui;

    //实例化客户端
    QTcpSocket *socket;
    //定义全局变量存储用户名
    QString username;
};
#endif // WIDGET_H

widget.cpp

#include "widget.h"
#include "ui_widget.h"

Widget::Widget(QWidget *parent)
    : QWidget(parent)
    , ui(new Ui::Widget)
{
    ui->setupUi(this);
    //实例化客户端
    socket=new QTcpSocket(this);
    //将发送和断开连接 的按钮默认设置不可用
    ui->sendBtn->setDisabled(true);
    ui->disconnectBtn->setDisabled(true);

    ui->accoutLine->setText("张三");
    ui->ipLine->setText("192.168.125.77");
    ui->portLine->setText("8888");
    //连接成功会触发connected信号,只需要一次
    connect(socket,&QTcpSocket::connected,this,&Widget::connnectedSlot);

    //收信号
    connect(socket,&QTcpSocket::readyRead,this,&Widget::readyReadSlot);

    connect(socket,&QTcpSocket::disconnected,this,&Widget::disconnectedSlot);
}

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


void Widget::on_connnectBtn_clicked()
{
    //连接服务器
    username=ui->accoutLine->text();
    QString ip=ui->ipLine->text();
    quint16 port=ui->portLine->text().toUInt();
    socket->connectToHost(ip,port);
    //判断是否连接成功:连接成功后,户端会自动发射一个connected信号,

}

void Widget::connnectedSlot()
{
    QMessageBox::information(this,"提示","连接成功");

    QString msg=username+"进入聊天室";
    socket->write(msg.toLocal8Bit());

    ui->sendBtn->setDisabled(false);
    ui->connnectBtn->setDisabled(true);
    ui->disconnectBtn->setDisabled(false);

}

void Widget::on_disconnectBtn_clicked()
{

    QString msg=username+"离开聊天室";
    socket->write(msg.toLocal8Bit());
    socket->disconnectFromHost();

}

void Widget::on_sendBtn_clicked()
{
    if(ui->infoLine->text().isEmpty()){
        QMessageBox::information(this,"提示","发送的消息不能为空");
        return;
    }
    QString msg=ui->infoLine->text();
    socket->write(msg.toLocal8Bit());
    ui->infoLine->setText("");
}
void Widget::readyReadSlot(){
    QByteArray msg=socket->readAll();

    ui->listWidget->addItem(QString::fromLocal8Bit(msg));
}

void Widget::disconnectedSlot()
{
    ui->sendBtn->setDisabled(true);
    ui->disconnectBtn->setDisabled(true);
    ui->connnectBtn->setDisabled(false);
    QMessageBox::information(this,"提示","断开连接成功");
}

ui:

运行结果:客户端连接之后可以成功发送信息

今日思维导图:

将聊天功能加入到仿qq登录之后:

代码:

page2.h:

#ifndef PAGE2_H
#define PAGE2_H

#include <QWidget>
#include <QMovie>
#include <QTcpSocket>
#include <QMessageBox>
#include <QDebug>
#define PORT 8888
#define IP "192.168.125.77"

namespace Ui {
class Page2;
}

class Page2 : public QWidget
{
    Q_OBJECT

public:
    explicit Page2(QWidget *parent = nullptr);
    ~Page2();
public slots:
    void login_slot();
    void connectedSlot();
    void readyReadSlot();
private slots:
    void on_sendBtn_clicked();

private:
    Ui::Page2 *ui;

    QTcpSocket *socket;

};

#endif // PAGE2_H

widget.h:

#ifndef WIDGET_H
#define WIDGET_H

#include <QWidget>
#include <QMovie>
#include <QMessageBox>
#include <QDebug>
#include <QMouseEvent>


QT_BEGIN_NAMESPACE
namespace Ui { class Widget; }
QT_END_NAMESPACE

class Widget : public QWidget
{
    Q_OBJECT

public:
    Widget(QWidget *parent = nullptr);
    ~Widget();

    void mousePressEvent(QMouseEvent *event) override;
    void mouseMoveEvent(QMouseEvent *event) override;

public slots:
    void loginButton_slot();


signals:
    void login_signal();

private:
    Ui::Widget *ui;

    QPoint p;//定义全局变量p,记录位置
public:
    static QString username;
};

#endif // WIDGET_H

main.cpp:

#include "widget.h"
#include "page2.h"

#include <QApplication>

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

    Page2 p2;
    QObject::connect(&w,&Widget::login_signal,&p2,&Page2::login_slot);
    return a.exec();
}

page2.cpp:

#include "page2.h"
#include "widget.h"
#include "ui_page2.h"

Page2::Page2(QWidget *parent) :
    QWidget(parent),
    ui(new Ui::Page2)
{
    ui->setupUi(this);
    QMovie *movie = new QMovie(":/111/cai.gif");
    ui->label->setMovie(movie);
    ui->label->setScaledContents(true);
    movie->start();
    //实例化客户端
    socket=new QTcpSocket(this);
    qDebug() << "实例化客户端";
    //建立connected信号和指定槽函数的连接
    connect(socket,&QTcpSocket::connected,this,&Page2::connectedSlot);
    //建立readyRead信号和指定槽函数连接
    connect(socket,&QTcpSocket::readyRead,this,&Page2::readyReadSlot);

}

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

void Page2::login_slot()
{
    qDebug() << "登录按钮";
    this->show();
    //连接客户端
    socket->connectToHost(IP,PORT);
}

void Page2::connectedSlot()
{
    //连接成功后触发该槽函数
    qDebug() << "连接成功";
    QMessageBox::information(this,"提示","连接成功");

    QString msg=Widget::username+"加入了聊天";
    socket->write(msg.toLocal8Bit());
}

void Page2::readyReadSlot()
{
    //收到服务端发送的消息时触发该槽函数
    QByteArray msg=socket->readAll();
    ui->listWidget->addItem(QString::fromLocal8Bit(msg));
}

void Page2::on_sendBtn_clicked()
{
    if(ui->infoEdit->toPlainText().isEmpty()){
        QMessageBox::information(this,"提示","发送的消息不能为空");
        return;
    }
    QString msg=ui->infoEdit->toPlainText();
    socket->write(msg.toLocal8Bit());
    ui->infoEdit->clear();
}

widget.cpp:

#include "widget.h"
#include "ui_widget.h"
QString Widget::username="";
Widget::Widget(QWidget *parent)
    : QWidget(parent)
    , ui(new Ui::Widget)
{
    ui->setupUi(this);
    this->setFixedSize(560,430);
    this->setStyleSheet("background-color:#faf7ec");
    this->setWindowFlag(Qt::FramelessWindowHint);//无边框
    QMovie *movie = new QMovie(":/111/cai.gif");
    ui->backLabel->setMovie(movie);
    ui->backLabel->setScaledContents(true);
    movie->start();

    ui->closeButton->setStyleSheet("border-image:url(:/111/basketball.png)");


    ui->avatorLabel->resize(60,60);
    ui->avatorLabel->setStyleSheet("border-image:url(:/111/user.png);border-radius:30px");

    ui->accountLabel->setPixmap(QPixmap(":/111/account.jpg"));
    //ui->accountLabel->resize(40,40);
    ui->accountLabel->setScaledContents(true);

    ui->passwdLabel->setPixmap(QPixmap(":/111/passwd.jpg"));
    //ui->passwdLabel->resize(40,40);
    ui->passwdLabel->setScaledContents(true);

    ui->accoountLine->setPlaceholderText("账号");
    ui->passwdLine->setPlaceholderText("密码");
    ui->passwdLine->setEchoMode(QLineEdit::Password);

    ui->loginLabel->setPixmap(QPixmap(":/111/2.png"));
    ui->loginLabel->setScaledContents(true);

    ui->loginButton->setStyleSheet("background-color:#409EFF;border-radius:5px");

    connect(ui->closeButton,SIGNAL(clicked()),this,SLOT(close()));

    connect(ui->loginButton,&QPushButton::clicked,this,&Widget::loginButton_slot);

}

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


void Widget::loginButton_slot()
{
    //判断用户账号密码的正确性
    if(ui->accoountLine->text()=="admin"&&ui->passwdLine->text()=="123456"){
        username="admin";
        qDebug() << "登录成功" <<endl;
        QMessageBox::information(this,"提示","登录成功",QMessageBox::Ok);
        this->close();
        //开启新窗口,手动触发信号
        emit login_signal();
    }else{
        qDebug() << "账号或者密码错误" <<endl;
        int res=QMessageBox::information(this,"提示","账号或者密码错误,是否继续登录",QMessageBox::Ok|QMessageBox::No);
        if(res==QMessageBox::Ok){
            ui->passwdLine->setText("");
        }else{
            this->close();
        }

    }
}
void Widget::mousePressEvent(QMouseEvent *event){
    p=event->pos();
}
void Widget::mouseMoveEvent(QMouseEvent *event){
    if(event->buttons()==Qt::LeftButton)
    this->move(event->globalPos()-p);
}

page2.ui:

widget.ui:

运行结果:

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1118760.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【代码随想录】算法训练营 第十一天 第五章 栈与队列 Part 2

20. 有效的括号 题目 给定一个只包括 (&#xff0c;)&#xff0c;{&#xff0c;}&#xff0c;[&#xff0c;] 的字符串 s &#xff0c;判断字符串是否有效。 有效字符串需满足&#xff1a; 左括号必须用相同类型的右括号闭合。左括号必须以正确的顺序闭合。每个右括号都有一…

FL Studio2024破解版水果注册机

我们都知道&#xff0c;FL Studio是一款非常好用的音乐编曲软件。随着商业音乐的兴起&#xff0c;编曲这一词汇逐渐被大家所熟知&#xff0c;而有些小伙伴可能也听说过编曲是由四大件进行编写的&#xff0c;所以今天就和大家分享一下&#xff0c;编曲中的四大件是什么&#xff…

Springboot整合Redis的Cluster集群进行API限流

Redis天生就是限流的好帮手&#xff01;因为它有key&#xff0c;有value&#xff0c;有expire过期时间。我们把ip和url作为表示某个接口的key&#xff0c;而value就是这个ip下访问该url接口的次数&#xff0c;过期时间就是设定的限时时间。 我们把限时时间设定为3s&#xff0c…

一、2023 CISSP认证介绍

目录 1.CISSP概况 2.CISSP考题分析 3.备考建议 1.CISSP概况 参考:

【C++代码】二叉搜索树的最近公共祖先,二叉搜索树中的插入操作,删除二叉搜索树中的节点--代码随想录

题目&#xff1a;二叉搜索树的最近公共祖先 给定一个二叉搜索树, 找到该树中两个指定节点的最近公共祖先。最近公共祖先的定义为&#xff1a;“对于有根树 T 的两个结点 p、q&#xff0c;最近公共祖先表示为一个结点 x&#xff0c;满足 x 是 p、q 的祖先且 x 的深度尽可能大&a…

Catalan 数 和 Stirling 数

这个也可以理解为栈&#xff0c;用 ( 表示 入栈 &#xff0c; ) 表示出栈 , 任何情况下表示入栈的 ( 都必须大于等于 ) 的个数 这个思路和入栈出栈的思路是等价的

2.1.C++项目:网络版五子棋对战之前置知识

文章目录 一、Websocketpp&#xff08;一&#xff09;Websocket介绍&#xff08;二&#xff09;报文格式&#xff08;三&#xff09;Websocketpp介绍&#xff08;四&#xff09;Websocketpp使用1.websocketpp常用接口介绍2. http/websocket服务器 &#xff08;五&#xff09;Js…

Vue基础与常用指令,Element基础

1.vue快速入门 vue概述 Vue是一套构建用户界面的渐进式前端框架 只关注视图层&#xff0c;并且非常容易学习&#xff0c;还可以很方便的与其它库或已有项目整合 通过尽可能简单的API来实现响应数据的绑定和组合的视图组件 特点 易用&#xff1a;在有HTMLCSSJavaScript的基…

http post协议发送本地压缩数据到服务器

1.客户端程序 import requests import os # 指定服务器的URL url "http://192.168.1.9:8000/upload"# 压缩包文件路径 folder_name "upload" file_name "test.7z" headers {Folder-Name: folder_name,File-Name: file_name } # 发送POST请求…

ExelaStealer:新型低成本网络犯罪武器崭露头角

网络犯罪的工具层出不穷&#xff0c;而最新的一款信息窃取器ExelaStealer也开始崭露头角。这种低成本的恶意软件专门针对受损的Windows系统&#xff0c;用于窃取敏感数据。据Fortinet FortiGuard Labs研究员James Slaughter在一份技术报告中表示&#xff1a;“ExelaStealer是一…

Arcgis 数据操作

在进行数据操作的时候&#xff0c;需要注意坐标系要一致&#xff0c;这是前提。 数据类型 文件地理数据库&#xff1a;gbd 个人地理数据库&#xff1a;mdb &#xff08;Mircosoft Access&#xff09; 矢量数据&#xff1a;shp 推荐使用gbd数据&#xff0c;效率会更高。 采…

C/C++程序设计和预处理

个人主页&#xff1a;仍有未知等待探索_C语言疑难,数据结构,小项目-CSDN博客 专题分栏&#xff1a;C语言疑难_仍有未知等待探索的博客-CSDN博客 目录 一、引言 二、程序的翻译环境和执行环境 1、什么是程序 2、程序的翻译环境 3、程序的执行环境 三、预处理 1、预定义符…

排序算法,冒泡排序算法及优化,选择排序SelectionSort,快速排序(递归-分区)

一、冒泡排序算法&#xff1a; 介绍&#xff1a; 冒泡排序&#xff08;Bubble Sort&#xff09;是一种简单直观的排序算法。它重复地走访过要排序的数列&#xff0c;一次比较两个元素&#xff0c;如果他们的顺序错误就把他们交换过来。走访数列的工作是重复地进行直到没有再需…

Nginx虚拟主机的搭建 基于ip 基于端口 基于域名

一、虚拟主机介绍 虚拟主机是一种特殊的软硬件技术&#xff0c;他可以将网络上的每一台计算机分成多个虚拟主机&#xff0c;每个虚拟主机可以单独对外提供web服务&#xff0c;这样就可以实现一台主机对多个web服务&#xff0c;每个虚拟主机都是独立的&#xff0c;互相不影响 ng…

卫星业务。。。。

卫星业务 • 卫星运营围绕三个主要部分展开 &#xff1a; – 运营卫星业务的 地面段 – 由所有空间资产组成的 空间段 – 接收卫星服务&#xff08;如 GPS 或通信&#xff09;的 用户段 • 1. 地面段 – 在卫星的整个生命周期中&#xff0c;地面段是所有卫星运行的中心。 • 一…

【Javascript】对象中的常规操作(增删改查)

目录 创建对象&#xff0c;并在控制台里打印该对 增 改 删 查 创建对象&#xff0c;并在控制台里打印该对象 增 给person这个对象增加一个性别的属性 对象 . 属性值 改 例如将年龄改为20 删 例如删除性别 查 除了console.log(person.?)查询

Leetcode1838. 最高频元素的频数

Every day a Leetcode 题目来源&#xff1a;1838. 最高频元素的频数 解法1&#xff1a;排序 滑动窗口 发现1&#xff1a;操作后的最高频元素必定可以是数组中已有的某一个元素。 发现2&#xff1a;优先操作距离目标值最近的&#xff08;小于目标值的&#xff09;元素。 …

SpringBoot项目创建失败或无法启动,启动报错时的常见问题及解决方案

1、无法启动&#xff0c;没有启动的三角按钮 原因&#xff1a;idea没有将其识别为一个maven项目 解决方案&#xff1a;告诉idea&#xff0c;这是一个maven项目 1.1、如果右侧有Maven项目&#xff0c;刷新一下 1.2、左侧项目鼠标右键&#xff0c;添加Maven框架支持 若没有选择m…

Vue2基础知识(三) 组件化

目录 一 组件1.1 组件的定义1.2 特点1.3 Vue-extend1.4 VueCompent 二 脚手架2.1 安装2.2 结构目录2.3 Render函数2.4 修改默认配置2.5 Ref 属性2.6 Prop 属性2.7 Mixin 属性2.8 插件2.9 Scoped 三 组件3.1 组件的注册3.1.1 局部注册3.1.2 全局注册 3.2 组件的通信3.2.1 父子关…

差分时钟与DDR3

Zynq上的存储器接口 所有 Zynq-7000 AP芯片上的存储器接口单元包括一个动态存储器控制器和几个 静态存储器接口模块。动态存储器控制器可以用于 DDR3、DDR3L、DDR2 和 LPDDR2。 静态存储器控制器支持一个 NAND 闪存接口、一个 Quad-SPI 闪存接口、一个并行数 据总线和并行 NOR …