Qt开发-TCP/IP网络通信(以及文件传输)

这篇具有很好参考价值的文章主要介绍了Qt开发-TCP/IP网络通信(以及文件传输)。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

引言

TCP/IP通信(即SOCKET通信)是通过网线将服务器Server端客户机Client端进行连接,在遵循ISO/OSI模型的四层层级构架的基础上通过TCP/IP协议建立的通讯。控制器可以设置为服务器端或客户端。

关于TCP/IP协议可详看:TCP/IP协议详解 - 知乎 (zhihu.com)

总的来说,TCP/IP通讯有两个部分:

  • 客户端服务器

  • QTcpServer(监听套接字)QTcpSocket(通讯套接字)

监听套接字,顾名思义,监听关于各种通讯的状态,一旦进行通讯,监听套接字会启动通讯套接字,进行通讯

客户端使用connectToHost函数主动连接服务器后,服务器会触发newConnectio这个槽函数,并进行取出QTcpServer(监听套接字),将相关内容取出并赋给QTcpSocket(通讯套接字)。

客户端向服务器发送数据,触发readyRead(),进行处理,彼此传递时,原理都是这样的。

对双方来说都起作用的部分:

  1. 一旦建立连接,就会触发connected,服务器特殊一点,触发的是newConnectio

  1. 互传数据也是一样的,一旦接受到,就会触发readyread

服务器中,需要监听套接字以及通讯套接字,监听套接字用于监听客户端是否给服务器发送请求

本篇博文做了初步的学习与尝试,编写了一个客户端和服务器基于窗口通信以及文件传输的小例程。


一,客户端

客户端的代码比服务器稍简单,总的来说,使用QT中的QTcpSocket类与服务器进行通信只需要以下5步:

(1)创建QTcpSocket套接字对象

    socket = new QTcpSocket(this);

(2)使用这个对象连接服务器

    QString ip = ui.lineEdit_ip->text();//获取ip
    int port = ui.lineEdit_2->text().toInt();//获取端口数据
    socket->connectToHost(ip, port);

(3)使用write函数向服务器发送数据

    QByteArray data = ui.lineEdit_3->text().toUtf8();//获取lineEdit控件中的数据并发送给服务器
    socket->write(data);

(4)当socket接收缓冲区有新数据到来时,会发出readRead()信号,因此为该信号添加槽函数以读取数据

 connect(socket, &QTcpSocket::readyRead, this, &QTcpClinet::ReadData);
void QTcpClinet::ReadData()
{
    QByteArray buf = socket->readAll();
    ui.textEdit->append(buf);
}

(5)断开与服务器的连接(关于close()和disconnectFromHost()的区别,可以按F1看帮助)

socket->disconnectFromHost();

客户端例程:(新建一个qt项目QTcpClinet(客户机))

  • ui界面

Qt开发-TCP/IP网络通信(以及文件传输)

本地回路ip:127.0.0.1 可以连接到本地ip(电脑内部循环的ip)

如果要和局域网其他ip连接 -> 在运行(win+R)+cmd+ipconfig ->ipv4地址 查看本机ip

  • QTcpClinet.h

#include <QtWidgets/QWidget>
#include "ui_QTcpClinet.h"
#include"QTcpSocket.h"
#pragma execution_character_set("utf-8")
class QTcpClinet : public QWidget
{
    Q_OBJECT

public:
    QTcpClinet(QWidget *parent = Q_NULLPTR);
    ~QTcpClinet();
public slots:
    void on_btn_connect_clicked();
    void ReadData();
    void on_btn_push_clicked();
private:
    Ui::QTcpClinetClass ui;
    QTcpSocket* socket;//创建socket指针
};
  • QTcpClinet.cpp

#include "QTcpClinet.h"

QTcpClinet::QTcpClinet(QWidget *parent)
    : QWidget(parent)
{
    ui.setupUi(this);
    socket = new QTcpSocket(this);
}

QTcpClinet::~QTcpClinet()
{
    delete this->socket;//回收内存
}

