首页 > 编程语言 >QT-TCP网络编程

QT-TCP网络编程

时间:2023-10-01 11:11:34浏览次数:41  
标签:do QT 编程 tcpserver TCP QString 客户端 服务端 socket

总体认识:

Qt NetWork提供了用于编写TCP/IP网络应用程序的各种类:

​ TCP的QTcpSocket和QTcpServer

​ UDP的QUdpSocket


TCP通信:

传输控制协议(transmission control protocol,TCP);可靠的,面向流和连接的传输协议,适合用于连续数据的传输.

通信必须先建立TCP连接;通信端需存在客户端和服务端.

服务端:

服务端必须使用QTcpServer进行端口监听,建立服务器;使用QTcpSocket建立连接,然后使用套接字(socket)进行通信.

QTcpServer的父类是QObject.

服务端程序首先需要使用listen()开始服务器监听,设置服务器监听的IP地址和端口,一般一个服务器只监听某个端口的网络连接.

当有新客户端连入时,QTcpServer的incomingConnection()会创建一个与该客户端连接的QTcpSocket对象,然后发送newConnection()信号;在该槽函数中可以用nextPendingConnection()接受客户端的连接,然后使用对应socket与客户端通信.

QTcpSocket是从QIODevice间接继承的类,因此是一种I/O设备类;

QTcpSocket其他函数都是从其父类QAbstractSocket继承或重定义的;

客户端:

客户端通过QTcpSocket对象与服务器建立连接并保持通信;客户端socket首先通过connectToHost()尝试连接服务器,需指定服务器IP地址和监听端口,该函数以异步方式连接到服务器,不会阻塞程序运行,连接成功后QTcpSocket发送connected()信号.

客户端与服务器建立连接后,各自的socket对象就可以向缓冲区写数据或读数据,当缓冲区有新数据进入时,socket会发送readyRead()信号,一般在对应槽函数读取缓冲区数据.

读取数据与写入数据的缓冲区是分开的,异步工作,不会冲突.


项目:局域网内部通信部分代码展示与解读

服务端:

//初始化服务端
void WidgetManager::initTCPServer()
{
    
    QString localIP = getLocalIP_server();	//获取服务端需要的本地IP

    tcpserver = new QTcpServer(this);	//基于本类创建的tcp server对象,该通信程序只同时存在一个服务端

    connect(tcpserver,SIGNAL(newConnection()),this,SLOT(do_newConnection()));	//将QTcpServer的newConnection信号与槽函数连接,当出现新客户端连入时触发自动调用处理
}

服务端的QTcpSocket对象初始化设置为nullptr不指向,后续用作虚拟客户端的对象;


//服务端获取本地IP
QString WidgetManager::getLocalIP_server()
{
    QString hostName = QHostInfo::localHostName();	//本地主机名
    QHostInfo hostInfo = QHostInfo::fromName(hostName);	//返回对应主机名的IP地址
    QString localIP = "";

    QList<QHostAddress> addrList = hostInfo.addresses();	//本机的IP地址列表

    if(addrList.isEmpty())
        return localIP;
    foreach (QHostAddress aHost, addrList) {
        if(QAbstractSocket::IPv4Protocol == aHost.protocol()){
            localIP = aHost.toString();	//查找IPv4协议的IP地址
            break;
        }
    }
    return localIP;
}

void WidgetManager::serverStartListen(QString serverIP,QString serverPort)
{
    QString IP = serverIP;	//本机监听为127.0.0.1,也可以是QHostAddress::LocalHost
    quint16 port = serverPort.toInt();

    QHostAddress address(IP);	//IP转换为QHostAddress用于参数

    tcpserver->listen(address,port);	//调用listen开始监听

    tcp_server_textfield_append("***开始监听 :"+tcpserver->serverAddress().toString()+" 端口: "+QString::number(tcpserver->serverPort()));

    tcp_server_state_changed(QString("监听状态 : 正在监听"));
}

