Qt--基于TCP客户端与服务器的聊天程序(半双工通信)

news2024/11/29 22:32:06

目录

任务:实现一个基于TCP的聊天程序,需要使用的类有:

QTcpServer ​编辑

QTcpSocket ​编辑

QTextStream 

服务端:server(QTcpServer)

步骤:

代码:

dialog.h

dialog.cpp

客户端:client(QTcpSocket)

步骤:

代码:

dialog.h

dialog.cpp

dialog.ui

 运行效果:

客户端

服务器 


任务:实现一个基于TCP的聊天程序,需要使用的类有:

  • QTcpServer

服务器管理类:管理服务器的多个连接,直接继承了QObject,因此不具备IO能力。

相关函数如下:

// 构造函数
QTcpServer::QTcpServer(QObject * parent = 0)// 服务器开启监听,等待客户主动发起连接
// 参数1:监听来自于哪个IP地址的请求,默认值为不限制IP地址,QHostAddress类是IP地址的封装类。
// 参数2:服务器端口号
// 返回值:监听开启结果
bool QTcpServer::listen(
                const QHostAddress & address = QHostAddress::Any, 
                quint16 port = 0)
// 有新连接建立的通知信号
void QTcpServer::newConnection() [signal]// 服务器是否还在监听
bool QTcpServer::isListening() const// 关闭服务器
void QTcpServer::close()
// 返回一个就绪的连接对象,此对象用于跟某个客户端进行IO操作
QTcpSocket * QTcpServer::nextPendingConnection() [virtual]
  • QTcpSocket

TCP连接类:进行网络IO操作,间接继承QIODevice类。

相关函数如下:

// 构造函数
QTcpSocket::QTcpSocket(QObject * parent = 0)
// 连接到服务器
// 参数1:服务器的IP地址
// 参数2:服务器的端口号
// 参数3:读写模式,默认为可读可写
void QAbstractSocket::connectToHost(const QString & hostName,
                                    quint16 port, 
                                    OpenMode openMode = ReadWrite) [virtual]
// 连接是否处于打开状态
bool QIODevice::isOpen() const
// 关闭连接
void QIODevice::close() [virtual]
// 拿到对面的IP地址封装类对象,如果没有连接返回QHostAddress::Null
QHostAddress QAbstractSocket::peerAddress() const
// 连接断开发射的信号
void QAbstractSocket::disconnected() [signal]
  • QTextStream 

文本流类:是一种高效地文本数据IO的辅助类。

服务端:server(QTcpServer)

步骤:

①创建QTcpServer对象

②监听list需要的参数是地址和端口号

③当有新的客户端连接成功回发送newConnect信号

④在newConnection信号槽函数中,调用nextPendingConnection函数获取新连接QTcpSocket对象

⑤连接QTcpSocket对象的readRead信号

⑥在readRead信号的槽函数使用read接收数据

⑦调用write成员函数发送数据

代码:

dialog.h

#ifndef DIALOG_H
#define DIALOG_H

#include <QDialog>
#include<QMessageBox>
//网络相关类
#include<QTcpServer>
#include<QTcpSocket>
#include<QTextStream>
#include<QDateTime>

namespace Ui {
class Dialog;
}

class Dialog : public QDialog
{
    Q_OBJECT

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

private:
    Ui::Dialog *ui;
    QTcpServer *server;//服务器对象
    QTcpSocket *socket=NULL;
private slots:
    void newConnSlot();//新连接的槽函数
    void disconnectedSlot(); //网络连接断开的槽函数
    void readReadSlot(); //读取客户端信息的槽函数
    void btnSendClickedSlot();//发送给客户端消息的槽函数
};

#endif // DIALOG_H

dialog.cpp

#include "dialog.h"
#include "ui_dialog.h"

Dialog::Dialog(QWidget *parent) :
    QDialog(parent),
    ui(new Ui::Dialog)
{
    ui->setupUi(this);
    //设置窗口标记为顶层
    setWindowFlags(Qt::WindowStaysOnTopHint);

    //创建管理类对象
    server=new QTcpServer(this);

    //连接服务器通知的信号槽
    connect(server,SIGNAL(newConnection()),this,SLOT(newConnSlot()));
    //向客户端发送信息的信号槽
    connect(ui->pushButtonSend,SIGNAL(clicked()),this,SLOT(btnSendClickedSlot()));

    // 服务器开启监听,等待客户主动发起连接
    // 参数1:监听来自哪个IP地址的请求,默认值为不限制IP地址,QHostAddress类是IP地址的封装类。
    // 参数2:服务器端口号
    server->listen(QHostAddress::Any,8887);
}

Dialog::~Dialog()
{
    if(server->isListening())//如果在监听
          server->close();//关闭服务器
    delete ui;
}

void Dialog::newConnSlot()
{
    //踢掉之前的连接
    if(socket!=NULL)
    {
        socket->close();
    }
    //拿到与客户端进行连接的QTcpSocket对象
    socket=server->nextPendingConnection();
    //建立断链通知的信号槽
    connect(socket,SIGNAL(disconnected()), this,SLOT(disconnectedSlot()));
    //建立读取消息的信号槽
    connect(socket,SIGNAL(readyRead()),this,SLOT(readReadSlot()));
    //拿到客户端的ip和端口号
    QString ip=socket->peerAddress().toString();
    quint16 port=socket->peerPort();
    QString time=QDateTime::currentDateTime().toString("hh:mm:ss");
    ui->textBrowser->append(time);
    ui->textBrowser->append("新连接来了!");
    ui->textBrowser->append("");
    ui->textBrowser->append(ip.append(":").append(QString::number(port)));
    ui->textBrowser->append("");
}

void Dialog::disconnectedSlot()
{
    //拿到客户端的ip和端口号
    QString ip=socket->peerAddress().toString();
    quint16 port=socket->peerPort();
    QString time=QDateTime::currentDateTime().toString("hh:mm:ss");
    ui->textBrowser->append(time);
    ui->textBrowser->append("连接结束了!");
    ui->textBrowser->append("");

    ui->textBrowser->append(ip.append(":").append(QString::number(port)));
    ui->textBrowser->append("");
}


void Dialog::readReadSlot()  //读取客户端信息
{
  QTextStream input(socket);
  QString time=QDateTime::currentDateTime().toString("hh:mm:ss");
  //读取数据
  QString msg=input.readAll();
  //展示内容
  ui->textBrowser->append(time);
  ui->textBrowser->append(msg);
}

void Dialog::btnSendClickedSlot()  //向客户端发送消息
{
    //获取用户输入的内容
   QString info=ui->lineEdit->text();
   if(info=="")
   {
      QMessageBox::warning(this,"提示","请输入发送的内容");
      return;
   }
   //创建文本流对象
   QTextStream output(socket);
   //发送内容
   output<<info;
   ui->lineEdit->clear();
}

客户端:client(QTcpSocket)

步骤:


①创建QTcpSocket对象

②当对象与Server连接成功时会发送connected 信号

③调用成员函数connectToHost连接服务器,需要的参数是地址和端口号

④connected信号的槽函数开启发送数据

⑤使用write发送数据,read接收数据

代码:

dialog.h

#ifndef DIALOG_H
#define DIALOG_H

#include <QDialog>
#include<QMessageBox>
#include<QDateTime>
//连接类
#include<QTcpSocket>
#include<QTextStream>
namespace Ui {
class Dialog;
}

class Dialog : public QDialog
{
    Q_OBJECT

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

private:
    Ui::Dialog *ui;
    QTcpSocket *client;//连接对象
    QTcpSocket *socket=NULL;
private slots:
    void btnConnClickedSlot();//连接按钮的槽函数
    void btnSendClickedSlot();//发送按钮的槽函数
    void connectedSlot();  //连接的槽函数
    void disconnectedSlot(); //断开连接的槽函数
    void readInfoSlot();//接收信息的槽函数
};

#endif // DIALOG_H

dialog.cpp

#include "dialog.h"
#include "ui_dialog.h"

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

    connect(ui->pushButtonConn,SIGNAL(clicked()),this,SLOT(btnConnClickedSlot()));
    connect(ui->pushButtonSend,SIGNAL(clicked()), this,SLOT(btnSendClickedSlot()));
    //设置窗口标记为顶层
    setWindowFlags(Qt::WindowStaysOnTopHint);
    //创建连接对象
    client=new QTcpSocket(this);
    //连接状态检测的信号槽
    connect(client,SIGNAL(connected()),this,SLOT(connectedSlot()));
    connect(client,SIGNAL(disconnected()),this,SLOT(disconnectedSlot()));
    //读取服务器发来消息的信号槽
    connect(client,SIGNAL(readyRead()),this,SLOT(readInfoSlot()));
}

Dialog::~Dialog()
{
    if(client->isOpen())//如果连接处于打开状态
          client->close();//关闭连接
    delete ui;
}

void Dialog::btnConnClickedSlot()
{
    // 默认输入有效,连接到服务器
    // 参数1:服务器的IP地址
    // 参数2:服务器的端口号
    client->connectToHost(ui->lineEditIp->text(),8887);
}

void Dialog::btnSendClickedSlot()
{
   //获取用户输入的内容
    QString msg=ui->lineEditInfo->text();
    if(msg=="")
    {
        QMessageBox::warning(this,"提示","请输入要发送的内容!");
        return;
    }
    //创建文本流对象
    QTextStream output(client);
    //发送内容
    output<<msg;
    //清空输入框
    ui->lineEditInfo->clear();
}

