Qt TCP的使用

编程环境

vs2019+QT5.12.10

tcp连接

1.构造创建

ServerListenSocket = new QTcpServer(this);	//服务器端监听套接字 ServerListenSocket
		ClientConnectedSocket = new QTcpSocket(this);
		closTcpFlag = false;
		if (!ServerListenSocket->listen(QHostAddress::Any, 8888))	//监听本机8888端口上的任意IP地址的连入
		{
			//QMessageBox::critical(this, tr("Fortune Server"), tr("Unable to start the server:%l.").arg(ServerListenSocket->errorString()));
			ServerListenSocket->close();
			return;
		}
		connect(ServerListenSocket, SIGNAL(newConnection()), this, SLOT(clientConnection()));	//将监听套接字有新客户端的接入信号newConnection() 的与处理的槽函数clientConnection() 连接

		clientRequestSize = 0;	//客户端发送过来的第一段数据块的大小(字节数)

2.二次交互(握手)

//有新客户端连接的处理函数
void Server::clientConnection()
{
	//ui.info->addItem("An new client is connected!");
	ShowNews("有个新设备已连接", "requestType");
	ClientConnectedSocket = ServerListenSocket->nextPendingConnection();	//返回已连接套接字对象
	connect(ClientConnectedSocket, SIGNAL(readyRead()), this, SLOT(readClientRequest()));	//将已连接套接字对象的准备好可读信号readyRead与 readClientRequest()槽函数连接
	connect(ClientConnectedSocket, SIGNAL(disconnected()), ClientConnectedSocket, SLOT(deleterLater()));	//已连接套接字的断开信号与自身的稍后删除信号相连接
}
//读取客户端发送过来的 请求 (发送任意字节流,只是将其按收发双方约定的规则解释为 第一次交互的请求)
void  Server::readClientRequest()
{
	QDataStream in(ClientConnectedSocket);
	in.setVersion(QDataStream::Qt_5_10);
	//如果客户端发送过来的第一段数据块的大小为0,说明确实是第一次交互
	if (clientRequestSize == 0)
	{
		//客户端发送过来的第一段数据块的大小如果小于 64bit ,则说明:还未收到客户端发送过来的前64bit的数据,这64bit的数据存储了客户端第一次请求包的大小(字节数)
		if (ClientConnectedSocket->bytesAvailable() < sizeof(quint16))
		{
			return ;	//返回,继续等待 接收数据,数据还在套接字缓存当中
		}
		else//如果 客户端发送过来的第一段数据块的大小>= 64bit 了
		{
			in >> clientRequestSize;//将数据的前64bit提取出来,存储到quint64 类型的clientRequestSize 
		}
	}
	if (ClientConnectedSocket->bytesAvailable() < clientRequestSize)//当前套接字缓冲区中存储的数据如果小于clientRequestSize个字节
	{
		return ;//返回,继续等待 接收数据,数据还在套接字缓存当中
	}
	quint8 requestType;
	in >> requestType;//从套接字缓冲区中读取 8bit的数据解释为quint8类型,存储到requestType中

	if (requestType == 'R')  //如果requestType是 'R'  字符R的ASCII值
	{
		ShowNews("连接成功", "");
		emit resivFirst();
		connect_flag = true;
	}
	
}

3.数据发送

ClientConnectedSocket->write(ba);//把图像数据写入传输给主机
int i_temp = ClientConnectedSocket->waitForReadyRead(); //等待主机响应第二个“ok”
ba.clear();
  1. 在头文件里
#pragma once

#include <QObject>
#include<QtNetwork>
#include<QTcpServer>
#include<QTcpSocket>
#include<QImage>
#include<QImageReader>
#include<QTime>
#include<QDebug>
#include<QMessageBox>
#include<QFileDialog>
#include <QPixmap>
#include <QMutexLocker>
#include <QMutex>

class Server : public QObject
{
	Q_OBJECT

public:
	Server();
	~Server();
private:
	
	QTcpServer*					ServerListenSocket;
	QTcpSocket*					ClientConnectedSocket;
	QTimer*						timer1;
	QPixmap						pixmap;
	QMatrix						matrix;
	quint16						clientRequestSize;
	QTimer*						timer;
	bool						connect_flag;
	bool						closTcpFlag;
private:
	
	void ShowNews(QString ch_z, QString mess);
signals:
	void resivFirst();
	//把处理后的记过返回给主线程
	void resultReady(QByteArray tempimg);
	void msgTrans(QString msg);
private slots:
	void						clientConnection();
	void 						readClientRequest();
	
public slots:
	void						SendData(QImage image);
	void						cutconnnect();
	
};

##如果在VS下使用Qt记得设置
在这里插入图片描述

