QT(c++)开发自学笔记:2.TCP/IP
一、下载网络调试助手
下载连接:通过网盘分享的文件:网络调试助手.exe
链接: https://pan.baidu.com/s/1A-aQ9oLb3aGdxDT8bl6qjw?pwd=nf1z 提取码: nf1z
就一个.exe,点两次打开两个,两个配置:
设置一个调试助手为 TCP 服务器
- 打开第一个网络调试助手,选择 TCP Server 模式。
- 配置服务器参数:
- 本地 IP 地址:选择本机 IP(通常为
127.0.0.1
用于本地测试,或局域网 IP 如192.168.x.x
)。 - 监听端口:设置一个端口号(如
8888
),确保该端口未被其他程序占用。 - 启动服务器:点击“启动”或“监听”按钮,调试助手进入监听状态,等待客户端连接。
- 本地 IP 地址:选择本机 IP(通常为
- 记录服务器信息:记下服务器的 IP 地址和端口号,用于客户端配置。
设置另一个调试助手为 TCP 客户端
- 打开第二个网络调试助手,选择 TCP Client 模式。
- 配置客户端参数:
- 远程主机 IP:输入服务器的 IP 地址(与服务器端一致,如
127.0.0.1:8888
或局域网 IP)。 - 远程端口:输入服务器监听的端口号(如
8888
)。 - 连接服务器:点击“连接”按钮,客户端会尝试连接到服务器。
- 远程主机 IP:输入服务器的 IP 地址(与服务器端一致,如
- 确认连接状态:连接成功后,服务器端会显示客户端已连接。
然后互相发个什么能接收就行
二、PyQt实现
再用PyQt实现并验证功能,充当TCP Client
PyQt代码如下:
import sys
import socket
import threading
from PyQt5.QtWidgets import QApplication, QMainWindow, QWidget, QVBoxLayout, QHBoxLayout, QLineEdit, QPushButton, QTextEdit, QCheckBox
from PyQt5.QtCore import pyqtSignal, QObjectclass 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)# 初始化 socketself.client_socket = Noneself.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!")returntry:self.client_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)self.client_socket.connect((ip, port))self.running = Trueself.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 = Falseif self.client_socket:self.client_socket.close()self.client_socket = Noneself.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()breakif 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()breakdef send_data(self):if not self.client_socket or not self.running:self.comm.status_updated.emit("Not connected to server!")returndata = 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代码如下:
#include "dialog.h"
#include <QApplication>int main(int argc, char *argv[])
{QApplication a(argc, argv);Dialog w;w.show();return a.exec();
}
dialog.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代码如下:
#ifndef DIALOG_H
#define DIALOG_H#include <QDialog>
#include <QTcpSocket>
#include <QHostAddress>QT_BEGIN_NAMESPACE
namespace Ui { class Dialog; }
QT_END_NAMESPACEclass Dialog : public QDialog
{Q_OBJECTpublic: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代码如下:
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_SOURCESmain.cppdialog.cppdialog.hdialog.ui${TS_FILES}
)if(${QT_VERSION_MAJOR} GREATER_EQUAL 6)qt_add_executable(TCP_IP_TestMANUAL_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 PRIVATEQt${QT_VERSION_MAJOR}::WidgetsQt${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 TRUEWIN32_EXECUTABLE TRUE
)include(GNUInstallDirs)
install(TARGETS TCP_IP_TestBUNDLE 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 | 状态: 未连接 |
运行结果如下: