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 服务器程序允许多个客户端接入,博主将在其他文章中进行讲解。










