QTcpSocket 服务端和客户端

news/2024/11/17 4:17:16/

前提:

pro文件中添加 QT += network

服务端主要采用信号槽机制,代码如如下

核心代码头文件#ifndef TCPSERVER_H
#define TCPSERVER_H#include <QObject>#include <QTcpServer>
#include <QTcpSocket>
#include <QDebug>
#include <QMutex>
#include <QByteArray>
#include <QQueue>class OneSession{public:QTcpSocket* m_clientSocket;QByteArray m_buffer;QQueue<QByteArray> m_msgQueue;QString m_carName;int m_id;QString m_longitude;QString m_latitude;QString m_altitude;OneSession(){m_id = 0;}~OneSession(){}
};class CenterTcpSvr : public QTcpServer
{Q_OBJECT
public:CenterTcpSvr(QObject *parent = nullptr): QTcpServer(parent){}QList<OneSession> m_sessions;QMutex m_mutexForSessions;
protected:void incomingConnection(qintptr socketDescriptor) override;void readyReadFun();void clientDisconnectedFun();void printAllSessions();int parseHead(OneSession& _one);int parseCmdAndDeal(OneSession& _one);
};#endif // TCPSERVER_H核心代码源文件#include "centertcpsvr.h"void CenterTcpSvr::incomingConnection(qintptr socketDescriptor)
{QTcpSocket *socket = new QTcpSocket(this);if (!socket->setSocketDescriptor(socketDescriptor)) {qDebug() << "Error setting socket descriptor";socket->deleteLater();return;}{ //该括号配合锁使用,请不要随意删除,避免造成锁范围扩大QMutexLocker locker(&m_mutexForSessions); // 使用QMutexLocker自动管理互斥锁OneSession one;one.m_clientSocket = socket;m_sessions.append(one);printAllSessions();}// QTcpSocket::connectedconnect(socket, &QTcpSocket::readyRead, this, &CenterTcpSvr::readyReadFun);connect(socket, &QTcpSocket::disconnected, this, &CenterTcpSvr::clientDisconnectedFun);
}struct ProtocolHead{char flag[4];char version[4];char id[4]; //自己的IDchar cmd[4]; //1,注册;2,发给教辅;其他为车标号目标char bodySize[4];
};struct UserProtocolForward{char dst[4];
};int CenterTcpSvr::parseHead(OneSession& _one)
{int ret = -1;while (_one.m_buffer.size() > sizeof(ProtocolHead)) {char* tmpBuf = _one.m_buffer.data();if (_one.m_id == 0) {unsigned  id =     ((unsigned int)tmpBuf[8]  << 24 & 0xFF000000)+((unsigned int)tmpBuf[9]  << 16 & 0x00FF0000)+((unsigned int)tmpBuf[10] <<  8 & 0x0000FF00)+((unsigned int)tmpBuf[11]       & 0x000000FF);_one.m_id = id;}unsigned  bodySize = ((unsigned int)tmpBuf[16]  << 24 & 0xFF000000)+((unsigned int)tmpBuf[17]  << 16 & 0x00FF0000)+((unsigned int)tmpBuf[18] <<  8 & 0x0000FF00)+((unsigned int)tmpBuf[19]       & 0x000000FF);qDebug() << "bodySize is:" << bodySize;int outSize = sizeof(ProtocolHead) + bodySize;if (_one.m_buffer.size() >= outSize) {_one.m_msgQueue.enqueue(_one.m_buffer.left(outSize));_one.m_buffer.remove(0, outSize);ret = 0;} else {break;}}return  ret;
}int CenterTcpSvr::parseCmdAndDeal(OneSession& _one)
{int ret = 0;while (_one.m_msgQueue.size() > 0) {QByteArray bAarray = _one.m_msgQueue.dequeue();qDebug() << "dequeue content is:" << bAarray;char* tmpBuf = bAarray.data();unsigned  cmd =     ((unsigned int)tmpBuf[12]  << 24 & 0xFF000000)+((unsigned int)tmpBuf[13]  << 16 & 0x00FF0000)+((unsigned int)tmpBuf[14] <<  8 & 0x0000FF00)+((unsigned int)tmpBuf[15]       & 0x000000FF);if (cmd == 1){ //注册} else if (cmd == 2) { //发送给教辅} else {bool isIn = false;QList<OneSession>::iterator it;for(it = m_sessions.begin(); it != m_sessions.end(); ++it) {qDebug() << "cmd is : " << cmd  << "_one.m_id: " << _one.m_id ;if (cmd == it->m_id) { // 进到这里表示发送目标it->m_clientSocket->write(bAarray.data(), bAarray.size());it->m_clientSocket->waitForBytesWritten(1000);}}if (isIn) {ProtocolHead sendDataStruct;QByteArray sd;char body[] = {"{\"code\":0, \"msg\":OK}"};int bsize = sizeof(body);memcpy(sendDataStruct.flag, "#GD#", 4);memcpy(sendDataStruct.version, "0001", 4);sendDataStruct.bodySize[0] = 0 >> 24 & 0x000000FF;sendDataStruct.bodySize[1] = 0 >> 16 & 0x000000FF;sendDataStruct.bodySize[2] = 0 >> 8 & 0x000000FF;sendDataStruct.bodySize[3] = 0 & 0x000000FF;char* csend = new char[sizeof (ProtocolHead)+bsize];memset(csend, 0, sizeof(ProtocolHead) + bsize);memcpy(csend, (char*)(&sendDataStruct), sizeof(ProtocolHead));//_one.m_clientSocket->write(csend, sizeof(ProtocolHead) + bsize);_one.m_clientSocket->waitForBytesWritten(1000);delete []csend;}}}return ret;
}void CenterTcpSvr::readyReadFun() {QTcpSocket *clientSocket = qobject_cast<QTcpSocket*>(sender());//qDebug() << "readyReadData :" << clientSocket;QByteArray data = clientSocket->readAll();qDebug() << "data :" << data;QList<OneSession>::iterator it;for(it = m_sessions.begin(); it != m_sessions.end(); ++it) {if(clientSocket == it->m_clientSocket) {it->m_buffer.append(data);break;}}if (-1 == parseHead(*it)) { //解析头并分包放入队列return;}parseCmdAndDeal(*it);}void CenterTcpSvr::printAllSessions() { //此处没有上锁,请在上层使用处控制qDebug() << "clientDisconnectedFun begin. <-----------";QList<OneSession>::iterator it;for(it = m_sessions.begin(); it != m_sessions.end(); ++it) {qDebug() << "所有会话:::" << it->m_clientSocket;}qDebug() << "clientDisconnectedFun   end. ----------->";
}void CenterTcpSvr::clientDisconnectedFun() {QTcpSocket *clientSocket = qobject_cast<QTcpSocket*>(sender());qDebug() << "clientDisconnected :" << clientSocket;if (clientSocket) {// 移除断开连接的客户端socketQMutexLocker locker(&m_mutexForSessions); // 使用QMutexLocker自动管理互斥锁QList<OneSession>::iterator it;for(it = m_sessions.begin(); it != m_sessions.end(); ++it) {//qDebug() << "所有会话:" << clientSocket;if (it->m_clientSocket == clientSocket){clientSocket->deleteLater();m_sessions.erase(it);qDebug() << "删除会话:" << clientSocket;break;}}printAllSessions();}
}调用上述代码头文件#ifndef TEST_H
#define TEST_H#include <QObject>#include <QCoreApplication>
#include <QNetworkAccessManager>
#include <QNetworkRequest>
#include <QNetworkReply>
#include <QUrl>
#include <QDebug>
#include <QThread>
#include <QString>
#include "centertcpsvr.h"class ThreadForCenterTcpSvr : public QThread
{Q_OBJECT
public:ThreadForCenterTcpSvr();void run(); //线程任务函数signals:void startsignal();  //启动⼦线程的信号
};#endif // TEST_H调用上述代码源文件#include "threadforcentertcpsvr.h"
#include <QCoreApplication>
#include "centertcpsvr.h"
#include <QByteArray>
#include <QCoreApplication>
#include <QNetworkAccessManager>
#include <QNetworkRequest>
#include <QNetworkReply>
#include <QUrl>
#include <QDebug>
#include <QThread>
#include <QString>ThreadForCenterTcpSvr::ThreadForCenterTcpSvr()
{}void ThreadForCenterTcpSvr::run()
{while (true){CenterTcpSvr* server = new CenterTcpSvr();if (!server->listen(QHostAddress::Any, 8080)) {qDebug() << "Server failed to start";return;}qDebug() << "Server start";QEventLoop loop;loop.exec(); // 进入事件循环,直到请求完成//QThread::exec(); //或者用这个也可以}
}在 main 函数中启动服务int main(int argc, char *argv[])
{QCoreApplication a(argc, argv);ThreadForCenterTcpSvr* p = new ThreadForCenterTcpSvr();p->start();return a.exec();
}

客户端有两种实现方式

第一种方式如下,非信号槽方式实现

#include <QCoreApplication>#include <QTcpSocket>
#include <QThread>
#include <QByteArray>
#include <QAbstractSocket>struct ProtocolHead{char flag[4];char version[4];char id[4]; //自己的IDchar cmd[4]; //0,注册;1,发给教辅;其他为车标号目标char bodySize[4];
};int main(int argc, char *argv[])
{QCoreApplication a(argc, argv);QTcpSocket socket;while (true) {// 连接到服务器socket.connectToHost("localhost", 8080);// 等待连接if(socket.waitForConnected(100000)) {qDebug() << "Connected!";int c = 0;while (true) {// 发送数据ProtocolHead sendDataStruct;QByteArray sd;char body[] = "Hello server!";int bsize = sizeof(body);memcpy(sendDataStruct.flag, "#GD#", 4);memcpy(sendDataStruct.version, "0001", 4);int id = 48102; //自己的标识sendDataStruct.id[0] = id >> 24 & 0x000000FF;sendDataStruct.id[1] = id >> 16 & 0x000000FF;sendDataStruct.id[2] = id >> 8 & 0x000000FF;sendDataStruct.id[3] = id & 0x000000FF;int cmd = 48102; //对方的标识sendDataStruct.cmd[0] = cmd >> 24 & 0x000000FF;sendDataStruct.cmd[1] = cmd >> 16 & 0x000000FF;sendDataStruct.cmd[2] = cmd >> 8 & 0x000000FF;sendDataStruct.cmd[3] = cmd & 0x000000FF;sendDataStruct.bodySize[0] = bsize >> 24 & 0x000000FF;sendDataStruct.bodySize[1] = bsize >> 16 & 0x000000FF;sendDataStruct.bodySize[2] = bsize >> 8 & 0x000000FF;sendDataStruct.bodySize[3] = bsize & 0x000000FF;char* csend = new char[sizeof (ProtocolHead)+bsize];memset(csend, 0, sizeof(ProtocolHead) + bsize);int pos = 0;memcpy(csend+pos, (char*)(&sendDataStruct), sizeof(ProtocolHead));pos += sizeof(ProtocolHead);memcpy(csend+pos, body, bsize);socket.write(csend, sizeof (ProtocolHead)+bsize);// 等待服务器响应if(socket.waitForBytesWritten(1000)){if (c%250000==0) {qDebug() << "Message sent!";}// 等待服务器的响应while(!socket.waitForReadyRead(1000)) {//QByteArray qb = socket.readAll();if (socket.error() == QAbstractSocket::SocketTimeoutError) {qDebug() << "超时错误:没有在指定时间内接收到数据";}if (socket.error() == QAbstractSocket::RemoteHostClosedError) {qDebug() << "远程主机关闭。";while (true) {//socket.abort();socket.connectToHost("localhost", 8080);// 等待连接if(socket.waitForConnected(1000)) {qDebug() << "Connected!";break;}}} else {qDebug() << "其他错误:" << socket.errorString();}}QByteArray qb = socket.readAll();qDebug() << c << ", ======Response received:" << qb;} else {qDebug() << "Failed to send message";}//break;c++;if (c/250000==10) {c=0;}}// 关闭连接socket.close();} else {qDebug() << "Failed to connect";}QThread::sleep(1);//break;}return a.exec();
}

第二种方式,信号槽方式

文件1#ifndef TCPCLIENT_H
#define TCPCLIENT_H#include <QObject>
#include <QTcpSocket>
#include <QObject>
#include <QTcpSocket>
#include <QThread>
#include <QByteArray>
#include <QAbstractSocket>class TcpClient : public QObject
{Q_OBJECT
public:explicit TcpClient(QObject *parent = nullptr);~TcpClient();void connectToServer(const QString &host, quint16 port);private slots:void onConnected();void onDisconnected();void onReadyRead();void onBytesWritten(qint64 bytes);private:QTcpSocket *socket;
signals:};#endif // TCPCLIENT_H文件2#include "tcpclient.h"TcpClient::TcpClient(QObject *parent) : QObject(parent), socket(new QTcpSocket(this))
{connect(socket, &QTcpSocket::connected, this, &TcpClient::onConnected);connect(socket, &QTcpSocket::disconnected, this, &TcpClient::onDisconnected);connect(socket, &QTcpSocket::readyRead, this, &TcpClient::onReadyRead);connect(socket, &QTcpSocket::bytesWritten, this, &TcpClient::onBytesWritten);
}TcpClient::~TcpClient()
{delete socket; // Qt 自动管理内存,这里只是示例
}void TcpClient::connectToServer(const QString &host, quint16 port)
{socket->connectToHost(host, port);
}void TcpClient::onConnected()
{qDebug() << "Connected to server";socket->write("Hello, Server!");
}void TcpClient::onDisconnected()
{qDebug() << "Disconnected from server";
}void TcpClient::onReadyRead()
{while (socket->canReadLine()){QString line = socket->readLine().trimmed();qDebug() << "Received from server:" << line;}
}void TcpClient::onBytesWritten(qint64 bytes)
{qDebug() << "Sent" << bytes << "bytes to server";
}文件3#include <QCoreApplication>#include <QTcpSocket>
#include <QObject>
#include <QTcpSocket>
#include <QThread>
#include <QByteArray>
#include <QAbstractSocket>
#include "tcpclient.h"int main(int argc, char *argv[])
{QCoreApplication a(argc, argv);TcpClient client;client.connectToServer("127.0.0.1", 8080); // 替换为实际的服务器 IP 和端口return a.exec();
}


http://www.ppmy.cn/news/1547621.html

相关文章

用 Python 从零开始创建神经网络(七):梯度下降(Gradient Descent)/导数(Derivatives)

梯度下降&#xff08;Gradient Descent&#xff09;/导数&#xff08;Derivatives&#xff09; 引言1. 参数对输出的影响2. 斜率&#xff08;The Slope&#xff09;3. 数值导数&#xff08;The Numerical Derivative&#xff09;4. 解析导数&#xff08;The Analytical Derivat…

七:如何用Chrome的Network面板分析HTTP报文

在Web开发和调试中,分析HTTP请求和响应报文可以帮助开发者了解浏览器和服务器之间的通信细节,定位并解决各种问题。Chrome浏览器的Network(网络)面板是一个强大的开发工具,它可以详细展示HTTP请求的各个方面,包括请求方法、状态码、头部信息、负载数据等。本文将介绍如何…

Mock.js生成随机数据,拦截 Ajax 请求

Mock.js 是一个用于模拟数据的 JavaScript 库&#xff0c;特别适合用于前端开发过程中生成假数据进行接口测试。它可以拦截 Ajax 请求并生成随机数据&#xff0c;还可以模拟服务器的响应来加速前端开发。 一、安装 Mock.js 可以通过以下几种方式引入 Mock.js&#xff1a; CDN…

1909. 删除一个元素使数组严格递增【简单】

题目描述 给你一个下标从 0 开始的整数数组 nums &#xff0c;如果 恰好 删除 一个 元素后&#xff0c;数组 严格递增 &#xff0c;那么请你返回 true &#xff0c;否则返回 false 。如果数组本身已经是严格递增的&#xff0c;请你也返回 true 。 数组 nums 是 严格递增 的定…

tensorflow案例6--基于VGG16的猫狗识别(准确率99.8%+),以及tqdm、train_on_batch的简介

&#x1f368; 本文为&#x1f517;365天深度学习训练营 中的学习记录博客&#x1f356; 原作者&#xff1a;K同学啊 前言 本次还是学习API和如何搭建神经网络为主&#xff0c;这一次用VGG16去对猫狗分类&#xff0c;效果还是很好的&#xff0c;达到了99.8% 文章目录 1、tqdm…

IDEA部署AI代写插件

前言 Hello大家好&#xff0c;当下是AI盛行的时代&#xff0c;好多好多东西在AI大模型的趋势下都变得非常的简单。 比如之前想画一幅风景画得先去采风&#xff0c;然后写实什么的&#xff0c;现在你只需描述出你想要的效果AI就能够根据你的描述在几分钟之内画出一幅你想要的风景…

Unity简单漫游摄像机

可以wasd漫游场景,q/e来上升下降,可以调用TeleportAndLookAtTarget来传送到一个Transform附近并注视它,挂到摄像机上就能使用. using UnityEngine;public class CameraController : MonoBehaviour {public float moveSpeed 5f; // 相机移动速度public float sprintMultiplier…

Unity学习笔记(4):人物和基本组件

文章目录 前言开发环境新增角色添加组件RigidBody 2D全局项目设置Edit 给地图添加碰撞体 总结 前言 今天不加班&#xff0c;有空闲时间。争取一天学一课&#xff0c;养成习惯 开发环境 Unity 6windows 11vs studio 2022Unity2022.2 最新教程《勇士传说》入门到进阶&#xff…