QT(c++)开发自学笔记:2.TCP/IP

一、下载网络调试助手

下载连接:通过网盘分享的文件:网络调试助手.exe

链接: https://pan.baidu.com/s/1A-aQ9oLb3aGdxDT8bl6qjw?pwd=nf1z 提取码: nf1z

就一个.exe,点两次打开两个,两个配置:

设置一个调试助手为 TCP 服务器

  1. 打开第一个网络调试助手,选择 TCP Server 模式。
  2. 配置服务器参数
    • 本地 IP 地址 :选择本机 IP(通常为 127.0.0.1 用于本地测试,或局域网 IP 如 192.168.x.x)。
    • 监听端口 :设置一个端口号(如 8888),确保该端口未被其他程序占用。
    • 启动服务器:点击"启动"或"监听"按钮,调试助手进入监听状态,等待客户端连接。
  3. 记录服务器信息:记下服务器的 IP 地址和端口号,用于客户端配置。

设置另一个调试助手为 TCP 客户端

  1. 打开第二个网络调试助手,选择 TCP Client 模式。
  2. 配置客户端参数
    • 远程主机 IP :输入服务器的 IP 地址(与服务器端一致,如 127.0.0.1:8888 或局域网 IP)。
    • 远程端口 :输入服务器监听的端口号(如 8888)。
    • 连接服务器:点击"连接"按钮,客户端会尝试连接到服务器。
  3. 确认连接状态:连接成功后,服务器端会显示客户端已连接。

然后互相发个什么能接收就行

二、PyQt实现

再用PyQt实现并验证功能,充当TCP Client

PyQt代码如下:

python 复制代码
import sys
import socket
import threading
from PyQt5.QtWidgets import QApplication, QMainWindow, QWidget, QVBoxLayout, QHBoxLayout, QLineEdit, QPushButton, QTextEdit, QCheckBox
from PyQt5.QtCore import pyqtSignal, QObject

class Communicate(QObject):
    # 自定义信号,用于线程和主界面通信
    data_received = pyqtSignal(str)
    status_updated = pyqtSignal(str)

