QT基础知识-通信(第四天)

1,Linux中Tcp通信流程简介

     服务器端:创建socket(监听套接字),绑定bind,服务器端监听listen,接受accept(通信套接字)。

     客户端:创建socket(通信套接字),连接connect(客户端主动和服务器链接)

2,Qt中Tcp的通信机制

    

Qt中服务器端也有两个套接字,其中一个类似QTcpServer(监听套接字),然后Linux中的bind和listen合在了一起为listen()函数,另一个通信套接字是QTcpSocket(通信套接字)。连接的时候有一个connectToHost(),但是通信的时候是一种信号和槽的连接,当主动连接的时候,成功后,服务器端会收到一个newConnection()信号,然后就会触发槽函数,参函数就会取出建立好连接的套接字 QTcpSocket()。 如果成功和对方建立好连接,通信套接字会自动触发connected();如果对方主动断开连接,通信套接字会自动触发disconnected()信号。发送数据的时候,如果发送成功,对方的通信套接字会触发readyRead()信号,需要在对应的槽函数做接收处理。

3,Qt中的Tcp客户端和服务端开发示例

     效果图:

     服务器代码:

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

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

    tcpServer = NULL;
    tcpSocket = NULL;

    //监听套接字,指定父对象,让其自动回收空间
    tcpServer = new QTcpServer(this);

    tcpServer -> listen(QHostAddress::Any, 8888);

    connect(tcpServer, &QTcpServer::newConnection,
            [=](){
              //取出建立好连接的通信套接字
              tcpSocket = tcpServer -> nextPendingConnection();
              //获取对方的IP和端口
              QString ip = tcpSocket -> peerAddress().toString();
              qint16 port = tcpSocket -> peerPort();
              QString temp = QString("[%1:%2]:成功连接").arg(ip).arg(port);
              ui -> textEditRead -> setText(temp);

              connect(tcpSocket, &QTcpSocket::readyRead,
                      [=](){
                         //从通信套接字中取出内容
                         QByteArray array = tcpSocket -> readAll();
                         ui -> textEditRead -> append(array);
                      }
                      );
            }
           );    
}

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

void Widget::on_buttonSend_clicked()
{
    if(tcpSocket == NULL){
        return;
    }
    //获取编辑区内容
    QString str = ui -> textEditWrite -> toPlainText();
    //利用通信套接字给对方发送数据
    tcpSocket -> write(str.toUtf8().data());
}

void Widget::on_buttonClose_clicked()
{
    if(tcpSocket == NULL){
        return;
    }
    //主动和客户端断开连接
    tcpSocket ->disconnectFromHost();
    tcpSocket -> close();
}

     客户端代码:

#include "clientwidget.h"
#include "ui_clientwidget.h"
#include<QHostAddress>

ClientWidget::ClientWidget(QWidget *parent) :
    QWidget(parent),
    ui(new Ui::ClientWidget)
{
    ui->setupUi(this);
    tcpSocket = NULL;
    //分配空间,指定父对象
    tcpSocket = new QTcpSocket(this);

    connect(tcpSocket, &QTcpSocket::connected,
            [=](){
              ui -> textEditRead-> setText("成功和服务器建立连接");
            }
            );

    connect(tcpSocket, &QTcpSocket::readyRead,
            [=](){
               //获取对方发送的内容
               QByteArray array = tcpSocket -> readAll();
               //追加到编辑区中
               ui -> textEditRead -> append(array);
            }
            );
}

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

void ClientWidget::on_buttonConnect_clicked()
{
    //获取服务器ip和端口
    QString ip = ui -> lineEditIp -> text();
    qint16 port = ui -> lineEditPort -> text().toInt();

    //主动和服务器建立连接
    tcpSocket -> connectToHost(QHostAddress(ip), port);
}

void ClientWidget::on_buttonSend_clicked()
{
    //获取编辑框内容
    QString str = ui -> textEditWrite -> toPlainText();
    tcpSocket -> write(str.toUtf8().data());
}

void ClientWidget::on_buttonClose_clicked()
{
    //主动和对方断开连接
    tcpSocket -> disconnectFromHost();
    tcpSocket->close();
}