void WidgetManager::serverStopListen()
{
    if(tcpserver->isListening())	//正在监听
    {
        if(tcpserver_socket != nullptr)	//socket对象存在
        {
            if(tcpserver_socket->state() == QAbstractSocket::ConnectedState)	//状态为已连接
            {
                tcpserver_socket->disconnectFromHost();	//虚拟客户端从服务端断开
            }
        }

        tcpserver->close();	//关闭服务端,此处为停止服务端本身的监听行为

        tcp_server_state_changed(QString("监听状态 : 停止监听"));
    }
}

void WidgetManager::do_newConnection()
{
    tcpserver_socket = tcpserver->nextPendingConnection();	//接受新连接

    connect(tcpserver_socket,SIGNAL(connected()),this,SLOT(do_clientConnected()));	//连接函数与槽函数绑定
    do_clientConnected();	//槽函数试运行,同时让假定服务端创建的虚拟客户端连接
    connect(tcpserver_socket,SIGNAL(disconnected()),this,SLOT(do_clientDisconnected()));	//断连函数与槽函数绑定
    connect(tcpserver_socket,&QTcpSocket::stateChanged,this,&WidgetManager::do_socketStateChanged);	//状态检测函数与槽函数绑定
    do_socketStateChanged(tcpserver_socket->state());	//试运行确定连接状态
    connect(tcpserver_socket,SIGNAL(readyRead()),this,SLOT(do_socketReadyRead()));	//readyRead函数绑定槽函数,缓冲区存在数据后即可触发
}

void WidgetManager::do_socketStateChanged(QAbstractSocket::SocketState socketState)
{
    switch (socketState) {
    case QAbstractSocket::UnconnectedState:
        tcp_server_state_changed("socket 状态: UnconnectedState");    break;
    case QAbstractSocket::HostLookupState:
        tcp_server_state_changed("socket 状态: HostLookupState");    break;
    case QAbstractSocket::ConnectingState:
        tcp_server_state_changed("socket 状态: ConnectingState");    break;
    case QAbstractSocket::BoundState:
        tcp_server_state_changed("socket 状态: BoundState");    break;
    case QAbstractSocket::ClosingState:
        tcp_server_state_changed("socket 状态: ClosingState");    break;
    case QAbstractSocket::ListeningState:
        tcp_server_state_changed("socket 状态: ListeningState");    break;
    }
}

void WidgetManager::do_clientDisconnected()
{
    tcp_server_textfield_append("**client socket disconnected");
    tcpserver_socket->deleteLater();	//删除服务端的socket对象
}

客户端:

//初始化客户端
void WidgetManager::initTCPClient()
{
    tcpClient = new QTcpSocket(this);

    QString localIP = getLocalIP_client();

    // 保留IP显示状态

    connect(tcpClient,SIGNAL(connected()),this,SLOT(do_connected()));	//槽函数连接情况与服务端基本一致
    connect(tcpClient,SIGNAL(disconnected()),this,SLOT(do_disconnected()));
    connect(tcpClient,&QTcpSocket::stateChanged,this,&WidgetManager::do_socketStateChange);
    connect(tcpClient,SIGNAL(readyRead()),this,SLOT(do_socketReadyReadClient()));
}

void WidgetManager::clientStartConnect(QString address, QString port)
{
    QString addr = address;
    quint16 prt = port.toInt();

    tcpClient->connectToHost(addr,prt);	//客户端尝试连接服务端,由服务端决策连接结果
}

void WidgetManager::clientStopConnect()
{
    if(tcpClient->state() == QAbstractSocket::ConnectedState)
        tcpClient->disconnectFromHost();	//客户端主动断开,服务端会有响应
}

void WidgetManager::clientSendData(QString data)
{
    QString msg = data;

    QByteArray str = msg.toUtf8();

    str.append('\n');

    tcpClient->write(str);

    tcpClient->flush();	//写入即发送的刷新函数
}

其余函数与上服务端一致,另编写见完整代码;

此版于2023/10/1 10:56更新,参考QT6开发指南

标签:do,QT,编程,tcpserver,TCP,QString,客户端,服务端,socket
From: https://www.cnblogs.com/NekoBlog/p/17738658.html

