基于qt软件的网上聊天室软件

news2024/11/26 2:43:26

1.服务器:

        1).功能:

用于创建一个客户端,通过文本编辑器来获得端口号,根据获得的端口号创建服务器,等待客户端连接

创建成功会提示服务器创建成功

在收到客户端发送的信息时,把这条信息发送给其他所有客户端,实现群聊功能

        2).代码:

-------------------------------------------------------------------
.pro
-------------------------------------------------------------------
QT       += core gui network

greaterThan(QT_MAJOR_VERSION, 4): QT += widgets

CONFIG += c++11

# The following define makes your compiler emit warnings if you use
# any Qt feature that has been marked deprecated (the exact warnings
# depend on your compiler). Please consult the documentation of the
# deprecated API in order to know how to port your code away from it.
DEFINES += QT_DEPRECATED_WARNINGS

# You can also make your code fail to compile if it uses deprecated APIs.
# In order to do so, uncomment the following line.
# You can also select to disable deprecated APIs only up to a certain version of Qt.
#DEFINES += QT_DISABLE_DEPRECATED_BEFORE=0x060000    # disables all the APIs deprecated before Qt 6.0.0

SOURCES += \
    main.cpp \
    widget.cpp

HEADERS += \
    widget.h

FORMS += \
    widget.ui

# Default rules for deployment.
qnx: target.path = /tmp/$${TARGET}/bin
else: unix:!android: target.path = /opt/$${TARGET}/bin
!isEmpty(target.path): INSTALLS += target
-------------------------------------------------------------------
widget.h
-------------------------------------------------------------------
#ifndef WIDGET_H
#define WIDGET_H

#include <QWidget>
#include <QMessageBox>
#include <QTcpServer>
#include <QTcpSocket>
#include <QPushButton>
#include <QLabel>
#include <QListWidget>
#include <QLineEdit>
#include <QList>

QT_BEGIN_NAMESPACE
namespace Ui { class Widget; }
QT_END_NAMESPACE

class Widget : public QWidget
{
    Q_OBJECT

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

public slots:
    void connectbtn_slot();
    void connected_slot();
    void readyread_slot();

private:
    Ui::Widget *ui;

    //定义服务器
    QTcpServer *server;

    //定义一个链表存放连接的客户端数据
    QList<QTcpSocket *> client;

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


    //连接按钮与对应槽函数
    connect(ui->connectbtn,&QPushButton::clicked,this,&Widget::connectbtn_slot);

}

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

//按下按钮后启动服务器
void Widget::connectbtn_slot()
{
    //读取端口号
    quint16 port = ui->portedit->text().toInt();
    //设置为监听状态
    if(!server->listen(QHostAddress::Any,port))
    {
        QMessageBox::critical(this,"失败","服务器启动失败");
        return ;
    }
    QMessageBox::information(this,"成功","服务器启动成功");

    //连接客户端信号与槽函数
    connect(server,&QTcpServer::newConnection,this,&Widget::connected_slot);
}

//客户端连接槽函数
void Widget::connected_slot()
{
    //获取客户端套接字
    QTcpSocket *cli = server->nextPendingConnection();

    //将得到的套接字写入链表
    client.push_back(cli);
    //把新套接字与接收信号连接
    connect(cli,&QTcpSocket::readyRead,this,&Widget::readyread_slot);

}

//有信息写入准备读取
void Widget::readyread_slot()
{
    //删除已经断开的客户端
    for(int i = 0;i < client.size();i++)
    {
        if(client.at(i)->state() == 0)
        {
            client.removeAt(i);
        }
    }

    //遍历发送数据的客户端
    for(int i = 0;i < client.size();i++)
    {
        if(client.at(i)->bytesAvailable() != 0)
        {
            //读取客户端数据
            QByteArray msg = client.at(i)->readAll();
            //将数据写到ui界面中
            ui->listWidget->addItem(QString::fromLocal8Bit(msg));
            //将数据发送给所有客户端
            for(int j = 0;j < client.size();j++)
            {
                client.at(j)->write(msg);
            }
        }
    }

}

2.客户端:

登陆界面 :

        1).功能:

输入账号密码,如果点击注册按钮,会将输入的账号密码存入数据库中

且对输入的账号密码进行判断(没输入账号会提示请输入账号,没输入密码会提示请输入密码),输入的账号不能重复,但是密码可以重复;

如果点击登录按钮,会读取数据库中注册的账号密码,与输入的账号密码进行对比,

