QT网络通信

1 基础概念

1.1 网络通信概念

  • MAC地址(硬件地址)
  • 网络IP地址(比如192.168.0.xxx)
  • 网络端口(实现多路通信,用来给不同应用程序来区分使用,范围(0~65535),1024之前的端口号已经有其他用途)

1.2 交换机和路由器

  • 交换机:端到端转发,基于MAC地址实现不同设备数据转发,速度快
  • 路由器:根据实际数据路线转发,基于网络IP地址实现不同网络之间的数据转发
  • 路由器在网络层,咯尤其可以处理TCP/IP协议,交换机不可以
  • 交换机在中继层,路由器根据IP地址寻址,交换机根据MAC地址寻址
  • 路由器提供防火墙服务,具有虚拟拨号上网功能,交换机不具备

1.3 TCP和UDP

1.TCP(传输控制协议):
面向链接的协议,主要用于大量数据的场合。
一个TCP链接必须要经过三次握手才能建立链接。
三次握手:

第一次握手:建立连接时,客户端发送syn包(seq=j)到服务器,并进入SYN_SENT状态,等待服务器确认;
			SYN:同步序列编号(Synchronize Sequence Numbers)。 
第二次握手:服务器收到syn包,必须确认客户的SYN(ack=j+1),
			同时自己也发送一个SYN包(seq=k),即SYN+ACK包,此时服务器进入SYN_RECV状态。 
第三次握手:客户端收到服务器的SYN+ACK包,向服务器发送确认包ACK(ack=k+1),
			此包发送完毕,客户端和服务器进入ESTABLISHED(TCP连接成功)状态,完成三次握手。 

我们假设A和B是通信的双方。我理解的握手实际上就是通信,发一次信息就是进行一次握手。
第一次握手: A给B打电话说,你可以听到我说话吗?
第二次握手: B收到了A的信息,然后对A说: 我可以听得到你说话啊,你能听得到我说话吗?  
第三次握手: A收到了B的信息,然后说可以的,我要给你发信息啦!
  在三次握手之后,A和B都能确定这么一件事: 我说的话,你能听到; 你说的话,我也能听到。 这样,就可以开始正常通信了。

为什么不是两次握手或者四次握手?

  如果两次,那么B无法确定B的信息A是否能收到,所以如果B先说话,可能后面的A都收不到,会出现问题。
  如果四次,那么就造成了浪费,因为在三次结束之后,就已经可以保证A可以给B发信息,A可以收到B的信息; B可以给A发信息,B可以收到A的信息。

2.UDP(用户数据协议)
无连接协议,主要用于不要求分组顺序,少量数据的传输场合,数据传输效率高,但是容易掉包。

2 网络编程——TCP

QTcpServer——服务端
QTcpSocket——客户端

  • 异步(非阻塞)访问
    通过信号和槽来实现。比如:当我们调用链接函数去链接服务器时,并不是立即链接成功,当链接成功时,会接收一个信号。
  • 同步(阻塞)访问
    通过waitForxxxx()函数来实现阻塞,如果使用此方法来链接,采用多线程,否则,会导致ANR现象,UI线程会被阻塞。
注:
	获取到的ip为::ffff:192.168.0.xxx
	可以使用ip = ip.remove(0,7);保留192.168.0.xxx
客户端实现过程:
	[1]客户向服务器发送链接请求
	[2]发送数据
	[3]建立与服务端读取数据的信号与槽,并且读取服务器数据
服务端实现过程:
	[1]监听客户端
	[2]建立新客户端信号相关的槽链接
	[3]在新客户端信号相关的槽函数里,判断客户端是否向我发来消息,
	   当客户端发送消息时,该客户端会自动发送一个信号readyRead
	[4]在读取消息槽函数中,要对客户端进行判断,到底是谁再给我发送消息。

MyTcpSocket

void MainWindow::on_linkBtn_clicked()
{
    //[1]建立链接
//    quint16 port = ui->ipLineEdit->text().toInt();
//    socket.connectToHost(ui->ipLineEdit->text(),port);
    socket.connectToHost(ui->ipLineEdit->text(),1991);
    ui->ipTextBrowser->append(ui->ipLineEdit->text());

    //[3]建立与服务端读取数据的链接
    connect(&socket,&QTcpSocket::readyRead,
            this,&MainWindow::readData);
}

void MainWindow::on_cleanBtn_clicked()
{
    ui->sendMsgTextEdit->clear();
}

void MainWindow::on_sendBtn_clicked()
{
    //[2]发送消息
    QByteArray msg = ui->sendMsgTextEdit->toPlainText().toUtf8();
    socket.write(msg);

    ui->msgTextBrowser->append("客户端(我):");
    ui->msgTextBrowser->append(msg);
}

void MainWindow::readData()
{
    QString msg = socket.readAll();
    ui->msgTextBrowser->append("服务端:");
    ui->msgTextBrowser->append(msg);
}

MyTcpServer

void MainWindow::on_startBtn_clicked()
{
    //[1]监听
//    quint16 port = ui->portLineEdit->text().toInt();
//    server.listen(QHostAddress::Any,port);
    server.listen(QHostAddress::Any,1991);
    //[2]建立新客户端信号相关的槽函数
    connect(&server,&QTcpServer::newConnection,
            this,&MainWindow::newClient);
}

void MainWindow::on_cleanBtn_clicked()
{
    ui->sendMsgTextEdit->clear();
}

void MainWindow::on_sendBtn_clicked()
{
    QByteArray msg = ui->sendMsgTextEdit->toPlainText().toUtf8();
    socket->write(msg);

    ui->msgTextBrowser->append("服务端(我):");
    ui->msgTextBrowser->append(msg);
}

void MainWindow::newClient()
{
    //[3]判断是谁在给我发送数据
    socket = server.nextPendingConnection();

    connect(socket,&QTcpSocket::readyRead,
            this,&MainWindow::readData);
}

void MainWindow::readData()
{
	//[4]
    QTcpSocket *msocket = dynamic_cast<QTcpSocket*>(sender());

    QString msg = msocket->readAll();

    ui->msgTextBrowser->append("客户端:");
    ui->msgTextBrowser->append(msg);

    QString ip = msocket->peerAddress().toString();//::ffff:192.168.0.xxx
    ip = ip.remove(0,7);//192.168.0.xxx

    ui->ipTextBrowser->append(ip);
}

在这里插入图片描述

3 HTTP协议

[1]QNetworkAccessManager  管理类
	 Allows the application to send network requests and receive replies
[2]QNetworkReply   回应类
	Contains the data and headers for a request sent with QNetworkAccessManager
[3]QNetworkRequest   请求类
	Holds a request to be sent with QNetworkAccessManager
[4][signal] void QNetworkAccessManager::finished(QNetworkReply *reply)
	当发送请求完成后会发送finshed信号
[5]QNetworkReply *QNetworkAccessManager::get(const QNetworkRequest &request)
	Posts a request to obtain the contents of the target request 
and returns a new QNetworkReply object opened for reading which emits the readyRead() signal whenever new data arrives.

实例:获取网络上的图片,并打印出来。

	//建立finshed相关的槽函数
	connect(&manger,&QNetworkAccessManager::finished,
            this,&MainWindow::readData);

void MainWindow::on_sendBtn_clicked()
{
    //发起网络请求
    QUrl url(ui->lineEdit->text());
    QNetworkRequest request(url);
    manger.get(request);
}

void MainWindow::readData(QNetworkReply *reply)
{
    //在槽函数中读取数据
    QByteArray arr = reply->readAll();

    QPixmap map;
    map.loadFromData(arr);
    map = map.scaled(ui->label->size());
    ui->label->setPixmap(map);
}

4 UDP通信

  • 每次写一个数据包,会发送bytesWritten(),有数据到达时,会发送readyRead()信号,不发送当前的信号,当前的数据包就会丢失,下一个数据包会发送自己的readyRead()。
  • writeDatagram() 和readDatagram() / receiveDatagram()传输数据。
  • UDP可以进行一对一的通信,并且它不需要建立链接,还可以进行组播
[1]socket端发送数据包,发送错误返回-1
	qint64 QUdpSocket::writeDatagram(const char *data, qint64 size,
									 const QHostAddress &address, quint16 port)
	qint64 QUdpSocket::writeDatagram(const QNetworkDatagram &datagram)
	qint64 QUdpSocket::writeDatagram(const QByteArray &datagram, 
									const QHostAddress &host, quint16 port)
[2]	接收端绑定发送端
	bool QAbstractSocket::bind(const QHostAddress &address, 
							   quint16 port, QAbstractSocket::BindMode mode)
	bool QAbstractSocket::bind(quint16 port, QAbstractSocket::BindMode mode)
[3]server端接收数据包
	qint64 QUdpSocket::readDatagram(char *data, qint64 maxSize, 
									QHostAddress *address = nullptr, quint16 *port = nullptr)

UDPSocket

void MainWindow::on_sendBtn_clicked()
{
    QString data = ui->textEdit->toPlainText();

    qint64 ret =msocket.writeDatagram(data.toUtf8(),QHostAddress(ui->ipLineEdit->text()),ui->portLineEdit->text().toUShort());
    if(ret != -1)
    {
        QMessageBox::information(this,"信息提示","发送成功");
    }    
}

UDPServer

	//[2]建立readyRead信号的槽函数
	 connect(&msocket,&QUdpSocket::readyRead,
            this,&MainWindow::readData);

void MainWindow::on_bandBtn_clicked()
{
	//[1]绑定发送端
    msocket.bind(QHostAddress::AnyIPv4,ui->portLineEdit->text().toUShort());
}

void MainWindow::readData()
{
    char buffer[1024];
    QHostAddress address;
    quint16 port;
    QByteArray datagarm;
    while (msocket.hasPendingDatagrams())
    {
    	//[3]读取发送端的数据
         datagarm.resize(msocket.pendingDatagramSize());
         msocket.readDatagram(datagarm.data(),datagarm.size(),&address,&port);
    }
    ui->textBrowser->append(address.toString() + ":" + datagarm);

//    msocket.readDatagram(buffer,1024,&address,&port);
//    ui->textBrowser->append(address.toString() + ":" + buffer);
}

在这里插入图片描述

5 JSON文件

5.1 获取JSON文件

[1]基础
	链接https://www.sojson.com/来检验json文件是否正确
	{}-----------json对象
	[]-----------json数组
[2]解析JSON文件步骤
	1.将服务器返回的数据转换成一个QJsonDOcument
	2.根据文件的数据格式,判断json数据最开始是json数组还是json对象
		QJsonObject object = doc.object();
		QJsonArray arr = doc.array();
	3.根据文件的数据格式,再根据key值来获取值,并且判断值的类型,用to系列函数来进行转换
	    QJsonObject object = fileDoc.object();
        QJsonArray resultArr = object.value("result").toArray();
        QJsonObject resultObj = resultArr.at(0).toObject();
     	QString runtime = resultObj.value("runtime").toString();
    4.将获取的值给ui组件,如果是显示图片,则利用HHTP协议中的管理类、请求类以及回复类。
	//http管理者finshed相关的槽函数
	connect(&manger,&QNetworkAccessManager::finished,
            this,&MainWindow::readData);

void MainWindow::on_getFilmInfoBtn_clicked()
{
    QFile file(":/film.json");
    bool isOk = file.open(QIODevice::ReadOnly);

    if(isOk)
    {
        QByteArray fileArr = file.readAll();
        //将QByteArray转化成QJsonDocument
        QJsonDocument fileDoc = QJsonDocument::fromJson(fileArr);

        QJsonObject object = fileDoc.object();
        QJsonArray resultArr = object.value("result").toArray();
        QJsonObject resultObj = resultArr.at(0).toObject();

        QString runtime = resultObj.value("runtime").toString();
        QString language = resultObj.value("language").toString();
        QString film_locations = resultObj.value("film_locations").toString();
        QString year = resultObj.value("year").toString();
        QString poster = resultObj.value("poster").toString();

        ui->languageLabel->setText(language);
        ui->yearLabel->setText(year);
        ui->filmLocationsLabel->setText(film_locations);
        ui->runtimeLabel->setText(runtime);


        qDebug() << poster;
        QUrl url(poster);
        QNetworkRequest request(url);
        manger.get(request);

    }
    else
    {
        QMessageBox::information(this,"文件打开","打开文件失败");
    }
}

void MainWindow::readData(QNetworkReply *reply)
{
    QByteArray arr = reply->readAll();
    QPixmap map;
    map.loadFromData(arr);
    map = map.scaled(ui->postLabel->size());
    ui->postLabel->setPixmap(map);
}

在这里插入图片描述

5.2 创建JSON文件

解析json文件从外向内逐步解析,创建json文件从内向外逐步创建。
创建完成之后应该在JSON官网进行检验JSON文件是否正确。

[1]QJsonArray和QJsonObject插入数据
	void QJsonArray::insert(int i, const QJsonValue &value)
	QJsonArray::iterator QJsonArray::insert(QJsonArray::iterator before, const QJsonValue &value)

	QJsonObject::iterator QJsonObject::insert(const QString &key, const QJsonValue &value)
[2]QJsonDocument插入数组或者对象
	void QJsonDocument::setArray(const QJsonArray &array)
	void QJsonDocument::setObject(const QJsonObject &object)
[3]将QJsonDocument转化成QByteArray
	QByteArray QJsonDocument::toJson() const
	QByteArray QJsonDocument::toJson(QJsonDocument::JsonFormat format) const
	将QJsonDocument转换成UTF-8编码的数据
	两种格式如下:
	1.QJsonDocument::Indented	0
	2.QJsonDocument::Compact    1
	第一种的形式:
      {
          "Array": [
              true,
              999,
              "string"
          ],
          "Key": "Value",
          "null": null
      }
	第二种的形式:
	{"Array":[true,999,"string"],"Key":"Value","null":null}

创建一个员工staff类的json文件。

思路:
	第一步:创建Staff类。
	第二步:丛内向外逐步创建。
	第三步:将QJsonDocument文件转换为QByteArray。
	第四步:将内容写入文件中。
	Staff *s1 = new Staff;
    s1->setName("张三");
    s1->setAge("28");
    s1->setSex("男");
    s1->setSalry("10000");
    .......................

	QJsonObject s1Obj;
    s1Obj.insert("name",s1->getName());
    s1Obj.insert("sex",s1->getSex());
    s1Obj.insert("age",s1->getAge());
    s1Obj.insert("salry",s1->getSalry());
    .......................

	QJsonArray staffArr;
    staffArr.insert(0,s1Obj);
    staffArr.insert(1,s2Obj);
    staffArr.insert(2,s3Obj);

    QJsonObject staffObj;
    staffObj.insert("staff",staffArr);

    QJsonDocument doc;
    doc.setObject(staffObj);

    QByteArray data = doc.toJson();

    QFile file("./staff.json");
    file.open(QIODevice::WriteOnly);

    file.write(data);

    file.close();

在这里插入图片描述

  • 3
    点赞
  • 15
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值