void QTcpClinet::on_btn_connect_clicked()
{
  if (ui.btn_connect->text()==tr("连接服务器"))
  {
    QString ip = ui.lineEdit_ip->text();//获取ip
    int port = ui.lineEdit_2->text().toInt();//获取端口数据
    //取消已有的连接
    socket->abort();
    //连接服务器
    socket->connectToHost(ip, port);
    bool isconnect = socket->waitForConnected();//等待直到连接成功
    //如果连接成功
    if (isconnect)
    {
        ui.textEdit->append("The connection was successful!!");
        ui.btn_push->setEnabled(true);//按钮使能
        //修改按键文字
        ui.btn_connect->setText("断开服务器连接");
        //接收缓冲区(服务器)信息
        connect(socket, &QTcpSocket::readyRead, this, &QTcpClinet::ReadData);
    }
    else
    {
        ui.textEdit->append("The connection falied!!");
    }
  }
  else
  {
      //断开连接
      socket->disconnectFromHost();
      ui.btn_connect->setText("连接服务器");
      ui.btn_push->setEnabled(false);//关闭发送按钮使能
  }

}

//接收缓冲区信息函数
void QTcpClinet::ReadData()
{
    QByteArray buf = socket->readAll();
    ui.textEdit->append(buf);
}
//发送按钮事件
void QTcpClinet::on_btn_push_clicked()
{
    QByteArray data = ui.lineEdit_3->text().toUtf8();//获取lineEdit控件中的数据并发送给服务器
    socket->write(data);
    //判断是否写入成功
    bool iswrite = socket->waitForBytesWritten();
    if (iswrite)
    {
        //写入成功
    }
    else
    {
        //没有写入成功
    }
}

二,服务器(需要一直运行哦)

服务器除了使用到了QTcpSocket类,还需要用到QTcpSever类。即便如此,也只是比客户端复杂一点点,用到了6个步骤:

(1)创建QTcpSever对象

    server = new QTcpServer(this);

(2)侦听一个端口,使得客户端可以使用这个端口访问服务器

    server->listen(QHostAddress::Any, 6677);//监听所有ip和6677端口

(3)当服务器被客户端访问时,会发出newConnection()信号,因此为该信号添加槽函数,并用一个QTcpSocket对象接受客户端访问

connect(server, &QTcpServer::newConnection, this, &TcpServer::ClientConnect);
void TcpServer::ClientConnect()
{
    //解析所有客户连接
    while (server->hasPendingConnections())
    {
        //连接上后通过socket(QTcpSocket对象)获取连接信息
        socket = server->nextPendingConnection();
        QString str = QString("[ip:%1,port:%2]").arg(socket->peerAddress().toString()).arg(socket->peerPort());//监听客户端是否有消息发送
        connect(socket, &QTcpSocket::readyRead, this, &TcpServer::ReadData1);
    }
}

(4)使用socket的write函数向客户端发送数据

socket->write(data);

(5)当socket接收缓冲区有新数据到来时,会发出readRead()信号,因此为该信号添加槽函数以读取数据

//监听客户端是否有消息发送
connect(socket, &QTcpSocket::readyRead, this, &TcpServer::ReadData1);
//获取客户端向服务器发送的信息
void TcpServer::ReadData1()
{
    QByteArray buf = socket->readAll();//readAll最多接收65532的数据
    QString str = QString("[ip:%1,port:%2]").arg(socket->peerAddress().toString()).arg(socket->peerPort());
    ui.textEdit_server->append(str +QString(buf));
    //socket->write("ok");//服务器接收到信息后返回一个ok
}

(6)取消侦听

server->close();

服务器例程:(添加一个新的qt项目TcpServer(服务器))

  • ui界面

  • TcpServer.h

#include <QtWidgets/QWidget>
#include"ui_TcpServer.h"
#include"qtcpserver.h"
#include"qtcpsocket.h"

class TcpServer : public QWidget
{
    Q_OBJECT

public:
    TcpServer(QWidget *parent = Q_NULLPTR);
    ~TcpServer();
public slots:
    void on_btn_server_clicked();
    void on_btn_listen_clicked();
private:
    Ui::TcpServerClass ui;
    QTcpServer* server;
    QTcpSocket* socket;//一个客户端对应一个socket
    void ClientConnect();
    void ReadData1();
    
};
  • TcpServer.cpp

#include "TcpServer.h"
#include"qstring.h"
#include"qdebug.h"
#pragma execution_character_set("utf-8")
TcpServer::TcpServer(QWidget *parent)
    : QWidget(parent)
{
    ui.setupUi(this);
    server = new QTcpServer(this);
   //客户机连接信号槽
    connect(server, &QTcpServer::newConnection, this, &TcpServer::ClientConnect);
}

TcpServer::~TcpServer()
{
    server->close();
    server->deleteLater();
}

void TcpServer::on_btn_listen_clicked()
{
    if (ui.btn_listen->text()=="侦听")
    {
        //从输入框获取端口号
        int port = ui.lineEdit_port->text().toInt();
        //侦听指定端口的所有ip
        if (!server->listen(QHostAddress::Any, port))
        {
            //若出错,则输出错误信息
            qDebug() << server->errorString();
            return;
        }
        //修改按键文字
        ui.btn_listen->setText("取消侦听");    
    }
    else
    {
        socket->abort();
        //取消侦听
        server->close();
        //修改按键文字
        ui.btn_listen->setText("侦听");
    }
}

void TcpServer::ClientConnect()
{
    //解析所有客户连接
    while (server->hasPendingConnections())
    {
        //连接上后通过socket获取连接信息
        socket = server->nextPendingConnection();
        QString str = QString("[ip:%1,port:%2]").arg(socket->peerAddress().toString()).arg(socket->peerPort());
        //提示连接成功
        ui.textEdit_server->append(str+"Connect to the server");
        //复选框选项为连接服务器的ip
        ui.comboBox->addItem(str);
        //将socket地址放入combobox属性内
        //ui.comboBox->setItemData(ui.comboBox->count()-1, QVariant((int)socket));
        //监听客户端是否有消息发送
        connect(socket, &QTcpSocket::readyRead, this, &TcpServer::ReadData1);
    }
}

//获取客户端向服务器发送的信息
void TcpServer::ReadData1()
{
    QByteArray buf = socket->readAll();//readAll最多接收65532的数据
    QString str = QString("[ip:%1,port:%2]").arg(socket->peerAddress().toString()).arg(socket->peerPort());
    ui.textEdit_server->append(str +QString(buf));
}

//服务器向客户端发送信息
void TcpServer::on_btn_server_clicked()
{
  if(ui.comboBox->count()== 0)return;
  //QTcpSocket* skt=  (QTcpSocket*)ui.comboBox->itemData(ui.comboBox->currentIndex()).value<int>();
  socket->write(ui.lineEdit1->text().toUtf8());
}

注意:write中需要写入char类型的元素或QByteArray类型的元素

Qt开发-TCP/IP网络通信(以及文件传输)

效果展示:

Qt开发-TCP/IP网络通信(以及文件传输)

三,TCP/IP文件传输

上文实现了消息的传输,由于socket->readAll();(readAll最多接收65532的数据),因此对于大文件的传输用此方法是不可取的。

TCP/IP文件传输的思路:

  1. 客户端和服务器连接

  1. 客户端选择文件,并发送文件给服务器(发送的是文件的帧头,格式:文件名&大小)

  1. 服务器触发readyRead,然后解析文件帧头(获取文件名和大小),并返回客户端一个ok消息

  1. 客户端触发readyRead,然后发送文件数据,通过progressBar显示进度

  1. 服务器再次触发readyRead,接收文件数据,并保存(通过ishead判断接收的是文件帧头还是文件数据)

代码实现:

新建服务器项目(TcpServer)

  • TcpServer.h

#pragma once

#include <QtWidgets/QWidget>
#include "ui_TcpServer.h"
#include"qtcpserver.h"
#include"qtcpsocket.h"
#pragma execution_character_set("utf-8")
class TcpServer : public QWidget
{
    Q_OBJECT

public:
    TcpServer(QWidget *parent = Q_NULLPTR);
    void hasConnect();
private:
    Ui::TcpServerClass ui;
    QTcpServer* server;
    QTcpSocket* socket;
    bool ishead;
    QString fileName;
    int fileSize;//接收文件的总大小
    int recvSize;//当前接收文件的大小
    QByteArray filebuf;//当前接收的文件数据
};
  • TcpServer.cpp

#include "TcpServer.h"
#include"qfile.h"
TcpServer::TcpServer(QWidget *parent)
    : QWidget(parent)
{
    ishead = true;
    ui.setupUi(this);
    server = new QTcpServer(this);
    //监听1122端口的ip
    server->listen(QHostAddress::Any, 1122);
    //如果有用户连接触发槽函数
    connect(server, &QTcpServer::newConnection, this, &TcpServer::hasConnect);
}