4,Qt中的Udp通信流程

     Udp是无连接的,所以不需要建立连接。Tcp通信相当于现实中的打电话。

     Qt中用到的套接字是QUdpSocket,然后使用bind进行绑定,然后使用readDatagram/writeDatagram进行文字的收发。如果对方给我发数据,套接字自动触发readyRead()。

    UDP传送数据示例:

   服务器端代码:

#include "widget.h"
#include "ui_widget.h"
#include<QHostAddress>

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

    //分配空间,指定父对象
    udpSocket = new QUdpSocket(this);
    //绑定
    udpSocket -> bind(8888);

    setWindowTitle("服务器端口为:8888");

    //当对方成功发送数据过来,自动触发 readyRead()
    connect(udpSocket, &QUdpSocket::readyRead, this, &Widget::dealMsg);
}

void Widget::dealMsg(){
    //读取对方发送的内容
    char buf[1024] = {0};
    QHostAddress cliAddr; //对方地址
    quint16 port; //对方端口
    qint64 len = udpSocket -> readDatagram(buf, sizeof(buf), &cliAddr, &port);
    if(len > 0){
        //格式化[192.68.2.2:8888]aaaa
        QString str = QString("[%1:%2] %3").arg(cliAddr.toString()).arg(port).arg(buf);
        //给编辑区设置内容
        ui -> textEdit -> setText(str);
    }
}

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

void Widget::on_buttonSend_clicked()
{
    //先获取对方的IP和端口
    QString ip = ui ->lineEditip -> text();
    qint16 port = ui -> lineEditPort -> text().toInt();

    //获取编辑区内容
    QString str = ui -> textEdit -> toPlainText();

    //给指定的IP发送数据
    udpSocket -> writeDatagram(str.toUtf8(), QHostAddress(ip), port);
}

5,定时器QTimer的对象可以设置时间间隔,每隔设置的时间间隔,定时器myTimer就会自动触发timeout()信号。

6,Tcp文件服务器客户端

     需求:从服务器端向客户端发送所选择的文件

     原理:

     客户端和服务器端首先建立连接,服务器端选择文件,在服务器端会获取文件的两个信息,大小和名字。先向客户端发送文件信息,客户端接收到信息之后。在客户端,对字符串进行分析,获取文件的大小和名字,并且在本地创建一个同名文件。在服务器端会读文件,发送数据(规则:读多少发多少)。在客户端,对方发多少就读多少,再把收到的内容写到文件里面。

     效果图:

    服务器端代码:

#include "widget.h"
#include "ui_widget.h"
#include <QFileDialog>

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

    //分配空间
    tcpServer = new QTcpServer(this);
    //监听
    tcpServer -> listen(QHostAddress::Any, 8888);
    setWindowTitle("服务器端口为:8888");

    //没有连接好之前设置两个按钮都不能按
    ui -> buttonFile -> setEnabled(false);
    ui -> buttonSend -> setEnabled(false);

    //如果客户端成功和服务器连接
    //tcpServer会自动触发 newConnection()
    connect(tcpServer, &QTcpServer::newConnection,
            [=](){
               //取出建立好连接的套接字
               tcpSocket = tcpServer -> nextPendingConnection();
               //获取对方的ip和端口
               QString ip = tcpSocket -> peerAddress().toString();
               quint16 port = tcpSocket -> peerPort();
               QString str = QString("[%1:%2]成功连接").arg(ip).arg(port);
               ui -> textEdit -> setText(str);//显示到编辑区

               //成功连接文件后才可以按选择文件
               ui -> buttonFile -> setEnabled(true);

               connect(tcpSocket, &QTcpSocket::readyRead,
                       [=](){
                          //取客户端的消息
                          QByteArray buf = tcpSocket -> readAll();
                          if(QString(buf) == "file done"){
                              //文件接收完毕
                              ui -> textEdit -> append("文件发送完毕");
                              file.close();

                              //断开客户端端口
                              tcpSocket -> disconnectFromHost();
                              tcpSocket -> close();
                          }
                       }

                       );
            }
    );
    connect(&timer, &QTimer::timeout,
            [=](){
              //关闭定时器
              timer.stop();
              //发送文件
              sendData();
            }
            );
}

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