相关文章

  • Qt之属性系统
    一、属性的定义1.赋予属性读写操作Qt提供了一个Q_PROPERTY()宏可以定义属性,它也是基于元对象系统实现的,在QObject的子类中,用Q_PROPERTY()定义属性。QWidget类兴义属性的一些例子:Q_PROPERTY(boolfocusREADhasFocus)Q_PROPERTY(boolenableREADisEnableWRITEsetEnable)......
  • JUC并发编程---Lock锁
    文章目录什么是Locksynchronized加锁和Lock加锁代码示例synchronized使用Lock加锁公平锁和非公平锁公平锁:非公平锁:Lock和Synchronized的区别synchronized版的生产者和消费者Lock版的生产者和消费者生产者和消费者出现的问题Condition精准通知和唤醒线程什么是Lock官网介绍:虽然......
  • Go每日一库之157:tproxy (TCP连接代理与分析 )
    你有同感吗?当大家在开发服务端代码的时候,会不会经常有如下疑问?纳闷MySQL连接池到底有多少连接?每个连接的生命周期持续多久?连接异常断开的时候到底是服务端主动断的,还是客户端主动断的?当长时间没有请求的时候,底层库是否有KeepAlive请求?复杂网络情况的处理从来都是后端......
  • pyqt-核心布局
    1、直接启动or选择启动一些应用是基于同一个目录下的配置加载,以及日志写入同一目录。而另一些应用是根据目标或时间等分为不同的配置目录与日志目录。对于前者,那么自然的是启动程序,进行读取和加载。而后者可以是启动后读取加载上一状态,也可以是先进入开始窗口,选择新建或其它。2......
  • pyqt5-QSize尺寸类
    1、介绍QSize,pyqt中用于定义组件尺寸的类。在处理图片对象QIcon等时常用。QSize()QSize(w:int,h:int)QSize(a0:QSize)2、基础使用setHeight(self,h:int)height(self)->intsetWidth(self,w:int)width(self)->int设置高度、获取高度、设置宽度、获取宽度 ......
  • Qt编写网易云界面 (9) -----歌词界面、部分功能实现
    最近就是就是完成一些小功能,歌词界面,部分按钮的点击事件,歌词列表等;效果如图:视频页面歌单列表:歌词界面:点击歌曲详情页面:歌曲列表代码listform:#ifndefLISTFORM_H#defineLISTFORM_H#include<QVector>#include<QWidget>#include<QString>namespaceUi{classLi......
  • NO.6 Linux系统编程-备忘
    一、文件I/OFILE*fp指针(指向的结构体有三个重要的成员)文件描述符:通过文件描述可以找到文件的inode,通过inode可以找到对应的数据块文件指针:读和写共享一个文件指针,读或者写都会引起文件指针的变化文件缓冲区:读或者写会先通过文件缓冲区,主要目的是为了减少对......
  • python提取论文图片波形数据:pyautogui键盘移动鼠标,跨模块全局变量使用,cv2局部放大窗口
    最近写了一个python提取论文图片波形数据的脚本,代码如下。涉及新知识点:pyautogui键盘移动鼠标,跨模块全局变量使用,cv2局部放大窗口,matplotlib图片在pyQT5lable显示,坐标变换,多线程同时使用。搜索相关关键字去对应代码区看注释就可以了。gui窗口:1#-*-coding:utf-8-*-2......
  • tcp协议基础
    前言:买的书到了,特此来学习一下。内容参考《web漏洞解析与攻防实战》-----1tcp协议之前有两篇基础的博客较为浅显的介绍了一下http协议,在http协议发挥作用之前,我们注意到会先建立tcp连接,那tcp连接要如何建立呢?这就需要tcp协议了。tcp协议是一种面向连接的,可靠的,基于字节流,双......
  • Qt之设置QLabel内容太长显示补全处理方法
    #include"mainwindow.h"#include"ui_mainwindow.h"#include<QFontMetrics>MainWindow::MainWindow(QWidget*parent):QMainWindow(parent),ui(newUi::MainWindow){ui->setupUi(this);QStringnewStr=ui->label->text();Q......