void Dialog::connectedSlot()
{
    //屏蔽连接按钮
    ui->pushButtonConn->setEnabled(false);
    ui->pushButtonConn->setText("已连接");
    //释放发送按钮
    ui->pushButtonSend->setEnabled(true);
}
void Dialog::disconnectedSlot()
{
    //恢复连接按钮
    ui->pushButtonConn->setEnabled(true);
    ui->pushButtonConn->setText("连接");
    //屏蔽发送按钮
    ui->pushButtonSend->setEnabled(false);
}
void Dialog::readInfoSlot()
{
    QTextStream input(client);
    QString time=QDateTime::currentDateTime().toString("hh:mm:ss");
    //读取数据
    QString info=input.readAll();
    ui->textBrowser->append(time);
    ui->textBrowser->append(info);
}

dialog.ui

 运行效果:

客户端

 

服务器 

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

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

相关文章

论文笔记:基于手机位置信息的地图匹配算法

2015计算机应用 整体思路和论文笔记&#xff1a;Hidden Markov Map MatchingThrough Noise and Sparseness_UQI-LIUWJ的博客-CSDN博客 很像&#xff0c;也是应用HMM进行地图匹配 HMMM本文 状态转移矩阵 观测概率矩阵 正态分布均值都是0&#xff0c;唯一不同的是S…

统计学习方法第五章——决策树

x.1 决策树前言 decision tree决策树是一种分类和回归的方法&#xff0c;本章只考虑在分类领域的使用。决策树使用了归纳法划分特征空间&#xff0c;以此来达到分类的目的。决策树不同于KNN中的kd树&#xff0c;它是多叉树&#xff0c;不是二叉树。决策树是一种概率模型。 决…

毕业2年,月薪25k,有时候人与人的差距比人和狗还大···

想起两年前交流过的一个应届生&#xff0c;当时他刚毕业技术水平不高&#xff0c;进了一个小公司做测试实习工作。最近联系上了&#xff0c;不问不知道&#xff0c;一问吓一跳&#xff0c;他现在已经进了某一线大厂&#xff0c;月薪25K。这位朋友其实也没比别人强多少&#xff…

原神服务端架构搭建工具+环境配置资料

原神服务端架构搭建工具环境配置资料 我是艾西&#xff0c;今天给大家分享一份详细的原神服务端结构资料教程&#xff0c;从服务端的获取到端口的使用以及安卓和ios的DAIL签名等一文让你明白怎么架设原神服务端&#xff0c;哪些工具资料又代表着什么意思&#xff08;保姆级教学…

RFM模型

目录标题 定义指标分析模型分析如何衡量每个客户的RFM指标1. 确定时间范围2. 要定义指标的衡量标准3. 对客户指标进行打分4. 计算平均值5. 用户分类 不同客户不同解决方案 RFM的最大短板RFM的深层问题R&#xff1a;用户离得越久就越有流失风险F&#xff1a;用户频次越高越忠诚M…

SpringBoot 日志文件从入门到实战

文章目录 1. 日志的作用2. 日志怎么用3. 自定义日志打印3.1 程序中得到日志对象3.2 使用日志对象打印日志3.3 日志格式说明 4. 日志级别4.1 日志级别的作用4.2 日志级别的分类与使用4.3 日志级别的设置4.4 综合练习 5. 日志的持久化5.1 配置日志文件的保存路径:5.2 配置日志文件…

MVP发布后,下一步该怎么办

MVP发布后&#xff0c;下一步该怎么办 一、为什么要从发布MVP开始&#xff1f;二、发布MVP后该做什么&#xff1f;1、推广MVP2、收集用户反馈3、进行用户发展访谈4、确定功能的优先次序5、建立一个产品路线图 三、如何衡量一个MVP的成功&#xff1f;1、分析仪表板的重要性2、创…

图解LeetCode——160. 相交链表

一、题目 给你两个单链表的头节点 headA 和 headB &#xff0c;请你找出并返回两个单链表相交的起始节点。如果两个链表不存在相交节点&#xff0c;返回 null 。 图示两个链表在节点 c1 开始相交&#xff1a; 题目数据 保证 整个链式结构中不存在环。注意&#xff0c;函数返回…

怎么把jfif改成png格式?这四种方法值得一试

怎么把JFIFf改成PNG格式&#xff1f;众所周知&#xff0c;JFIF是一种JPEG图像格式的子集&#xff0c;可以提供高质量的压缩效果。但是&#xff0c;JFIF格式有一些限制&#xff0c;例如它不支持透明度和多图层等功能&#xff0c;而PNG格式则是一种无损压缩的图像文件格式&#x…

