QT--实现TCP通信

提示:本文为学习记录,若有错误,请联系作者,谦虚受教。


前言

生活总是来来往往,千万别等来日方长。


一、TCP协议

传输控制协议(TCP,Transmission Control Protocol)是一种面向连接的、可靠的、基于字节流的传输层通信协议。

优点:
(1)基于流的方式;
(2)面向连接;
(3)可靠通信方式;
(4)在网络状况不佳的时候尽量降低系统由于重传带来的带宽开销;
(5)通信连接维护是面向通信的两个端点的,而不考虑中间网段和节点。

详细可见:《TCP网址》

为满足TCP协议的这些特点,TCP协议做了如下的规定:
①数据分片:在发送端对用户数据进行分片,在接收端进行重组,由TCP确定分片的大小并控制分片和重组;
②到达确认:接收端接收到分片数据时,根据分片数据序号向发送端发送一个确认;
③超时重发:发送方在发送分片时启动超时定时器,如果在定时器超时之后没有收到相应的确认,重发分片;
④滑动窗口:TCP连接每一方的接收缓冲空间大小都固定,接收端只允许另一端发送接收端缓冲区所能接纳的数据,TCP在滑动窗口的基础上提供流量控制,防止较快主机致使较慢主机的缓冲区溢出;
⑤失序处理:作为IP数据报来传输的TCP分片到达时可能会失序,TCP将对收到的数据进行重新排序,将收到的数据以正确的顺序交给应用层;
⑥重复处理:作为IP数据报来传输的TCP分片会发生重复,TCP的接收端必须丢弃重复的数据;
⑦数据校验:TCP将保持它首部和数据的检验和,这是一个端到端的检验和,目的是检测数据在传输过程中的任何变化。如果收到分片的检验和有差错,TCP将丢弃这个分片,并不确认收到此报文段导致对端超时并重发。

《传说中的TCP的三次握手》
在这里插入图片描述
上位机作为客户端,主动连接服务器,发送数据,接收数据。
简单理解:
A发送:我要发信息给你啦,你准备好了吗?
B发送:我准备好啦,你发吧!
A发送:好的,那我发送啦。
然后A就开始发送。
这就是理解层面上的三次握手。
以下会有相关QT的简单例子。

二、TCP通信步骤

1.TCP服务端的实现流程

重点:
A. 调用listen监听端口

B. 连接信号newConnection,在槽函数里调用nextPendingConnection获取连接进来的socket。

扫描二维码关注公众号,回复: 15681754 查看本文章

(1).h引入头文件

#include <QTcpServer>
#include <QTcpSocket>

(2)创建QTcpServer的对象用作监听套接字;

QTcpServer *m_s;
QTcpSocket *m_tcp;

在构造函数中new

//放在构造函数中
m_s = new QTcpServer(this);

(3)使用listen()方法监听网卡的ip和端口

void MainWindow::on_bt_listen_clicked()
{
    
    
    unsigned short port = ui->lineEdit->text().toUShort();
    bool ret =m_s->listen(QHostAddress::Any, port);
    if(ret)
    {
    
    
        ui->textEdit->append("启动服务器成功");
    }
    else {
    
    
        ui->textEdit->append("error");
    }
    qDebug()<<ret<<m_s->errorString();
}

(4)发送信息

void MainWindow::on_bt_sendmsg_clicked()
{
    
    
    QString msg = ui->text_send->toPlainText();
    m_tcp->write(msg.toUtf8());
    ui->textEdit->append("服务器说:"+msg);
}

(5)关闭连接

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

(6)触发上面的槽函数,在构造函数用connect()
当有数据传送的时候,客户端QTcpSocket会触发readyRead()信号,然后readAll()读取数据并显示出来。

connect(m_s,&QTcpServer::newConnection,this,[=]()
    {
    
    
        m_tcp = m_s->nextPendingConnection();
        ui->textEdit->append("成功和客户端建立了连接!");
        connect(m_tcp,&QTcpSocket::readyRead,this,[=]()
        {
    
    
            QByteArray data = m_tcp->readAll();
            ui->textEdit->append("客户端说:"+data);
        });
        connect(m_tcp,&QTcpSocket::disconnected,this,[=]()
        {
    
    
            ui->textEdit->append("客户端断开了连接!");
            m_tcp->close();
            m_tcp->deleteLater();
        });

    });

2.TCP客户端的实现流程

重点:
A. 调用connectToHost连接服务器

B. 调用waitForConnected判断是否连接成功

C. 连接信号readyRead槽函数,异步读取数据

D. 调用waitForReadyRead,阻塞读取数据

E.调用disconnected断开连接

F.调用state() == QAbstractSocket::UnconnectedState和waitForDisconnected判断是否断开连接

(1).h引入头文件

#include <QTcpSocket>

(2)创建QTcpSocket套接字

QTcpSocket *tcpsocket;//通信套接字

构造函数中new

tcpsocket = new QTcpSocket(this);

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

void MainWindow::on_bt_lianjie_clicked()
{
    
      
    qint16 port = ui->tcp_com->text().toInt();
    //unsigned short ip = ui->ip->text().toUShort();
    QString ip = ui->tcp_add->text();
    tcpsocket->connectToHost(QHostAddress(ip),port);
    if(tcpsocket->waitForConnected(3000))
    {
    
    
        ui->textdisplay->append("连接服务器成功!");
    }
    else
    {
    
    
        ui->textdisplay->append("连接失败,请检查IP地址和端口!");
    }
}

(4)发送信息write()

void MainWindow::on_pushButton_clicked()
{
    
    
    QString msg = ui->text_send->toPlainText();
    ui->textdisplay->append("客户端说:"+msg);
    //获取编辑框内容
    QString str = ui->text_send->toPlainText();
    //发送数据
    tcpsocket->write(str.toUtf8().data());
    ui->textdisplay->append("客户端发送:"+str);
}

(5)断开连接

void MainWindow::on_bt_duankai_clicked()
{
    
    
    //主动和对方断开连接
    tcpsocket->disconnectFromHost();
    tcpsocket->close();
    //断开成功
    if (tcpsocket->state() == QAbstractSocket::UnconnectedState || tcpsocket->waitForDisconnected(1000))
    {
    
    
        ui->textdisplay->append("连接已断开!");
    }
    else
    {
    
    
        ui->textdisplay->append("无法断开与服务器的连接!");
    }
}

(6)触发上面的槽函数,在构造函数中使用connect()
当客户端的嵌套字socket接收缓冲区有新数据到来时,会发出readRead()信号

connect(tcpsocket,&QTcpSocket::connected,this,[=]()
    {
    
    
        ui->textdisplay->append("成功和服务器建立连接!");
    });
    connect(tcpsocket,&QTcpSocket::readyRead,this,[=]()
    {
    
    
        QByteArray data = tcpsocket->readAll();
        ui->textdisplay->append("服务器说:"+data);
    });
    connect(tcpsocket,&QTcpSocket::disconnected,this,[=]()
    {
    
    
        //m_tcp->close();
        //m_tcp->deleteLater();
        ui->textdisplay->append("已和服务器断开连接!");
    });


在这里插入图片描述

总结

提示:善于总结,多进一步。

猜你喜欢

转载自blog.csdn.net/m0_51988927/article/details/124941676