QT学习(网络通讯:TCP)-1

QT学习(网络)

TCP

TCP即TransmissionControl Protocol,传输控制协议。与UDP不同,它是面向连接和数据流的可靠传输协议。也就是说,它能使一台计算机上的数据无差错的发往网络上的其他计算机,所以当要传输大量数据时,我们选用TCP协议。

TCP协议的程序使用的是客户端/服务器(C/S)模式,在Qt中提供了QTcpSocket类来编写客户端程序,使用QTcpServer类编写服务器端程序。我们在服务器端进行端口的,一旦发现客户端的连接请求,就会发出newConnection()信号,可以关联这个信号到我们自己的槽进行数据的发送。而在客户端,一旦有数据到来就会发出readyRead()信号,可以关联此信号进行数据的接收。其实,在程序中最难理解的地方就是程序的发送和接收了,为了让大家更好的理解,我们在这一节只是讲述一个传输简单的字符串的例子,在下一节再进行扩展,实现任意文件的传输。

进入代码环节

***.pro

QT       += core gui network

界面如下:
在这里插入图片描述
在这里插入图片描述

***1.h

#ifndef WIDGET_H
#define WIDGET_H

#include <QWidget>
#include <QtNetwork/QtNetwork>

namespace Ui {
class Widget;
}

class Widget : public QWidget
{
    Q_OBJECT

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

private slots:
    void newConnect(); //连接服务器
    void readMessage();  //接收数据
    void displayError(QAbstractSocket::SocketError);  //显示错误

    void on_pushButton_clicked();

private:
    Ui::Widget *ui;
    QTcpSocket *tcpSocket;
    QString message;  //存放从服务器接收到的字符串
    quint16 blockSize; //存放文件的大小信息
};

#endif // WIDGET_H

***1.cpp

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

Widget::Widget(QWidget *parent) :
    QWidget(parent),
    ui(new Ui::Widget)
{
    ui->setupUi(this);
    tcpSocket = new QTcpSocket(this);
    connect(tcpSocket,SIGNAL(readyRead()),this,SLOT(readMessage()));
    connect(tcpSocket,SIGNAL(error(QAbstractSocket::SocketError)),
            this,SLOT(displayError(QAbstractSocket::SocketError)));
}

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

/********************************
    这个函数实现了连接到服务器
    主机:localhost
    端口号:6666
*******************************/
void Widget::newConnect()
{
    blockSize = 0; //初始化其为0
    tcpSocket->abort(); //取消已有的连接

    //连接到主机,这里从界面获取主机地址和端口号
    tcpSocket->connectToHost(ui->hostLineEdit->text(),ui->portLineEdit->text().toInt());
}

/******************************************************************
    这个函数实现了数据的接收,它与服务器端的发送函数相对应。
    首先我们要获取文件的大小信息,然后根据文件的大小来判断是否接收到了完整的文件
    数据流版本:QDataStream::Qt_4_6
******************************************************************/
void Widget::readMessage()
{
    QDataStream in(tcpSocket);
    in.setVersion(QDataStream::Qt_4_6);//设置数据流版本,这里要和服务器端相同
    if(blockSize==0) //如果是刚开始接收数据
    {
       //判断接收的数据是否有两字节,也就是文件的大小信息
       if(tcpSocket->bytesAvailable() < (int)sizeof(quint16))//如果有则保存到blockSize变量中,没有则返回,继续接收数据
       {
           return;
       }
       in >> blockSize;
    }
    if(tcpSocket->bytesAvailable() < blockSize)//如果没有得到全部的数据,则返回,继续接收数据
    {
        return;
    }
    in >> message;//将接收到的数据存放到变量中

    ui->messageLabel->setText(message);//显示接收到的数据
}

//错误信息输出
void Widget::displayError(QAbstractSocket::SocketError)
{
    qDebug() << tcpSocket->errorString(); //输出错误信息
}

//“连接”按钮的单击事件
void Widget::on_pushButton_clicked()//连接按钮
{
     newConnect(); //请求连接
}

***2.h

#ifndef WIDGET_H
#define WIDGET_H

#include <QWidget>
#include <QtNetWork/QtNetwork>

namespace Ui {
class Widget;
}

class Widget : public QWidget
{
    Q_OBJECT

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

private slots:
    void sendMessage();//数据发送函数
private:
    Ui::Widget *ui;
    QTcpServer *tcpServer;
};

#endif // WIDGET_H


***2.cpp

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

Widget::Widget(QWidget *parent) :
    QWidget(parent),
    ui(new Ui::Widget)
{
    ui->setupUi(this);
    tcpServer = new QTcpServer(this);
    if(!tcpServer->listen(QHostAddress::LocalHost,6666)) //本地主机的6666端口,如果出错就输出错误信息,并关闭
    {
        qDebug() << tcpServer->errorString();
        close();
    }
    connect(tcpServer,SIGNAL(newConnection()),this,SLOT(sendMessage()));//连接信号和相应槽函数
}

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

/*******************************************************************************************
    这个是数据发送函数
    (1)为了保证在客户端能接收到完整的文件,我们都在数据流的最开始写入完整文件的大小信息,
    这样客户端就可以根据大小信息来判断是否接受到了完整的文件。
    而在服务器端,在发送数据时就要首先发送实际文件的大小信息,
    但是,文件的大小一开始是无法预知的,所以这里先使用了out<<(quint16) 0;
    在block的开始添加了一个quint16大小的空间,也就是两字节的空间,它用于后面放置文件的大小信息。
    然后out<<tr("hello Tcp!!!");输入实际的文件,这里是字符串。
    当文件输入完成后我们再使用out.device()->seek(0);
    返回到block的开始,加入实际的文件大小信息,也就是后面的代码,
    它是实际文件的大小:out<<(quint16) (block.size() - sizeof(quint16));

    (2)在服务器端我们可以使用tcpServer的nextPendingConnection()函数来获取已经建立的连接的Tcp套接字,
    使用它来完成数据的发送和其它操作。这里,我们关联了disconnected()信号和deleteLater()槽,然后我们发送数据

    数据流版本:QDataStream::Qt_4_6
****************************************************************************************************/
void Widget::sendMessage()
{    
    QByteArray block;//用于暂存我们要发送的数据
    QDataStream out(&block,QIODevice::WriteOnly);//使用数据流写入数据

    out.setVersion(QDataStream::Qt_4_6);//设置数据流的版本,客户端和服务器端使用的版本要相同
    out<<(quint16) 0;
    out<<tr("hello Tcp!!!");
    out.device()->seek(0);
    out<<(quint16) (block.size() - sizeof(quint16));

    QTcpSocket *clientConnection = tcpServer->nextPendingConnection();//我们获取已经建立的连接的子套接字

    connect(clientConnection,SIGNAL(disconnected()),clientConnection,SLOT(deleteLater()));
    clientConnection->write(block);
    clientConnection->disconnectFromHost();

    ui->statusLabel->setText("send message successful!!!");//发送数据成功后,显示提示
}

学习链接:
网络Tcp1

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值