从零开始Vue3+Element Plus后台管理系统(九)——使用API协作平台Mock数据

截止目前&#xff0c;本项目用了2个接口&#xff0c;一个登录&#xff0c;一个获取列表数据。接下来想做的页面和功能&#xff0c;为了看起来更真实&#xff0c;就需要增加更多的模拟数据。 Mock语法写得有些随意&#xff0c;看起来还是很假 &#x1f601; Mock数据可以使用M…

认识IPv6---寻址模式与地址类型与格式

本文目录 1、IPv6寻址模式1.1、单播(unicast)1.1、组播(multicast)1.1、任播(Anycast) 2、IPv6地址的类型与格式2.1、IPv6地址的格式2.2、IPv6地址的类型2.2.1、单播地址简介2.2.2、组播地址简介2.2.3、任播地址简介 IPv6的出现&#xff0c;最重要的原因是为了解决IPv4地址不足…

我扛住字节面试了,太干了......

春招进展快 2 个月&#xff0c;今年相比往年我感觉比较卷&#xff0c;很少见到offer收割机的选手。 不管环境如何&#xff0c;持续学习这个是不能放弃的&#xff0c;心态也要稳一稳&#xff0c;坦然面对失败&#xff0c;失败才是常态&#xff0c;成功可能才是偶然的。 好了&a…

LitCTF-web-WP(部分)

前言 CSDN内容合伙人 2023年CSDN新星计划Web安全方向导师。 华为MindSpore截至目前最年轻的优秀开发者 阿里云专家博主、华为网络安全云享专家以及腾讯云自媒体分享计划博主。 吉林师范大学CTF校队——SuD0战队的队长 吉林师范大学网信网安学生负责人 核心粉丝群超过50人 带队…

网址域名查询-域名注册查询工具

域名查找软件 域名查找软件是一种能够帮助用户快速查询域名相关信息的工具。它通常提供了批量域名查询和实时域名查询服务&#xff0c;能够帮助用户查询域名的注册信息、到期时间、所有者信息、域名服务器等多种相关信息。以下是域名查找软件的主要特点&#xff1a; 批量域名查…

roadmap go语言

技术类的Roadmap&#xff08;路线图&#xff09;具有许多好处&#xff0c;下面是其中几个主要的好处&#xff1a; 明确目标&#xff1a;Roadmap可以帮助技术团队明确目标和愿景。它提供了一个清晰的计划&#xff0c;使团队成员知道他们正在朝着什么方向前进&#xff0c;并且可…

网络正常但是web、ftp、telnet应用新建连接偶尔卡顿处理方法

目录 问题现象 故障定位 TCP报头 options详解 Opions Kind有哪些 options中的Timestamp详解 TSval & TSecr Timestamp Value的单位是什么 TCP连接的建立与释放 普通三次握手 带时间戳的三次握手 抓包展示带时间戳的tcp会话 WireShark中的时间 VS tcpdump 直接…

KDZD400Q紫外臭氧浓度分析仪

一、产品概述 检测仪用于快速检测多种气体浓度、温湿度测量并超标报 警的场合。采用 2.31 寸高清彩屏实时显示&#xff0c;选用进口品牌的气体传感器&#xff0c;主要检测原理有&#xff1a; 电化学、红外、催化燃烧、热导、PID 光离子等。 可以检测管道中或受限空间、大气环境…

耗时几个月,终于决定把原本想用于商业的系统开源了

前言 嗨&#xff0c;大家好&#xff0c;我是希留&#xff0c;一个被迫致力于全栈开发的老菜鸟。 今天又来给小伙伴们分享一个基于 SpringBoot Vue 实现的前后端分离后台管理系统项目&#xff1b; 简介 这个项目是基于xiliu-tenant脚手架项目搭建而成&#xff0c;原本是帮朋…

USB转UART转串口芯片 GP232RNL国产低成本替代FT232RL/FT232RNL

近期收到很多人咨询FT232RL跟新版FT232RNL两者有什么区别&#xff0c;实际上就是内部做了一点升级&#xff0c;FT232RNL支持Windows11系统&#xff0c;参数并没有改动&#xff0c;完全可以直接替换使用。 今天小编给大家讲讲FT232RNL国产低成本替代芯片–GP232RNL GP232RNL 是…

动态规划:01背包理论基础 一维dp数组(滚动数组)

确定dp数组的定义 在一维dp数组中&#xff0c;dp[j]表示&#xff1a;容量为j的背包&#xff0c;所背的物品价值可以最大为dp[j]。 一维dp数组的递推公式 dp[j]为 容量为j的背包所背的最大价值&#xff0c;那么如何推导dp[j]呢&#xff1f; dp[j]可以通过dp[j - weight[i]]推导…