接收端(客户端)

.h

#pragma once

#include <QtWidgets/QWidget>
#include "ui_UdpRev.h"
#include <QWidget>
#include<QTcpSocket>
#include<QTcpServer>
#include <QPixmap>
#include <QMatrix>
#include <QTimer>
#include <QPushButton>
#include <QApplication>
#include <QtGui>
#include <QLabel>
#include <QPushButton>
#include <QComboBox>



//包头

class TcpRev : public QWidget
{
	Q_OBJECT

public:
	TcpRev(QWidget* parent = Q_NULLPTR);
	QTcpSocket tcpSocket;
	QImage img;
	qint64 imageBlockSize;
	double m_currentIndex;
	QImage	pixmapScale(const QImage& image, const double& index);
	void wheelEvent(QWheelEvent* event);
	int		         m_npfpsCont;				///< 帧率计数
	double			 fFps;
	QTimer*			 m_pTimer;					///<定时器
private:
	void ShowImage(QByteArray ba);
private:
	
private slots:
	void						connectToServer();
	void						sendRequest();
	void						ReceiveData();
	void						connectionCloseByServer();
	void						error();
	void						tcpConnected();

	void						on_connectToServer_clicked();
	void						on_requestVideo_clicked();
	void						ShowNewsR(QString ch_z, QString mess);
	void						TimerEvent();

private:
	Ui::TCPRevClass ui;
};

.cpp

#include "TcpRev.h"
#include <QBuffer>
#include <QImage>
#include <QDebug>
#include <QHostInfo>
#include <QNetworkInterface>
#include <QCoreApplication>
#include <QTime>
#include <windows.h>
#include "qevent.h"
#pragma execution_character_set("utf-8")
TcpRev::TcpRev(QWidget *parent)
    : QWidget(parent)
{
    ui.setupUi(this);
	setAttribute(Qt::WA_StyledBackground);
	ui.newEdit_2->setStyleSheet("QTextEdit{background-color:transparent}");
	ui.lineEditPort_2->setStyleSheet("QLineEdit{background-color:transparent}"
		"QLineEdit{border-width:0;border-style:outset}");
	ui.lineEditIP_2->setStyleSheet("QLineEdit{background-color:transparent}"
		"QLineEdit{border-width:0;border-style:outset}");
	ui.lineEditRevSize->setStyleSheet("QLineEdit{background-color:transparent}"
		"QLineEdit{border-width:0;border-style:outset}");
	ui.lineEditRevFrame->setStyleSheet("QLineEdit{background-color:transparent}"
		"QLineEdit{border-width:0;border-style:outset}");
	ui.lineEditRevSize_2->setStyleSheet("QLineEdit{background-color:transparent}"
		"QLineEdit{border-width:0;border-style:outset}");
	QBrush myBrush;
	QPalette palette;
	myBrush = QBrush(Qt::white, Qt::DiagCrossPattern);
	palette.setBrush(QPalette::Text, myBrush);
	ui.newEdit_2->setPalette(palette);
	ui.lineEditPort_2->setPalette(palette);
	ui.lineEditIP_2->setPalette(palette);
	ui.lineEditRevSize->setPalette(palette);
	ui.lineEditRevFrame->setPalette(palette);
	ui.lineEditRevSize_2->setPalette(palette);
	
	imageBlockSize = 0;		//一次接收的图像数据的大小(字节数)

	m_pTimer = new QTimer(this);
	
	connect(m_pTimer, SIGNAL(timeout()), this, SLOT(TimerEvent()));
	connect(&tcpSocket, SIGNAL(disconnect()), this, SLOT(connectionCloseByServer()));//套接字的断开信号
	connect(&tcpSocket, SIGNAL(readyRead()), this, SLOT(ReceiveData()));//套接字一次可读的触发信号
	connect(&tcpSocket, SIGNAL(error(QAbstractSocket::SocketError)), this, SLOT(error()));//套接字的错误消息信号
	m_currentIndex = 0.4;
	m_npfpsCont = 0;				///< 帧率计数
	fFps = 0; 
}

void TcpRev::on_connectToServer_clicked()//点击该按钮,连接到服务器主机
{
	connectToServer();
	bool k = connect(&tcpSocket, SIGNAL(connected()), this, SLOT(tcpConnected()));	//将连接以完成信号与 tcpConnected()槽函数绑定
}

void TcpRev::on_requestVideo_clicked()//点击该按钮,发送请求,请求视频图像序列
{
	sendRequest();
	m_pTimer->start(50);
}
void TcpRev::connectToServer()
{
	//连接到本机的8888端口
	 tcpSocket.connectToHost(QHostAddress::LocalHost, 8888);//connectToHost是异步连接函数(不阻塞),一经调用结束立刻返回
	ui.lineEditIP_2->setText(QHostInfo::localHostName());
	ui.lineEditPort_2->setText("8888");
	
	ShowNewsR("连接中", "connecting to LocalHost port 8888...");
	ui.connectToServer->setEnabled(false);
	ShowNewsR("连接成功", QString::number(QHostAddress::LocalHost));
}

