QTcpSocket
是 Qt 框架中的一个类,它提供了基于 TCP(传输控制协议)的套接字,用于在网络上进行通信。它适用于需要在客户端和服务器之间建立可靠连接的网络应用程序。
在 pro文件 中加上 network
QT += network
头文件:
#include <QTcpSocket>
常用函数:
-
connectToHost()
- 签名:
void connectToHost(const QString &hostName, quint16 port)
- 描述:建立到指定主机
hostName
和端口port
的连接。此方法是异步的,即它立即返回,并且连接在后台尝试。 - 用法:
socket->connectToHost("example.com", 80);
- 签名:
-
disconnectFromHost()
- 签名:
void disconnectFromHost()
- 描述:关闭与主机的连接。此函数也是异步的;它启动关闭连接的过程,一旦连接关闭,套接字会发出
disconnected()
信号。 - 用法:
socket->disconnectFromHost();
- 签名:
-
write()
- 签名:
qint64 write(const QByteArray &data)
- 描述:通过套接字发送指定的
data
。该函数返回实际写入的字节数。 - 用法:
socket->write("Hello, server!");
- 签名:
-
read()
- 签名:
QByteArray read(qint64 maxSize)
- 描述:从套接字读取最多
maxSize
字节的数据,并将其返回为QByteArray
。 - 用法:
QByteArray responseData = socket->read(1024);
- 签名:
-
waitForConnected()
- 签名:
bool waitForConnected(int msecs = 30000)
- 描述:阻塞直到套接字连接到主机,或直到
msecs
毫秒过去。如果连接成功建立,则返回true
;否则返回false
。 - 用法:
if (socket->waitForConnected()) { /* Connected */ }
- 签名:
-
waitForDisconnected()
- 签名:
bool waitForDisconnected(int msecs = 30000)
- 描述:阻塞直到套接字与主机断开连接,或直到
msecs
毫秒过去。如果套接字成功断开连接,则返回true
。 - 用法:
if (socket->waitForDisconnected()) { /* Disconnected */ }
- 签名:
-
bytesAvailable()
- 签名:
qint64 bytesAvailable() const
- 描述:返回可供读取的字节数。
- 用法:
if (socket->bytesAvailable() > 0) { /* Data is available */ }
- 签名:
-
state()
- 签名:
QAbstractSocket::SocketState state() const
- 描述:返回套接字的当前状态。它可以处于
UnconnectedState
、HostLookupState
、ConnectingState
、ConnectedState
、BoundState
、ListeningState
或ClosingState
状态。 - 用法:
if (socket->state() == QTcpSocket::ConnectedState) { /* Connected */ }
- 签名:
-
connected()
- 信号:
void connected()
- 描述:当套接字成功连接到主机时发出此信号。
- 用法:
connect(socket, &QTcpSocket::connected, this, &MyClass::onConnected);
- 信号:
-
disconnected()
- 信号:
void disconnected()
- 描述:当套接字与主机断开连接时发出此信号。
- 用法:
connect(socket, &QTcpSocket::disconnected, this, &MyClass::onDisconnected);
- 信号:
-
readyRead()
- 信号:
void readyRead()
- 描述:当有数据可以读取时发出此信号。
- 用法:
connect(socket, &QTcpSocket::readyRead, this, &MyClass::onReadyRead);
- 信号:
-
errorOccurred()
- 信号:
void errorOccurred(QAbstractSocket::SocketError socketError)
- 描述:当发生错误时发出此信号。可以使用
socketError
参数检查错误类型。 - 用法:
connect(socket, &QTcpSocket::errorOccurred, this, &MyClass::onError);
- 信号:
1、通过Python作为服务端发送摄像头捕捉的视频数据
import cv2
import numpy as np
import socket
# 创建一个socket连接
server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
server_socket.bind(('192.168.xx.xx',8088))
server_socket.listen(1)
# 接收来自C++/Qt客户端的连接
capture = cv2.VideoCapture(0) # 0 表示摄像头设备号,也可以改为视频文件路径
while True:
print("等待连接……")
client_socket, addr = server_socket.accept()
print("连接成功!")
try:
while True:
# 读取摄像头数据
ret, frame = capture.read()
if ret is not None:
ret, encoded_frame = cv2.imencode(".jpg", frame) # 使用JPEG编码格式
frame_string = encoded_frame.tobytes() # 将编码后的图像转换为字符串
# 发送图像数据给C++/Qt客户端
client_socket.sendall(frame_string)
confirm_msg = client_socket.recv(1024)
if confirm_msg.decode() == "received":
continue
else:
continue
except Exception as e:
print("发送数据错误:", e)
client_socket.close()
server_socket.close()
capture.release()
2、qt作为客户端进行接收并显示在label上。
#include <QTcpSocket> // 提供TCP套接字功能
#include <QByteArray> // 提供字节数组功能
#include <QDataStream> // 提供数据流读写功能
clientSocket=new QTcpSocket();
clientSocket->connectToHost("192.168.XX.XX", 8088);
QObject::connect(clientSocket, &QTcpSocket::readyRead, [=]() {
QByteArray data = clientSocket->readAll();
// 将接收到的数据转换为图像格式(例如使用QImage)
QImage image;
// 限制图像大小
image.loadFromData(data, "JPG"); // 指定图像格式为JPEG
label_image[i]->setPixmap(QPixmap::fromImage(image));
// 向服务器发送确认信号,以便服务器继续发送下一帧数据
clientSocket->write("received");
});
3.判断qt客户端是否连接。
clientSocket=new QTcpSocket();
QObject::connect(clientSocket, &QTcpSocket::stateChanged, [=](QAbstractSocket::SocketState socketState) {
if (socketState == QAbstractSocket::ConnectedState) {
qDebug() << "Connected to the server.";
} else if (socketState == QAbstractSocket::UnconnectedState) {
qDebug() << "Disconnected from the server.";
label_image[i]->setText("无视频信号");
label_image[i]->setAlignment(Qt::AlignHCenter | Qt::AlignVCenter);
// 在连接断开时重新连接
clientSocket->abort(); // 中断当前连接
clientSocket->connectToHost("192.168.XX.XX", 8088); // 重新发起连接
}
});
clientSocket->connectToHost("192.168.XX.XX", 8088);//初始化连接
4、在这个过程中有时发送数据不完整会出现数据不完整,导致视频界面闪烁。
解决方法一:只显示完整数据
缺点:画面不流畅、很卡顿。
QObject::connect(clientSocket, &QTcpSocket::readyRead, [=]() {
QByteArray data = clientSocket->readAll();
QImage image;
image.loadFromData(data, "JPG");
bool success = image.loadFromData(data, "JPG");
if (!success) {
qDebug() << "Failed to load image";
}
else{
QPixmap tempPixmap = QPixmap::fromImage(image);
QPixmap optimizedPixmap = tempPixmap.scaled(label_image[i]->size(), Qt::KeepAspectRatio, Qt::SmoothTransformation);
label_image[i]->setPixmap(optimizedPixmap);
label_image[i]->setVisible(true); // 设置可见
// 向服务器发送确认信号
clientSocket->write("received");
}
解决方法二:将一帧数据分包发送,qt分包接收
python 端分包发送将一帧长度发送给qt
def python2qt(q_frame):
while True:
try:
qt_client_socket, _ = qt_server_socket.accept()
print("成功连接qt")
while True:
frame = q_frame.get()
print("获得新frame")
_, encoded_frame = cv2.imencode(".jpg", frame) # 使用JPEG编码格式
size = len(encoded_frame) # 确定一帧数据长度
# 发送帧长度+图像数据给C++/Qt客户端
qt_client_socket.sendall(struct.pack('!i', size) + encoded_frame.tobytes()) # 发送帧大小和帧数据
confirm_msg = qt_client_socket.recv(1024)
if confirm_msg.decode() == "received":
print("接收到qt消息")
except Exception as e:
print("发送数据到qt错误:", e)
qt_client_socket.close()
qt分段接收
//.h
private:
QTcpSocket *clientSocket = nullptr;
QByteArray receivedData;
quint32 expectedSize = 0;
//.cpp
// 在 readyRead 信号的槽函数中接收数据
QObject::connect(clientSocket, &QTcpSocket::readyRead, [=]() {
if (expectedSize == 0) {
// 判断是否接收到了期望的帧大小数据
if (clientSocket->bytesAvailable() < sizeof(quint32)) {
return;
}
// 读取四个字节的帧大小数据
QByteArray sizeData = clientSocket->read(sizeof(quint32));
// 将网络字节顺序(大端序)的数据转换为主机字节顺序
QDataStream sizeStream(sizeData);
sizeStream.setByteOrder(QDataStream::BigEndian);
sizeStream >> expectedSize;
// 清空已接收的数据
receivedData.clear();
receivedData.reserve(expectedSize); // 预分配接收数据的大小
}
// 继续接收剩余数据
receivedData.append(clientSocket->readAll());
// 检查是否已接收到完整的一帧数据
if (receivedData.size() >= expectedSize) {
// 处理完整的一帧数据
// 将数据转换为图像或视频
// 在 QLabel 上显示图像或视频
QImage image;
image.loadFromData(receivedData, "JPG");
// 在此处进行图像处理或显示
label_image[i]->setPixmap(QPixmap::fromImage(image));
// 清空已接收的数据和帧大小
receivedData.clear();
expectedSize = 0;
clientSocket->write("received");
}
});
Qt端完整代码如下:
创建一个接受视频流的类DataReceiverThread
class DataReceiverThread : public QObject
{
Q_OBJECT
public:
explicit DataReceiverThread(int PORT, QObject *parent = nullptr)
: QObject(parent), m_PORT(PORT)
{
}
public:
void run()
{
m_clientSocket = new QTcpSocket(this);
QObject::connect(m_clientSocket, &QTcpSocket::stateChanged, [=](QAbstractSocket::SocketState socketState) {
if (socketState == QAbstractSocket::ConnectedState) {
isConnect=true;
qDebug() << "Connected to the server.";
} else if (socketState == QAbstractSocket::UnconnectedState) {
isConnect=false;
m_clientSocket->abort(); // 中断当前连接
if (m_clientSocket->state() == QAbstractSocket::ConnectedState) {
m_clientSocket->waitForDisconnected(); // 等待连接关闭完成
}
qDebug() <<"disConnected to the server.";
QMetaObject::invokeMethod(this, [this]() {
m_clientSocket->connectToHost("192.168.31.XX", m_PORT); // 重新发起连接
}, Qt::QueuedConnection);
// m_clientSocket->connectToHost("192.168.31.XX", m_PORT); // 重新发起连接
emit no_connect();
// 重新启动接收数据的线程
}
});
m_clientSocket->connectToHost("192.168.31.XX", m_PORT);
QObject::connect(m_clientSocket, &QTcpSocket::readyRead, [=]() {
if(isConnect){
if (m_expectedSize == 0) {
// 判断是否接收到了期望的帧大小数据
if (m_clientSocket->bytesAvailable() < sizeof(quint32)) {
return;
}
// 读取四个字节的帧大小数据
QByteArray sizeData = m_clientSocket->read(sizeof(quint32));
// 将网络字节顺序(大端序)的数据转换为主机字节顺序
QDataStream sizeStream(sizeData);
sizeStream.setByteOrder(QDataStream::BigEndian);
sizeStream >> m_expectedSize;
// 清空已接收的数据
m_receivedData.clear();
m_receivedData.reserve(m_expectedSize); // 预分配接收数据的大小
}
// 继续接收剩余数据
m_receivedData.append(m_clientSocket->readAll());
// 检查是否已接收到完整的一帧数据
if (m_receivedData.size() >= m_expectedSize) {
emit dataReceived(m_receivedData);
}}}
);
}
public slots:
void clearData(){
m_receivedData.clear();
m_expectedSize = 0;
m_clientSocket->write("received");
}
void stopThread() {
// 停止线程的运行
m_stopThread = true;
// 关闭套接字连接
m_clientSocket->disconnectFromHost();
qDebug()<<"destruction";
// 将线程从事件循环中退出
QCoreApplication::postEvent(this, new QEvent(QEvent::Quit));
}
void deleteObject() {
delete this;
}
signals:
void dataReceived(const QByteArray& data);
void no_connect();
private:
QTcpSocket* m_clientSocket;
int m_PORT;
quint32 m_expectedSize=0;
QByteArray m_receivedData;
bool isConnect;
bool m_stopThread;
void stopConnection()
{
m_stopThread = true;
m_clientSocket->disconnectFromHost();
m_clientSocket->deleteLater();
m_clientSocket = nullptr;
}
};
mianwindows.h
QTcpSocket *clientSocket = nullptr;
DataReceiverThread* DreceiverThread= new DataReceiverThread(0000);
mainwindows .cpp
void Video_Status::updatePixmap(int i)
{
DreceiverThread = new DataReceiverThread(12101);
receiverThread = new QThread();
DreceiverThread->moveToThread(receiverThread);
QObject::connect(this, &Video_Status::stopThread, DreceiverThread, &DataReceiverThread::stopThread);
//当接受数据后显示在label上
QObject::connect(DreceiverThread, &DataReceiverThread::dataReceived,this,[=](const QByteArray& data) {
QImage image;
image.loadFromData(data, "JPG");
// 在此处进行图像处理或显示
label_image[i][0]->setPixmap(QPixmap::fromImage(image));
emit over();
});
//显示完图像后清楚数据
QObject::connect(this, &Video_Status::over, DreceiverThread, &DataReceiverThread::clearData);
//断开连接时显示无视频信号
QObject::connect(DreceiverThread, &DataReceiverThread::no_connect, this,[=]() {
label_image[i][0]->setPalette(pe);
label_image[i][0]->setText("无视频信号");
});
//将线程和类连接在一起
QObject::connect(receiverThread, &QThread::started, DreceiverThread, &DataReceiverThread::run);
receiverThread->start();
}