class TCPClientWindow(QMainWindow):
    def __init__(self):
        super().__init__()
        self.setWindowTitle("PyQt TCP Client")
        self.resize(600, 400)

        # 初始化通信对象
        self.comm = Communicate()
        self.comm.data_received.connect(self.update_display)
        self.comm.status_updated.connect(self.update_status)

        # 初始化 socket
        self.client_socket = None
        self.running = False

        # 创建界面
        self.init_ui()

    def init_ui(self):
        # 主布局
        main_widget = QWidget()
        self.setCentralWidget(main_widget)
        layout = QVBoxLayout(main_widget)

        # IP 和端口输入
        ip_port_layout = QHBoxLayout()
        self.ip_input = QLineEdit("127.0.0.1")
        self.port_input = QLineEdit("8888")
        ip_port_layout.addWidget(QLineEdit("IP:"))
        ip_port_layout.addWidget(self.ip_input)
        ip_port_layout.addWidget(QLineEdit("Port:"))
        ip_port_layout.addWidget(self.port_input)
        layout.addLayout(ip_port_layout)

        # 连接、断开、清空按钮
        button_layout = QHBoxLayout()
        self.connect_btn = QPushButton("Connect")
        self.disconnect_btn = QPushButton("Disconnect")
        self.clear_btn = QPushButton("Clear Log")
        self.connect_btn.clicked.connect(self.connect_to_server)
        self.disconnect_btn.clicked.connect(self.disconnect)
        self.clear_btn.clicked.connect(self.clear_log)
        self.disconnect_btn.setEnabled(False)
        button_layout.addWidget(self.connect_btn)
        button_layout.addWidget(self.disconnect_btn)
        button_layout.addWidget(self.clear_btn)
        layout.addLayout(button_layout)

        # 接收数据区域
        self.display = QTextEdit()
        self.display.setReadOnly(True)
        layout.addWidget(self.display)

        # 发送数据区域
        send_layout = QHBoxLayout()
        self.send_input = QLineEdit()
        self.send_btn = QPushButton("Send")
        self.hex_check = QCheckBox("Hex Mode")
        self.send_btn.clicked.connect(self.send_data)
        send_layout.addWidget(self.send_input)
        send_layout.addWidget(self.send_btn)
        send_layout.addWidget(self.hex_check)
        layout.addLayout(send_layout)

    def connect_to_server(self):
        ip = self.ip_input.text()
        try:
            port = int(self.port_input.text())
        except ValueError:
            self.comm.status_updated.emit("Invalid port number!")
            return

        try:
            self.client_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
            self.client_socket.connect((ip, port))
            self.running = True
            self.comm.status_updated.emit(f"Connected to {ip}:{port}")
            self.connect_btn.setEnabled(False)
            self.disconnect_btn.setEnabled(True)

            # 启动接收线程
            threading.Thread(target=self.receive_data, daemon=True).start()
        except Exception as e:
            self.comm.status_updated.emit(f"Connection failed: {str(e)}")

    def disconnect(self):
        self.running = False
        if self.client_socket:
            self.client_socket.close()
            self.client_socket = None
        self.comm.status_updated.emit("Disconnected")
        self.connect_btn.setEnabled(True)
        self.disconnect_btn.setEnabled(False)

    def receive_data(self):
        while self.running:
            try:
                data = self.client_socket.recv(1024)
                if not data:
                    self.comm.status_updated.emit("Server closed connection")
                    self.disconnect()
                    break
                if self.hex_check.isChecked():
                    # 十六进制显示
                    data_str = data.hex()
                else:
                    # 文本显示(假设 UTF-8 编码)
                    data_str = data.decode('utf-8', errors='ignore')
                self.comm.data_received.emit(data_str)
            except Exception as e:
                if self.running:
                    self.comm.status_updated.emit(f"Receive error: {str(e)}")
                    self.disconnect()
                break

    def send_data(self):
        if not self.client_socket or not self.running:
            self.comm.status_updated.emit("Not connected to server!")
            return

        data = self.send_input.text()
        try:
            if self.hex_check.isChecked():
                # 发送十六进制数据
                data_bytes = bytes.fromhex(data)
            else:
                # 发送文本数据
                data_bytes = data.encode('utf-8')
            self.client_socket.send(data_bytes)
            self.comm.data_received.emit(f"Sent: {data}")
        except Exception as e:
            self.comm.status_updated.emit(f"Send error: {str(e)}")

    def update_display(self, data):
        self.display.append(data)

    def update_status(self, status):
        self.display.append(f"[Status] {status}")

    def clear_log(self):
        self.display.clear()

    def closeEvent(self, event):
        self.disconnect()
        event.accept()

if __name__ == '__main__':
    app = QApplication(sys.argv)
    window = TCPClientWindow()
    window.show()
    sys.exit(app.exec_())

三、C++实现(进入正题)

因为毕竟是学习C++,下面就一步步的实现,实现网络调试助手与C++版的QT上位机通讯。

main.cpp代码如下:

cpp 复制代码
#include "dialog.h"
#include <QApplication>

int main(int argc, char *argv[])
{
    QApplication a(argc, argv);
    Dialog w;
    w.show();
    return a.exec();
}

dialog.cpp如下:

cpp 复制代码
#include "dialog.h"
#include "ui_dialog.h"
#include <QMessageBox>
#include <QDebug>

Dialog::Dialog(QWidget *parent)
    : QDialog(parent)
    , ui(new Ui::Dialog)
    , tcpSocket(nullptr)
{
    ui->setupUi(this);
    initUI();
    initSocket();
}

Dialog::~Dialog()
{
    if(tcpSocket) {
        tcpSocket->disconnectFromHost();
        delete tcpSocket;
    }
    delete ui;
}

void Dialog::initUI()
{
    // 设置界面默认状态
    ui->connectBtn->setText("连接");
    ui->sendBtn->setEnabled(false);
    ui->ipEdit->setText("127.0.0.1");
    ui->portEdit->setText("8080");
    ui->receiveText->setReadOnly(true);
}