void TcpRev::sendRequest()//发送请求,请求视频图像序列
{
	QByteArray requestMessage;	//请求消息(字节数组)
	QDataStream out(&requestMessage, QIODevice::WriteOnly);//只读输出流
	out.setVersion(QDataStream::Qt_5_10);
	out << quint16(0) << quint8('R');//将请求消息的大小、长度(字节数)与请求消息 'R'写入 输出数据流out
	out.device()->seek(0);
	out << quint16(requestMessage.size() - sizeof(quint16));

	tcpSocket.write(requestMessage);//将输出数据流中的数据写入套接字
	ShowNewsR("发送中", "Sending request...");
	ui.requestVideo->setEnabled(false);

}
static int receiveCount = 0;//接收 readyRead()信号的触发计数
static int imageCount = 0;	//接收到的图像数据的计数

void TcpRev::ReceiveData()
{
	receiveCount++;
	QString rCount = QString::number(receiveCount);
	ui.lineEditRevFrame->setText(rCount);
	QByteArray message;//存放从服务器接收到的字节流数据
	QDataStream in(&tcpSocket);	//将客户端套接字与输入数据流对象in绑定

	in.setVersion(QDataStream::Qt_5_12);//设置数据流的版本

	//ShowNewsR("接收信号的个数", QString::number(receiveCount));
	if (imageBlockSize == 0)
	{
		//如果imageBlockSize == 0 则说明,一幅图像的大小信息还未传输过来
		//uint64是8字节的8 Bytes  64bit
		//判断接收的数据是否有8字节(文件大小信息)
		//如果有则保存到basize变量中,没有则返回,继续接收数据
		if (tcpSocket.bytesAvailable() < (int)sizeof(quint64))
		{//一幅图像的大小信息还未传输过来
			return;
		}
		in >> imageBlockSize;//一幅图像的大小信息
		if (imageBlockSize == (quint64)0xFFFFFFFFFFFFFFFF)//视频结束的标注符
		{
			tcpSocket.close();
			
			ShowNewsR("警告", "the video is end!");
			ui.requestVideo->setEnabled(true);
			ui.connectToServer->setEnabled(true);
			return;
		}
		ui.lineEditRevSize->setText(QString::number(imageBlockSize));
		tcpSocket.write("ok");
		message.resize(imageBlockSize);

	}
	//如果没有得到一幅图像的全部数据,则返回继续接收数据
	if (tcpSocket.bytesAvailable() < imageBlockSize)
	{
		return;
	}

	in >> message;//一幅图像所有像素的完整字节流

	imageBlockSize = 0;//已经收到一幅完整的图像,将imageBlockSize置0,等待接收下一幅图像
	imageCount++;	//已接收的图像计数

	QString iCount = QString::number(imageCount);
	ui.lineEditRevFrame->setText(iCount);
	ShowImage(message);	//显示当前接收到的这一幅图像
}
void TcpRev::connectionCloseByServer()//服务端主动断开了已连接套接字
{
	ShowNewsR("错误", ":Connection closed by server!");
	tcpSocket.close();//关闭客户端套接字
	ui.connectToServer->setEnabled(true);
}
void TcpRev::error()
{
	
	ShowNewsR("错误", tcpSocket.errorString());
	tcpSocket.close();
	ui.connectToServer->setEnabled(true);
}

void TcpRev::tcpConnected()//套接字已经建立连接信号的处理槽函数
{
	ui.requestVideo->setEnabled(true);
}


void TcpRev::ShowImage(QByteArray ba)	//从接收到了字节流中,执行与服务器断相反的操作:解压缩、解释为图像数据
{
	m_npfpsCont++;
	QString ss = QString::fromLatin1(ba.data(), ba.size());
	QByteArray rc;
	rc = QByteArray::fromBase64(ss.toLatin1());
	QByteArray rdc = qUncompress(rc);
	
	//img.loadFromData(rdc,"JPEG");//解释为jpg格式的图像
	img.loadFromData(rdc);//解释为jpg格式的图像

	QImage image;

	image = pixmapScale(img, m_currentIndex);
	rc.clear();
	tcpSocket.write("ok");
	update();
}
void TcpRev::wheelEvent(QWheelEvent* event)
{
	
	m_currentIndex += event->delta() / 3000.0;
	ui.lineEditRevSize_2->setText(QString::number(m_currentIndex));
	if (event->delta()<0 && m_currentIndex<0.13)
	{
		m_currentIndex = m_currentIndex - 0.05;
	}
	else
	{
		m_currentIndex += event->delta() / 1000.0;
	}
	if (event->delta() < 0 && m_currentIndex <0.02)
	{
		m_currentIndex = 0.01;
	}
	QImage image;
	image = pixmapScale(img, m_currentIndex);
	ui.widget->show();
	tcpSocket.write("ok");
	
	update();

}
QImage TcpRev::pixmapScale(const QImage& image, const double& index)
{
	QImage r_image;
	r_image = image.scaled(image.width() * index, image.height() * index, Qt::IgnoreAspectRatio, Qt::SmoothTransformation);
	ui.labelImage_2->setPixmap(QPixmap::fromImage(r_image));
	ui.labelImage_2->resize(r_image.size());
	tcpSocket.write("ok");
	return r_image;
}
void TcpRev::ShowNewsR(QString ch_z, QString mess)
{
	QString tempText;
	QByteArray tem1 = ch_z.toUtf8();
	QByteArray tem2 = mess.toLatin1();
	tempText.sprintf("%s:%s", tem1.data(), tem2.data());
	ui.newEdit_2->append(tempText);

}
void TcpRev::TimerEvent()
{
	uint32_t       nDevNum = 1;
	
	// TODO: Add your message handler code here and/or call default
	uint32_t	    i = 0;      //循环变量
	static int		s_nCont = 0;      //累加值
	static time_t	s_lTimeFirst = clock();
	static time_t	s_lTimeSecond = clock();

	if ((nDevNum <= 0))
	{

		return;
	}

	//统计帧率
	s_nCont++;
	if (s_nCont >= 10)
	{
		s_lTimeFirst = clock();
		for (i = 0; i < nDevNum; i++)
		{
			fFps = m_npfpsCont * 1000 / (float)(s_lTimeFirst - s_lTimeSecond);
			m_npfpsCont = 0;
		}
		s_lTimeSecond = clock();
		s_nCont = 0;
	}
	ui.label_fps->setText(QString::number(fFps));

}

此致

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: QTTCP通信使用线程池可以提高系统的并发性和响应性。通常情况下,使用单个线程来处理TCP通信可能会导致系统在处理大量连接时出现阻塞,影响系统的性能。 通过使用线程池,可以将TCP通信任务分配给多个线程来处理,从而使系统能够同时处理多个连接。线程池包含一组线程,这些线程在需要时可以立即执行任务。这样,可以确保每个连接都能获得足够的CPU时间,而不会被其他连接阻塞。 QT的线程池机制可以方便地管理线程的生命周期,分配和回收线程资源。当一个连接建立时,可以从线程池中获取一个线程来处理该连接,当连接终止时,可以将线程归还给线程池。这种方式可以避免不断创建和销毁线程的开销,提高系统的效率。 通过使用线程池,还可以实现线程的复用。当一个连接完成任务后,并不立即销毁线程,而是将线程放回线程池,以供下一个连接使用。这样可以避免线程的频繁创建和销毁,减少系统的开销,提高系统的性能。 总而言之,QTTCP通信使用线程池可以提高系统的并发性和响应性,减少线程的创建和销毁开销,提高系统的性能和效率。 ### 回答2: 在Qt中,TCP通信可以使用线程池来实现。线程池是一种管理和调度多个线程的机制,可以有效地利用系统资源并提高程序的性能。 在使用Qt进行TCP通信时,我们可以使用QtQtConcurrent模块配合线程池来处理通信任务。QtConcurrent模块提供了一些方便的类和函数来进行并行编程,其中就包括了线程池。 首先,我们需要创建一个线程池对象,并指定线程池中的线程数量。可以根据需要来调整线程数量,以避免线程过多或过少造成的资源浪费或程序性能下降。 然后,我们可以使用QtConcurrent::run()函数来将需要执行的通信任务放入线程池中执行。该函数接受一个函数指针或lambda表达式作为参数,用于指定要执行的任务。同时,我们还可以通过该函数的返回值来获取任务的执行结果。 在通信任务中,我们可以使用QtQTcpSocket类来进行TCP通信操作。比如,可以使用QTcpSocket的connectToHost()函数来连接到服务器,使用write()函数来发送数据,使用read()函数来接收数据等等。 通过使用线程池,我们可以将通信任务分配给多个线程同时处理,提高了程序的并发性能。同时,线程池还可以根据系统资源的使用情况动态调整线程数量,以避免资源浪费和程序性能下降。 总之,通过在Qt使用线程池来处理TCP通信,可以提高程序的性能和并发能力,同时还可以更好地利用系统资源。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值