如果一致则提示登录成功并跳转到聊天室界面,如果在数据库中找不到账号则提示账号不存在,如果存在账号但是密码不正确,则输出密码错误

        2).代码:

-------------------------------------------------------------------
.pro
-------------------------------------------------------------------
QT       += core gui network sql

greaterThan(QT_MAJOR_VERSION, 4): QT += widgets

CONFIG += c++11

# The following define makes your compiler emit warnings if you use
# any Qt feature that has been marked deprecated (the exact warnings
# depend on your compiler). Please consult the documentation of the
# deprecated API in order to know how to port your code away from it.
DEFINES += QT_DEPRECATED_WARNINGS

# You can also make your code fail to compile if it uses deprecated APIs.
# In order to do so, uncomment the following line.
# You can also select to disable deprecated APIs only up to a certain version of Qt.
#DEFINES += QT_DISABLE_DEPRECATED_BEFORE=0x060000    # disables all the APIs deprecated before Qt 6.0.0

SOURCES += \
    chat.cpp \
    main.cpp \
    widget.cpp

HEADERS += \
    chat.h \
    widget.h

# Default rules for deployment.
qnx: target.path = /tmp/$${TARGET}/bin
else: unix:!android: target.path = /opt/$${TARGET}/bin
!isEmpty(target.path): INSTALLS += target

RESOURCES += \
    Icon.qrc

FORMS += \
    chat.ui
-------------------------------------------------------------------
widget.h
-------------------------------------------------------------------
#ifndef WIDGET_H
#define WIDGET_H

#include <QWidget>

#include <QIcon>
#include <QLabel>
#include <QPushButton>
#include <QLineEdit>

#include <QMessageBox>
#include <QDebug>

#include <QSqlDatabase>
#include <QSqlQuery>



class Widget : public QWidget
{
    Q_OBJECT

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

signals:
    void loginsuccess_signal(QString account);

public slots:
    void loginbtn_slot();
    void signinbtn_slot();

private:
    //组件
    QPushButton *login_btn;
    QPushButton *signin_btn;
    QLineEdit *account_edit;
    QLineEdit *pwd_edit;
    QLabel *main_lab;
    QLabel *account_lab;
    QLabel *pwd_lab;

    //数据库初始化
    QSqlDatabase db;

};
#endif // WIDGET_H
-------------------------------------------------------------------
main.cpp
-------------------------------------------------------------------
#include "widget.h"
#include "chat.h"

#include <QApplication>

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

    QObject::connect(&w,&Widget::loginsuccess_signal,&c,&chat::openchatroom_slot);

    return a.exec();
}
-------------------------------------------------------------------
widget.cpp
-------------------------------------------------------------------
#include "widget.h"

Widget::Widget(QWidget *parent)
    : QWidget(parent)
{
    //---------------------------------窗口设置---------------------------------
    //设置窗口为固定大小
    this->setFixedSize(540,410);
    //设置窗口icon
    this->setWindowIcon(QIcon(":/icon/wodepeizhenshi.png"));
    //设置窗口名
    this->setWindowTitle("鹏哥快聊");

    //---------------------------------标签设置---------------------------------
    //=====创建界面标签=====
    main_lab = new QLabel(this);
    //修改标签大小
    main_lab->resize(540,200);
    //在标签中插入图片
    main_lab->setPixmap(QPixmap(":/icon/logo.png"));
    //设置为自适应
    main_lab->setScaledContents(1);

    //=====创建账号标签=====
    account_lab = new QLabel(this);
    //修改标签大小
    account_lab->resize(50,40);
    //在标签中插入图片
    account_lab->setPixmap(QPixmap(":/icon/userName.jpg"));
    //移动到合适位置
    account_lab->move(90,220);
    //设置为自适应
    account_lab->setScaledContents(1);

    //=====创建密码标签=====
    pwd_lab = new QLabel(this);
    //修改标签大小
    pwd_lab->resize(account_lab->size());
    //在标签中插入图片
    pwd_lab->setPixmap(QPixmap(":/icon/passwd.jpg"));
    //移动到合适位置
    pwd_lab->move(account_lab->x(),account_lab->y()+80);
    //设置为自适应
    pwd_lab->setScaledContents(1);

    //---------------------------------按钮设置---------------------------------
    //=====创建登录按钮=====
    login_btn = new QPushButton(this);
    //设置按钮大小
    login_btn->resize(100,40);
    //在按钮上插入图片
    login_btn->setIcon(QIcon(":/icon/login.png"));
    //在按钮上输入文本
    login_btn->setText("登录");
    //移动到合适位置
    login_btn->move(270,360);
    connect(login_btn,&QPushButton::clicked,this,&Widget::loginbtn_slot);

    //=====创建注册按钮=====
    signin_btn = new QPushButton(this);
    //设置按钮大小
    signin_btn->resize(100,40);
    //在按钮上插入图片
    signin_btn->setIcon(QIcon(":/icon/cancel.png"));
    //在按钮上输入文本
    signin_btn->setText("注册");
    //移动到合适位置
    signin_btn->move(login_btn->x()+120,login_btn->y());
    connect(signin_btn,&QPushButton::clicked,this,&Widget::signinbtn_slot);

    //---------------------------------输入设置---------------------------------
    //=====创建账号输入=====
    account_edit = new QLineEdit(this);
    //设置占位文本
    account_edit->setPlaceholderText("QQ号码/手机/邮箱");
    //设置大小
    account_edit->resize(200,40);
    //移动到适合位置
    account_edit->move(account_lab->x()+100,account_lab->y());

    //=====创建密码输入=====
    pwd_edit = new QLineEdit(this);
    //设置占位文本
    pwd_edit->setPlaceholderText("密码");
    //设置大小
    pwd_edit->resize(account_edit->size());
    //移动到合适位置
    pwd_edit->move(pwd_lab->x()+100,pwd_lab->y());
    //设置回文显示模式
    pwd_edit->setEchoMode(QLineEdit::Password);

    //--------------------------------数据库设置--------------------------------
    //判断数据库是否存在
    if(!db.contains("user.db"))
    {
        //不存在
        //创建一个数据库
        db = QSqlDatabase::addDatabase("QSQLITE");
        //命名为user.db
        db.setDatabaseName("user.db");
    }
    //存在
    //打开数据库
    if(!db.open())
    {
        //打开失败
        QMessageBox::critical(this,"error","数据库打开失败");
        return;
    }
    //打开成功
    //创建一个表
    //准备sql语句
    QString sql = "create table if not exists usertab("
                  "account char primary key,"
                  "passwd char)";

    //创建一个数据库指令执行者
    QSqlQuery commend;
    //执行sql语句
    if(!commend.exec(sql))
    {
        //创建表失败
        QMessageBox::critical(this,"error","表格创建失败");
        return;
    }


}

Widget::~Widget()
{
}

//登录按钮槽函数
void Widget::loginbtn_slot()
{
    //获取账号和密码
    QString account =  account_edit->text();
    QString passwd = pwd_edit->text();
    //从数据库中读取账号和密码
    QString sql = "select * from usertab";
    //创建执行者并执行sql语句
    QSqlQuery commend;
    if(!commend.exec(sql))
    {
        //查找失败
        QMessageBox::critical(this,"error","获取注册表失败");
        return;
    }
    //查找成功,循环比较数据库信息与输入的信息
    while (commend.next())
    {
        if(commend.value(0).toString() == account)
        {
            //账号正确
            if(commend.value(1).toString() == passwd)
            {
                QMessageBox::information(this,"success","登录成功");
                emit loginsuccess_signal(account);
                this->close();
                return;
            }
            //密码错误
            QMessageBox::critical(this,"failed","登录失败,密码错误");
            pwd_edit->clear();
            return;
        }
    }
    //账号错误
    QMessageBox::critical(this,"failed","登录失败,账号不存在");
    pwd_edit->clear();
}

//注册按钮槽函数
void Widget::signinbtn_slot()
{
    //判断是否填写完整信息
    if(account_edit->text() == "")
    {
        QMessageBox::critical(this,"error","请输入账号");
        return;
    }
    if(pwd_edit->text() == "")
    {
        QMessageBox::critical(this,"error","请输入密码");
        return;
    }
    //获取界面上的账号和密码
    QString account = account_edit->text();
    QString passwd = pwd_edit->text();
    //将注册的账号密码写入数据库中
    //准备sql语句
    QString sql = QString("insert into usertab values(\"%1\",\"%2\")")
            .arg(account).arg(passwd);
    //创建执行者并执行sql语句
    QSqlQuery commend;
    if(!commend.exec(sql))
    {
        //注册失败,账号重复
        QMessageBox::critical(this,"error","重复注册");
        return;
    }
    //注册成功
    QMessageBox::information(this,"success","注册成功");
}

聊天室界面:

        1).功能:

进入时,显示登录时的账号,且发送按钮、断开按钮和发送的文本编辑器不可用

通过主机的IP和端口号进行连接至主机后,给服务器发送xxx : 进入聊天室,发送按钮、断开按钮和发送的文本编辑器重新可用,使IP和端口号的文本编辑器以及连接聊天室按钮不可用

在发送的文本编辑器中写入信息并按下发送按钮后,会整合发送信息然后发送信息给服务器,发送后清空文本内容,

点击断开聊天室按钮,重新使发送按钮、断开按钮和发送的文本编辑器不可用,使IP和端口号的文本编辑器以及连接聊天室按钮可用

并会给服务器发送一条xxx : 离开聊天室的信息

        2).代码:

-------------------------------------------------------------------
chat.h
-------------------------------------------------------------------
#ifndef CHAT_H
#define CHAT_H

#include <QWidget>
#include <QTcpSocket>
#include <QPushButton>
#include <QLabel>
#include <QLineEdit>
#include <QListWidget>
#include <QMessageBox>

namespace Ui {
class chat;
}

class chat : public QWidget
{
    Q_OBJECT

public:
    explicit chat(QWidget *parent = nullptr);
    ~chat();

public slots:
    void openchatroom_slot(QString account);
    void connectbtn_slot();
    void connected_slot();
    void readyread_slot();
    void sndbtn_slot();
    void disconbtn_slot();
    void disconnect_slot();

private:
    Ui::chat *ui;

    //定义一个客户端套接字
    QTcpSocket *client;

    //定义一个用户名
    QString username;

};

#endif // CHAT_H
-------------------------------------------------------------------
chat.cpp
-------------------------------------------------------------------
#include "chat.h"
#include "ui_chat.h"

chat::chat(QWidget *parent) :
    QWidget(parent),
    ui(new Ui::chat)
{
    //将用户名居中显示
    //ui->usernamelab_2->setAlignment(Qt::AlignCenter);

    ui->setupUi(this);
    //为客户端套接字实例化空间
    client = new QTcpSocket(this);
    //连接按钮与对应槽函数连接
    connect(ui->connectbtn,&QPushButton::clicked,this,&chat::connectbtn_slot);
    //已连接信号与对应槽函数连接
    connect(client,&QTcpSocket::connected,this,&chat::connected_slot);
    //服务器发送信息信号与对应槽函数连接
    connect(client,&QTcpSocket::readyRead,this,&chat::readyread_slot);
    //发送按钮与对应槽函数连接
    connect(ui->sndbtn,&QPushButton::clicked,this,&chat::sndbtn_slot);
    //断开按钮与对应槽函数
    connect(ui->disconbtn,&QPushButton::clicked,this,&chat::disconbtn_slot);
    //断开连接信号与对应槽函数
    connect(client,&QTcpSocket::disconnected,this,&chat::disconnect_slot);
    //设置按钮与编辑框可用
    ui->disconbtn->setEnabled(0);
    ui->sndbtn->setEnabled(0);
    ui->msgedit->setEnabled(0);
}

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

void chat::openchatroom_slot(QString account)
{
    username = account;
    qDebug() << account;
    qDebug() << username;
    ui->usernamelab_2->setText(username);
    this->show();
}

//连接按钮槽函数
void chat::connectbtn_slot()
{
    //从ui界面读取IP与port
    QString IP = ui->IPedit->text();
    quint16 port = ui->portedit->text().toUInt();
    //连接服务器
    client->connectToHost(IP,port);
}

//已连接槽函数
void chat::connected_slot()
{
    //连接完成提示信息
    QMessageBox::information(this,"成功","成功进入聊天室");
    //发送xx进入聊天室提示给服务器
    QString sndmsg = username + " : 进入聊天室";
    client->write(sndmsg.toLocal8Bit());

    //设置按钮与编辑框可用
    ui->IPedit->setEnabled(0);
    ui->portedit->setEnabled(0);
    ui->connectbtn->setEnabled(0);
    ui->sndbtn->setEnabled(1);
    ui->msgedit->setEnabled(1);
    ui->disconbtn->setEnabled(1);

}

//读取服务器发送信息
void chat::readyread_slot()
{
    //读取信息
    QByteArray msg = client->readAll();
    //将读取的信息发送到信息框内
    ui->listWidget->addItem(QString::fromLocal8Bit(msg));
}