void TcpServer::hasConnect()
{
    while (server->hasPendingConnections()>0)//判断当前连接了多少人
    {
        //用socket和我们的客户端连接,一个客户端对应一个套接字socket
        socket = server->nextPendingConnection();
        //服务器界面上输出客户端信息
        ui.textEdit->append(QString("%1:新用户连接").arg(socket->peerPort()));
        //如果客户端发送信息过来了,触发匿名函数
        connect(socket, &QTcpSocket::readyRead, [=]() {
            QByteArray buf = socket->readAll();
            //用一个标志位ishead判断是头还是数据位
            if (ishead)
            {
                //如果是头,解析头(文件名,文件大小)
                QString str = QString(buf);
                ui.textEdit->append(str);
                QStringList strlist = str.split("&");
                fileName = strlist.at(0);//解析帧头文件名
                fileSize = strlist.at(1).toInt();//解析帧头文件大小
                ishead = false;//下次接收到的文件就是我们的数据
                recvSize = 0;
                filebuf.clear();
                socket->write("ok");
            }
            else
            {
                //根据文件名和文件大小接收和保存文件
                filebuf.append(buf);
                recvSize += buf.size();//每接收一次文件,当前文件大小+1
                //当接收文件大小等于总文件大小,即文件数据接收完毕
                if (recvSize>=fileSize)
                {
                    //保存文件
                    QFile file(ui.lineEdit->text() + fileName);
                    file.open(QIODevice::WriteOnly);
                    file.write(filebuf);
                    file.close();
                    ishead = true;
                }
            }
            });
    }
}

新建客户端项目(QTcpClient)

  • QTcpClient.h

#include <QtWidgets/QWidget>
#include"ui_QTcpClient.h"
#include"qtcpsocket.h"
#pragma execution_character_set("utf-8")
class QTcpClient : public QWidget
{
    Q_OBJECT

public:
    QTcpClient(QWidget *parent = Q_NULLPTR);
public slots:
    void on_btn_connect_clicked();
    void on_btn_choose_clicked();
    void on_btn_open_clicked();
    
private:
    Ui::QTcpClientClass ui;
    QTcpSocket* socket;
};
  • QTcpClient.cpp

#include "QTcpClient.h"
#include"qfiledialog.h"
#include"qfileinfo.h"
QTcpClient::QTcpClient(QWidget *parent)
    : QWidget(parent)
{
    ui.setupUi(this);
    socket = new QTcpSocket(this);
    
}
void QTcpClient::on_btn_connect_clicked()
{
    QString ip = ui.lineEdit_ip->text();//获取ip
    int port = ui.lineEdit_port->text().toInt();//获取端口数据
    socket->connectToHost(ip, port);//连接服务器
    //等待连接成功
    if (socket->waitForConnected())
    {
        ui.textEdit->append("<font color='green'>连接服务器成功!</font>");    
        ui.btn_open->setEnabled(true);
        
        //如果服务器发送信息到客户端,触发匿名函数
        connect(socket, &QTcpSocket::readyRead, [=]() {
            //读取服务器发送的信息(即缓冲区信息)
            QByteArray buf = socket->readAll();
            if (buf=="ok")
            {
                QFile file = (ui.label_path->text());
                if (!file.open(QIODevice::ReadWrite))
                {
                    //读取文件失败
                    return;
                }
                qint64 currentlen = 0;//当前已经发送的大小
                qint64 allLength = file.size();//总文件大小
                do
                {
                    char data[1024];
                    qint64 msize = file.read(data, 1024);//读文件放入打他数组中,返回读取到的大小
                    socket->write(data, msize);//把读取到的data数据发送给服务器
                    currentlen += msize;//实时获取当前发送的文件大小
                    ui.progressBar->setValue(currentlen *100 / allLength);//更新界面进度条
                } while (currentlen < allLength);//当发送文件等于文件大小时,发送完毕,循环结束
            }
            });

    }
    else
    {
        ui.textEdit->append("<font color='red'>连接服务器失败!</font>");
    }
}
//选择文件事件
void QTcpClient::on_btn_choose_clicked()
{
    QString path = QFileDialog::getOpenFileName(this, "打开文件", "", "(*.*)");
    ui.label_path->setText(path);
}
//发送文件事件
void QTcpClient::on_btn_open_clicked()
{
    QFileInfo info(ui.label_path->text());
    //用QFileInfo::fileName,size获取文件名和大小 格式:文件名&大小
    //服务器用该格式解析文件名和大小
    QString head = QString("%1&%2").arg(info.fileName()).arg(info.size());
    //将该格式发送给服务器 toUtf8:QString转QByteArray或char类型
    socket->write(head.toUtf8()); 
}