void Dialog::initSocket()
{
    tcpSocket = new QTcpSocket(this);

    // 连接信号槽
    connect(tcpSocket, &QTcpSocket::connected,
            this, &Dialog::on_socket_connected);
    connect(tcpSocket, &QTcpSocket::disconnected,
            this, &Dialog::on_socket_disconnected);
    connect(tcpSocket, &QTcpSocket::readyRead,
            this, &Dialog::on_socket_readyRead);
    // 修改为使用 errorOccurred 信号
    connect(tcpSocket, &QTcpSocket::errorOccurred,
            this, &Dialog::on_socket_error);
}

void Dialog::on_connectBtn_clicked()
{
    if(!tcpSocket->isOpen()) {
        // 连接服务器
        QString ip = ui->ipEdit->text();
        quint16 port = ui->portEdit->text().toUInt();

        tcpSocket->connectToHost(QHostAddress(ip), port);
        ui->connectBtn->setText("连接中...");
        ui->connectBtn->setEnabled(false);
    } else {
        // 断开连接
        tcpSocket->disconnectFromHost();
        ui->connectBtn->setText("断开中...");
    }
}

void Dialog::on_sendBtn_clicked()
{
    if(tcpSocket && tcpSocket->state() == QAbstractSocket::ConnectedState) {
        QString message = ui->sendText->toPlainText();
        if(!message.isEmpty()) {
            tcpSocket->write(message.toUtf8());
            tcpSocket->flush(); // 立即发送

            // 添加到发送记录
            ui->receiveText->append("[发送] " + message);
            ui->sendText->clear();
        }
    } else {
        QMessageBox::warning(this, "警告", "请先连接服务器!");
    }
}

void Dialog::on_socket_connected()
{
    ui->connectBtn->setText("断开");
    ui->connectBtn->setEnabled(true);
    ui->sendBtn->setEnabled(true);
    ui->statusLabel->setText("状态: 已连接");
    ui->receiveText->append("[系统] 连接成功!");

    qDebug() << "TCP连接成功";
}

void Dialog::on_socket_disconnected()
{
    ui->connectBtn->setText("连接");
    ui->connectBtn->setEnabled(true);
    ui->sendBtn->setEnabled(false);
    ui->statusLabel->setText("状态: 已断开");
    ui->receiveText->append("[系统] 连接已断开");

    qDebug() << "TCP连接断开";
}

void Dialog::on_socket_readyRead()
{
    QByteArray data = tcpSocket->readAll();
    QString message = QString::fromUtf8(data);
    ui->receiveText->append("[接收] " + message);

    qDebug() << "接收数据:" << message;
}

void Dialog::on_socket_error(QAbstractSocket::SocketError socketError)
{
    QString errorStr;
    switch(socketError) {
    case QAbstractSocket::RemoteHostClosedError:
        errorStr = "服务器关闭连接";
        break;
    case QAbstractSocket::HostNotFoundError:
        errorStr = "服务器地址错误";
        break;
    case QAbstractSocket::ConnectionRefusedError:
        errorStr = "连接被拒绝";
        break;
    default:
        errorStr = tcpSocket->errorString();
    }

    ui->connectBtn->setText("连接");
    ui->connectBtn->setEnabled(true);
    ui->sendBtn->setEnabled(false);
    ui->statusLabel->setText("状态: 连接失败");
    ui->receiveText->append("[错误] " + errorStr);

    QMessageBox::critical(this, "连接错误", errorStr);
    qDebug() << "TCP错误:" << errorStr;
}

dialog.h代码如下:

cpp 复制代码
#ifndef DIALOG_H
#define DIALOG_H

#include <QDialog>
#include <QTcpSocket>
#include <QHostAddress>

QT_BEGIN_NAMESPACE
namespace Ui { class Dialog; }
QT_END_NAMESPACE

class Dialog : public QDialog
{
    Q_OBJECT

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

private slots:
    void on_connectBtn_clicked();
    void on_sendBtn_clicked();
    void on_socket_connected();
    void on_socket_disconnected();
    void on_socket_readyRead();
    void on_socket_error(QAbstractSocket::SocketError socketError);

private:
    Ui::Dialog *ui;
    QTcpSocket *tcpSocket;
    void initUI();  // 初始化界面
    void initSocket(); // 初始化Socket
};

#endif // DIALOG_H

CMakeLists.txt代码如下:

cpp 复制代码
cmake_minimum_required(VERSION 3.16)

project(TCP_IP_Test VERSION 0.1 LANGUAGES CXX)

set(CMAKE_AUTOUIC ON)
set(CMAKE_AUTOMOC ON)
set(CMAKE_AUTORCC ON)

set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_STANDARD_REQUIRED ON)

# 添加Network组件到find_package中
find_package(QT NAMES Qt6 Qt5 REQUIRED COMPONENTS Widgets Network LinguistTools)
find_package(Qt${QT_VERSION_MAJOR} REQUIRED COMPONENTS Widgets Network LinguistTools)

set(TS_FILES TCP_IP_Test_zh_CN.ts)

set(PROJECT_SOURCES
        main.cpp
        dialog.cpp
        dialog.h
        dialog.ui
        ${TS_FILES}
)

if(${QT_VERSION_MAJOR} GREATER_EQUAL 6)
    qt_add_executable(TCP_IP_Test
        MANUAL_FINALIZATION
        ${PROJECT_SOURCES}
    )
    qt_create_translation(QM_FILES ${CMAKE_SOURCE_DIR} ${TS_FILES})
else()
    if(ANDROID)
        add_library(TCP_IP_Test SHARED
            ${PROJECT_SOURCES}
        )
    else()
        add_executable(TCP_IP_Test
            ${PROJECT_SOURCES}
        )
    endif()
    qt5_create_translation(QM_FILES ${CMAKE_SOURCE_DIR} ${TS_FILES})
endif()

# 添加Network到target_link_libraries
target_link_libraries(TCP_IP_Test PRIVATE
    Qt${QT_VERSION_MAJOR}::Widgets
    Qt${QT_VERSION_MAJOR}::Network  # 添加这一行
)

if(${QT_VERSION} VERSION_LESS 6.1.0)
  set(BUNDLE_ID_OPTION MACOSX_BUNDLE_GUI_IDENTIFIER com.example.TCP_IP_Test)
endif()
set_target_properties(TCP_IP_Test PROPERTIES
    ${BUNDLE_ID_OPTION}
    MACOSX_BUNDLE_BUNDLE_VERSION ${PROJECT_VERSION}
    MACOSX_BUNDLE_SHORT_VERSION_STRING ${PROJECT_VERSION_MAJOR}.${PROJECT_VERSION_MINOR}
    MACOSX_BUNDLE TRUE
    WIN32_EXECUTABLE TRUE
)

include(GNUInstallDirs)
install(TARGETS TCP_IP_Test
    BUNDLE DESTINATION .
    LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
    RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}
)

if(QT_VERSION_MAJOR EQUAL 6)
    qt_finalize_executable(TCP_IP_Test)
endif()

在dialog.ui中要添加如下控件:

控件类型 对象名称 文本/提示
Line Edit ipEdit 127.0.0.1
Line Edit portEdit 8080
QPushButton connectBtn 连接
Text Edit sendText 发送消息...
QPushButton sendBtn 发送
Text Edit receiveText (只读)
Label statusLabel 状态: 未连接

运行结果如下:

相关推荐
Mr_WangAndy20 小时前
C++设计模式_行为型模式_观察者模式Observer(发布-订阅(Publish-Subscribe))
c++·观察者模式·设计模式
墨尘笔尖20 小时前
Qt浮动(堆叠)窗口实现详解
开发语言·qt
程序员东岸20 小时前
避坑修链表:从顺序表到单链表的那点事儿(含可跑示例与小项目串联)
数据结构·笔记·学习·程序人生·链表
future141220 小时前
C++ 学习日记
开发语言·c++·学习
百味瓶20 小时前
nodejs调用C++动态库
c++·node.js
去往火星21 小时前
C++(Qt)软件调试---binutils工具集详解
开发语言·c++
yuyanjingtao21 小时前
CCF-GESP 等级考试 2025年9月认证C++一级真题解析
c++·青少年编程·gesp·csp-j/s
xzk2012100221 小时前
洛谷 P1438 无聊的数列 题解
c++·算法·树状数组
赶飞机偏偏下雨21 小时前
【Java笔记】消息队列
java·开发语言·笔记