//发送按钮对应槽函数
void chat::sndbtn_slot()
{
    //读取ui界面发送框内信息
    QString msg = ui->msgedit->text();
    msg = username + " : " + msg;
    //将数据发送给服务器
    client->write(msg.toLocal8Bit());
    ui->msgedit->clear();
}

//断开连接按钮对应槽函数
void chat::disconbtn_slot()
{
    //准备发送离开信息
    QString msg = username + " : 离开聊天室";
    client->write(msg.toLocal8Bit());
    //断开连接
    client->disconnectFromHost();
}

//断开连接信号槽函数
void chat::disconnect_slot()
{
    QMessageBox::information(this,"断开连接","成功退出聊天室");
    //设置按钮与编辑框可用
    ui->IPedit->setEnabled(1);
    ui->portedit->setEnabled(1);
    ui->connectbtn->setEnabled(1);
    ui->disconbtn->setEnabled(0);
    ui->sndbtn->setEnabled(0);
    ui->msgedit->setEnabled(0);
}

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

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

相关文章

力扣(LeetCode)算法_C++——有效的数独

请你判断一个 9 x 9 的数独是否有效。只需要 根据以下规则 &#xff0c;验证已经填入的数字是否有效即可。 数字 1-9 在每一行只能出现一次。 数字 1-9 在每一列只能出现一次。 数字 1-9 在每一个以粗实线分隔的 3x3 宫内只能出现一次。&#xff08;请参考示例图&#xff09; …

vue3集成jsoneditor

一、背景 之前在做录制回放平台的时候&#xff0c;需要前端展示子调用信息&#xff0c;子调用是一个请求列表数组结构&#xff0c;jsoneditor对数组的默认展示结构是[0].[1].[2]..的方式&#xff0c;为了达到如下的效果&#xff0c;必须用到 onNodeName的钩子函数&#xff0c;…

使用Minifilter过滤驱动保护文件

代码如下: 可以保护拓展名.com文件不被删除、重命名、读写、可执行。#include <ntifs.h> #include <ntstrsafe.h> #include <fltKernel.h> static UNICODE_STRING ProtectedExtention RTL_CONSTANT_STRING(L"com"); //卸载回调 PFLT_FILTER gFile…

如何写一个可以找到工作的简历不至于太烂

简历是自己的一个很重要的标签&#xff0c;是获得面试的敲门砖&#xff0c;简历是要时常更新的&#xff0c;否则会错过一些机会。简历也是给自己的正反馈。 方法 ● 模仿&#xff0c;例如Boss&#xff0c;拉钩下面都给你一个案例模板供你参考&#xff0c;但是我觉得其实参考性…

基础算法--二分查找

二分查找 算法原理 1. 简介 故事分享&#x1f3ec;&#xff1a; 有一天小明到图书馆借了 N 本书&#xff0c;出图书馆的时候&#xff0c;警报响了&#xff0c;于是保安把小明拦下&#xff0c;要检查一下哪本书没有登记出借。小明正准备把每一本书在报警器下过一下&#xff0…

C语言基本知识

基础 第一个函数 argc代表参数个数argument count。argv代表参数value&#xff0c;第一个为放的是文件名&#xff0c;后面是传入的参数。 编译过程 预处&#xff1a;gcc -E hello.c -o hello.i编译&#xff1a;gcc -S hello.c(.i) -o hello.s汇编&#xff1a;gcc -c hello.c…

2023年09月数据库流行度最新排名

点击查看最新数据库流行度最新排名&#xff08;每月更新&#xff09; 2023年09月数据库流行度最新排名 TOP DB顶级数据库索引是通过分析在谷歌上搜索数据库名称的频率来创建的 一个数据库被搜索的次数越多&#xff0c;这个数据库就被认为越受欢迎。这是一个领先指标。原始数…

C语言“牵手”1688商品详情数据方法,1688商品详情API接口,1688API申请指南

1688是中国最大的自营式电商企业&#xff0c;在线销售计算机、手机及其它数码产品、家电、汽车配件、服装与鞋类、奢侈品、家居与家庭用品、化妆品与其它个人护理用品、食品与营养品、书籍与其它媒体产品、母婴用品与玩具、体育与健身器材以及虚拟商品等。 1688平台的商品详情…

route命令小结

Destination: 如果不满足该列的任何一个ip,则走默认的default Gataway: *是 不指定gateway.有的系统是0.0.0.0,与*意义相同 Genmask: 0.0.0.0是不指定掩码, 255.255.0.0掩码了16位,172.17 开头的ip,会走这个网关 255.255.255.0掩码了16位,192.168.0 开头的ip都会走这个网关 当是…

基于Alexnet深度学习网络的人员口罩识别算法matlab仿真

目录 1.算法运行效果图预览 2.算法运行软件版本 3.部分核心程序 4.算法理论概述 5.算法完整程序工程 1.算法运行效果图预览 2.算法运行软件版本 matlab2022a 3.部分核心程序 file_path1 test\mask\;% 图像文件夹路径 %获取测试图像文件夹下所有jpg格式的图像文件…

网工内推 | 国企专场,网络运维工程师,华为/思科认证优先

01 中百集团 招聘岗位&#xff1a;运维工程师 职责描述&#xff1a; 1、对集团内使用云计算架构&#xff08;Kubernetes&#xff09;的系统进行规划、运维及管理相关工作。 2、对集团数据中心系统的大数据基础架构&#xff08;Cloudera Distribution Hadoop&#xff09;的规划…

vue3项目修改浏览器的项目icon小图标

修改vue3项目的浏览器的图标 vue2修改图标

微信的标签怎样管理?怎样标签群发更高效?(建议收藏)

01 管理标签的意义 随着时间的递增&#xff0c;微信好友越来越多&#xff0c;很容易就超过了上千人&#xff0c;如果这时候&#xff0c;还没有具备管理标签的意识&#xff0c;那就必须得提上日程了。 管理标签有如下几点意义&#xff1a; 1、方便找到对方 2、分组备注&am…

HarmonyOS/OpenHarmony(Stage模型)应用开发单一手势(三)

五、旋转手势&#xff08;RotationGesture&#xff09; RotationGesture(value?:{fingers?:number; angle?:number}) 旋转手势用于触发旋转手势事件&#xff0c;触发旋转手势的最少手指数量为2指&#xff0c;最大为5指&#xff0c;最小改变度数为1度&#xff0c;拥有两个可…

分享一下在微信上有哪些微信活动可以做

微信营销活动是吸引更多用户和提高品牌知名度的有效策略。下面是一些微信营销活动的做法&#xff1a; 抽奖活动&#xff1a;通过设置奖品和参与条件&#xff0c;吸引用户参与抽奖活动。例如&#xff0c;可以设置关注公众号、转发活动页面等条件&#xff0c;吸引更多用户参与抽奖…

Redis 分布式锁

面试题&#xff1a; Redis除了拿来做缓存&#xff0c;你还见过基于Redis的什么用法&#xff1f; 1.数据共享&#xff0c;分布式Session 2.分布式锁 3.全局ID 4.计算器、点赞 5.位统计 6.购物车 7.轻量级消息队列&#xff1a;list、stream 8.抽奖 9.点赞、签到、打卡 10.差集交集…

手机技巧:推荐7款日常生活中实用的工具类app

目录 1、DeepL-翻译神器 2、我的倒计时 3、醒图APP-图片美化神器 4、DAMA 5、氢时光 今天给大家推荐7款日常生活中实用的工具类app&#xff01; 1、DeepL-翻译神器 Deepl手机版是一款强大简便的在线翻译工具&#xff0c;与全球最精确的语言词典系统连接&#xff0c;您可以…

二进制安全虚拟机Protostar靶场(3)溢出控制程序指针,基础知识讲解 Stack Three,Stack Four

前言 这是一个系列文章&#xff0c;之前已经介绍过一些二进制安全的基础知识&#xff0c;这里就不过多重复提及&#xff0c;不熟悉的同学可以去看看我之前写的文章 二进制安全虚拟机Protostar靶场 安装,基础知识讲解,破解STACK ZERO https://blog.csdn.net/qq_45894840/artic…

生信分析-在线小工具|永久收藏

生信分析软件在生物信息学研究中可以帮助研究人员处理、分析和解释生物学数据&#xff0c;从而揭示生物学系统的结构和功能。如数据处理和格式转换、序列比对和测序数据分析、基因组注释和功能预测、基因表达分析、变异检测和遗传分析、数据可视化等软件功能都可以提高研究效率…

一文讲透:CRM客户管理系统的功能有哪些?

CRM客户管理系统的功能有哪些&#xff1f; CRM客户管理系统是一种能够帮助企业管理客户关系的软件系统&#xff0c;它包括了客户信息管理、销售管理、客户服务管理、营销管理和数据分析等功能&#xff0c;能够帮助企业更好地了解客户需求&#xff0c;优化销售流程&#xff0c;…