void Widget::on_buttonFile_clicked()
{
    QString filePath = QFileDialog::getOpenFileName(this, "open", "../");
    if(false == filePath.isEmpty()){//如果选择的文件路径有效
        fileName.clear();
        fileSize = 0;

        //获取文件信息
        QFileInfo info(filePath);
        fileName = info.fileName(); //获取文件名字
        fileSize = info.size(); //获取文件大小
        sendSize = 0;//已经发送文件的大小

        //只读方式打开文件,指定文件的名字
        file.setFileName(filePath);
        //打开文件
        bool isOk = file.open(QIODevice::ReadOnly);
        if(false == isOk){
            qDebug() << "只读方式打开失败 77";
        }
        //提示打开文件的路径
        ui -> textEdit -> append(filePath);

        ui -> buttonFile -> setEnabled(false);
        ui -> buttonSend -> setEnabled(true);
    }else{
        qDebug() << "选择文件路径出错 62";
    }
}

//发送文件按钮
void Widget::on_buttonSend_clicked()
{
    //先发送文件头信息 文件名##文件大小
    QString head = QString("%1##%2").arg(fileName).arg(fileSize);
    qint64 len = tcpSocket -> write(head.toUtf8());
    if(len > 0){//头部信息发送成功
       //发送真正的文件信息,防止TCP连包文件,需要通过延时定时器延时20ms
       timer.start(20);
    }else{
        qDebug() << "头部信息发送失败 110";
        file.close();
        ui -> buttonFile -> setEnabled(true);
        ui -> buttonSend -> setEnabled(false);
    }
    //在发送文件的信息
}

void Widget::sendData(){
    qint64 len = 0;
    do{
        //每次发送数据的大小
        char buf[4*1024] = {0};
        len = 0;
        //往文件中读数据
        len = file.read(buf, sizeof(buf));
        //发送数据,读多少
        len = tcpSocket -> write(buf, len);
        //发送数据累加
        sendSize += len;
    }while(len > 0);
    //是否发送文件完毕
    if(sendSize == fileSize){
        ui -> textEdit ->append("文件发送完毕");
        file.close();
        //把客户端端口断开
        tcpSocket -> disconnectFromHost();
        tcpSocket -> close();
    }
}

    客户端代码:

#include "clientwidget.h"
#include "ui_clientwidget.h"
#include <QDebug>
#include <QMessageBox>
#include <QHostAddress>

ClientWidget::ClientWidget(QWidget *parent) :
    QWidget(parent),
    ui(new Ui::ClientWidget)
{
    ui->setupUi(this);    
    tcpSocket = new QTcpSocket(this);
    ui -> progressBar -> setValue(0);//当前值
    isStart = true;

    connect(tcpSocket, &QTcpSocket::readyRead,
            [=](){
               //取出接收的内容
               QByteArray buf = tcpSocket -> readAll();
               if(true == isStart){//接收头
                   isStart = false;

                   //初始化
                   //解析文件头部信息buf = "hello##1024"
                   fileName = QString(buf).section("##", 0, 0);
                   fileSize = QString(buf).section("##", 1, 1).toInt();
                   recvSize =0;

                   //打开文件
                   file.setFileName(fileName);
                   bool isOk = file.open(QIODevice::WriteOnly);
                   if(false == isOk){
                       qDebug() << "Write Only error 40";
                   }

                   QString str = QString("接收的文件:[%1:%2kb]").arg(fileName).arg(fileSize/1024);
                   QMessageBox::information(this, "文件信息", str);

                   //设置进度条
                   ui -> progressBar -> setMinimum(0);//最小值
                   ui -> progressBar -> setMaximum(fileSize / 1024);//最大值
                   ui -> progressBar -> setValue(0);//当前值

               }else{//接收文件信息
                   qint64 len = file.write(buf);

                  if(len > 0){
                       recvSize += len;//累计接收大小
                       qDebug() << len;
                   }
                   //更新进度条
                   ui -> progressBar -> setValue(recvSize/1024);

                   if(recvSize == fileSize){
                       //先给服务器发送(接收文件完成的信息)
                       tcpSocket -> write("file done");

                       file.close();
                       QMessageBox::information(this, "完成", "文件接收完成");

                       tcpSocket -> disconnectFromHost();
                       tcpSocket -> close();
                   }
               }
            }
            );
}

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


void ClientWidget::on_buttonConnect_clicked()
{
    //获取服务器的ip和端口
    QString ip = ui -> lineEditIp -> text();
    quint16 port = ui -> lineEditPort -> text().toInt();
    tcpSocket -> connectToHost(QHostAddress(ip), port);
}

 

  • 2
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值