QT网络编程——TCP

TCP

TCP(Transmission Control Protocol,传输控制协议)是一个用于数据传输的低层的网络协议,多个互联网协议(包括 HTTP 和 FTP)都是基于 TCP 协议的。它是可靠的、面向流、面向连接的传输协议,特别适合连续数据的传输。

TCP 通信必须先建立连接,分为客户端和服务端,也就是所谓的 C/S(Client/Server)模型,如图:

在这里插入图片描述

客户端

客户端使用 QTcpSocket 与 TCP 服务器建立连接并通信。

QTcpSocket 类除了构造函数和析构函数,其他函数都是从 QAbstractSocket 继承或重定义的。QAbstractSocket 用于 TCP 通信的主要接口函数如图:

在这里插入图片描述

客户端的 QTcpSocket 实例首先通过 connectToHost() 尝试连接到服务器,需要指定服务器的 IP 地址和端口号。connectToHost() 是异步方式连接服务器,不会阻塞程序运行,连接后发射 connected() 信号。

widget.h

#ifndef WIDGET_H
#define WIDGET_H

#include <QWidget>
#include <QTcpSocket>

QT_BEGIN_NAMESPACE
namespace Ui { class Widget; }
QT_END_NAMESPACE

class Widget : public QWidget
{
    Q_OBJECT

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

private slots:
    void onConnected();
    void onDisconnected();
    void onReadyRead();

    void on_btnConnect_clicked();
    void on_btnSendMsg_clicked();

private:
    Ui::Widget *ui;

    QTcpSocket *m_client{};
};
#endif // WIDGET_H

widget.cpp

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

#include <QHostAddress>

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

    setWindowTitle("客户端");

    m_client = new QTcpSocket(this);
    connect(m_client, &QTcpSocket::connected, this, &Widget::onConnected);
    connect(m_client, &QTcpSocket::disconnected, this, &Widget::onDisconnected);
    connect(m_client, &QTcpSocket::readyRead, this, &Widget::onReadyRead);
}

Widget::~Widget()
{
    m_client->abort();
    delete ui;
}

void Widget::onConnected()
{
    ui->labConnectState->setText("Connecting");
}

void Widget::onDisconnected()
{
    ui->labConnectState->setText("Disconnect");
}

void Widget::onReadyRead()
{
    ui->textEditRecv->setText(m_client->readAll());
}

void Widget::on_btnConnect_clicked()
{
    QString str = ui->btnConnect->text();
    if (str == "Connect") {
        ui->btnConnect->setText("Disconnect");

        if (m_client->state() == QAbstractSocket::SocketState::ConnectingState) {
            m_client->close();
        }

        QString address = ui->leAddress->text();
        QString port = ui->lePort->text();
        m_client->connectToHost(QHostAddress(address), port.toInt());
    } else {
        ui->btnConnect->setText("Connect");
        m_client->close();
    }
}

void Widget::on_btnSendMsg_clicked()
{
    QString msg = ui->textEditSend->toPlainText();
    m_client->write(msg.toLocal8Bit());
}

界面如图:

在这里插入图片描述

服务端

服务端程序必须使用 QTcpServer 进行端口监听,建立服务器。QTcpSocket 用于建立连接后使用套接字。

QTcpServer 是从 QObject 继承的类,它主要用于服务器建立网络监听,创建网络 Socket 连接。QTcpServer 类的主要接口函数如下所示:

在这里插入图片描述

服务端可以使用 QTcpServer::listen() 指定监听的 IP 地址和端口,一般一个服务程序只监听某个端口的网络连接。

当有新的客户端接入时,QTcpServer 内部的 incomingConnection() 函数会创建一个与客户端连接的 QTcpSocket 对象,然后发出信号 newConnection()。可以使用 nextPendingConnection() 接受客户端的连接,然后使用 QTcpSocket 与客户端通信。所以在客户端与服务端建立连接后,具体的数据通信是通过 QTcpSocket 完成的。

widget.h

#ifndef WIDGET_H
#define WIDGET_H

#include <QWidget>
#include <QTcpServer>
#include <QTcpSocket>

QT_BEGIN_NAMESPACE
namespace Ui { class Widget; }
QT_END_NAMESPACE

class Widget : public QWidget
{
    Q_OBJECT

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

private slots:
    void onNewConnection();
    void onReadyRead();
    void onClientDisconnected();

    void on_btnClose_clicked();
    void on_btnSendMsg_clicked();

private:
    Ui::Widget *ui;

    QTcpServer *m_server{};
    QTcpSocket *m_socket{};
};
#endif // WIDGET_H

widget.cpp

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

#include <QHostAddress>
#include <QHostInfo>

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

    setWindowTitle("服务端");

    // 获取本地 ip
    QString ip;
    QHostInfo info = QHostInfo::fromName(QHostInfo::localHostName());
    for (auto &address : info.addresses()) {
        if (address != QHostAddress::LocalHost && address.toIPv4Address()) {
            ip = address.toString();
            break;
        }
    }
    ui->leAddress->setText(ip);
    ui->lePort->setText("1234");

    m_server = new QTcpServer(this);
    m_server->listen(QHostAddress::Any, 1234);
    connect(m_server, &QTcpServer::newConnection, this, &Widget::onNewConnection);
}

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

void Widget::onNewConnection()
{
    m_socket = m_server->nextPendingConnection();
    // 告知客户端连接成功
    m_socket->write("Congratulations on successfully connecting !");
    connect(m_socket, &QTcpSocket::readyRead, this, &Widget::onReadyRead);
    connect(m_socket, &QTcpSocket::disconnected, this, &Widget::onClientDisconnected);

    // 更新连接状态显示
    ui->labConnectState->setText(QString("new connection %1 %2")
                                     .arg(m_socket->peerAddress().toString())
                                     .arg(m_socket->peerPort()));
}

void Widget::onReadyRead()
{
    ui->textEditRecv->setText(m_socket->readAll());
}

void Widget::onClientDisconnected()
{
    ui->labConnectState->setText(QString("%1 %2 disconnected")
                                     .arg(m_socket->peerAddress().toString())
                                     .arg(m_socket->peerPort()));
}

void Widget::on_btnClose_clicked()
{
    m_server->close();
}

void Widget::on_btnSendMsg_clicked()
{
    if (!m_socket)
        return;

    QString msg = ui->textEditSend->toPlainText();
    m_socket->write(msg.toLocal8Bit());
}

界面如图:

在这里插入图片描述

总结

本文只简单演示了 TCP 通信的基本原理。服务端只允许一个客户端连接。然而,一般的 TCP 服务器程序允许多个客户端接入,博主将在其他文章中进行讲解。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值