效果展示:文章来源地址https://www.toymoban.com/news/detail-498091.html

Qt开发-TCP/IP网络通信(以及文件传输)

到了这里,关于Qt开发-TCP/IP网络通信(以及文件传输)的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 【网络编程】网络通信基础——简述TCP/IP协议

    个人主页:兜里有颗棉花糖 欢迎 点赞👍 收藏✨ 留言✉ 加关注💓本文由 兜里有颗棉花糖 原创 收录于专栏【网络编程】【Java系列】 本专栏旨在分享学习网络编程的一点学习心得,欢迎大家在评论区交流讨论💌 ip地址简单来说就是用来描述网络上一个设备的所在位置。 端

    2024年02月04日
    浏览(62)
  • QT 中基于 TCP 的网络通信 (备查)

    基于 TCP 的套接字通信需要用到两个类: 1) QTcpServer :服务器类,用于监听客户端连接以及和客户端建立连接。 2) QTcpSocket :通信的套接字类,客户端、服务器端都需要使用。 这两个套接字通信类都属于网络模块 network 。 TCP协议是 双向连接,双向断开,即 三次握手,四次

    2024年02月03日
    浏览(55)
  • 【Qt网络编程】实现TCP协议通信

    传输控制协议(TCP,Transmission Control Protocol)是 一种面向连接的、可靠的、基于字节流的传输层通信协议 ,由IETF的RFC 793 定义。 TCP建立连接前,需要进行三次握手,如下图所示: TCP断开连接前,需要进行四次挥手,如下图所示: Qt中提供了QTcpSocket类和QTcpServer类分别用于创

    2024年02月16日
    浏览(48)
  • 网络通信深入解析:探索TCP/IP模型

            你知道在我们的网页浏览器的地址当中输入url,未必是如何呈现的吗?         web浏览器根据地址栏中指定的url,从web服务器获取文件资源(resource)等信息,从而显示出web页面。web使用HTTP(超文本传输协议)的协议作为规范,完成从客户端从服务器端等一系列的运

    2024年02月07日
    浏览(61)
  • 网络编程——深入理解TCP/IP协议——OSI模型和TCP/IP模型:构建网络通信的基石

    TCP/IP协议,即 传输控制协议/互联网协议 ,是一组用于在计算机网络中实现通信的协议。它由两个主要的协议组成:TCP(传输控制协议)和IP(互联网协议)。TCP负责确保数据的可靠传输,而IP则负责路由数据包以在网络中传递。TCP/IP协议簇还包含其他辅助协议,如UDP(用户数

    2024年02月14日
    浏览(57)
  • 深入理解TCP/IP协议:网络通信的基石

    提示:本系列文章重点学习TCP/IP协议 提示:在这里先对TCP/IP协议做一个概述,以便大家能更好的理解: TCP/IP协议是当今互联网世界中最为重要的网络通信协议之一,它承载了全球范围内数以亿计的设备之间的通信。无论是在个人日常使用的智能手机,还是在企业级的网络架

    2024年04月16日
    浏览(51)
  • 深入理解网络通信和TCP、IP协议-01

    计算机网络是什么? 随着计算机技术发展,计算机的体积和价格都在下降,之前计算机多用于研究机构,现 阶段逐步进入一般的公司用于办公。原来计算机之间传输数据需要通过软盘等第三方存储介 质进行转存,人们需要将数据直接通过通信线路传输,来缩短传输时间,于

    2024年02月16日
    浏览(55)
  • 网络编程day2——基于TCP/IP协议的网络通信

            计算机S                                                 计算机C      创建socket对象                                   创建socket对象      准备通信地址(自己的ip(非公网ip))      准备通信地址                                     (计算

    2024年02月10日
    浏览(68)
  • 《TCP/IP网络编程》阅读笔记--进程间通信

    目录 1--进程间通信 2--pipe()函数 3--代码实例 3-1--pipe1.c 3-2--pipe2.c 3-3--pipe3.c 3-4--保存信息的回声服务器端         为了实现进程间通信,使得两个不同的进程间可以交换数据,操作系统必须提供两个进程可以同时访问的内存空间;         为了完成进程间通信,需要创建管道

    2024年02月09日
    浏览(53)
  • QT C++ 基于TCP通信的网络聊天室

    .ui .pro 在pro文件中添加network库 .h .main .cpp .ui .pro 在pro文件中添加network库 .h .main .cpp        

    2024年02月09日
    浏览(60)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包