QTcpSocket 服务端和客户端
前提:
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::connected
connect(socket, &QTcpSocket::readyRead, this, &CenterTcpSvr::readyReadFun);
connect(socket, &QTcpSocket::disconnected, this, &CenterTcpSvr::clientDisconnectedFun);
}
struct ProtocolHead{
char flag[4];
char version[4];
char id[4]; //自己的ID
char 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) {
// 移除断开连接的客户端socket
QMutexLocker 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]; //自己的ID
char 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();
}