**
更新时间:2020-08-14
**
使用socket可能会遇到的问题:
1.QSocketNotifier: Socket notifiers cannot be enabled or disabled from another thread
解决方法:跨线程使用write处理
2.Type conversion already registered from type QSharedPointer to type QObject*
这种情况是你设置的ip地址不对,没有连接上对方的socket服务器。
QT的槽函数机制是我最喜欢的。
下面封装了socket通信的客户端和服务端类
pro文件添加网络模块
QT += network
客户端:
包括基本的接收数据,发送心跳包,维持长连接,断线1h重连服务。
端口号,ip地址采用的是读取本地配置信息。可方便随时修改。
再次感叹一下QT的方便!!!
头文件和cpp
#ifndef TESTCLIENT_H
#define TESTCLIENT_H
#include <QTcpSocket> //通信套接字
#include <QTimer>
#include <QObject>
class TestClient: public QObject
{
Q_OBJECT //添加这个宏是为了实现信号和槽的通信
public:
TestClient();
//是否连接标志
bool bislink;
int gzmsgnum;
int heartnum;
//启动
//初始化连接
void initClient();
private slots:
//关闭连接
void slotCloseLink();
//读取文字
void slotReadyRead();
//发送信号
void sendheart();
private:
//配置项
QString id;
qint16 port;
//定时器
QTimer *heart_timer;
QTcpSocket *m_Socket; //通信套接字
};
#endif // TESTCLIENT_H
#include "testclient.h"
#include <QHostAddress>
#include <QSettings>
#include <QDebug>
TestClient::TestClient()
{
//读取配置文件
QSettings *configIni=new QSettings("./config.ini",QSettings::IniFormat);
id = configIni->value("GZ/id").toString();
port = configIni->value("GZ/port").toInt();
delete configIni;//读取完成删除指针
gzmsgnum=0;
heartnum=0;
bislink=false;
}
void TestClient::initClient()
{
m_Socket = NULL;//先为空
//分配空间,指定父对象
m_Socket = new QTcpSocket();
m_Socket->abort();// 取消原有连接
//连接服务器
m_Socket->connectToHost(id,port);
if (m_Socket->waitForConnected(30000))
{
qDebug()<<"link server success";
bislink=true;
//通过信号接收服务器数据
connect(m_Socket, &QTcpSocket::readyRead,this, &TestClient::slotReadyRead);
//断开连接
connect(m_Socket, &QTcpSocket::disconnected,this, &TestClient::slotCloseLink);
//启动定时器每1min去发送心跳
heart_timer = new QTimer();
connect(heart_timer, SIGNAL(timeout()),this, SLOT(sendheart()));
heart_timer->start(60 * 1000);//1min
}else
{
qDebug()<<"link server failed";
bislink=false;
initClient();
}
}
void TestClient::slotCloseLink()
{
qDebug()<<"link server break";
bislink = false;
heart_timer->stop();
//等待write数据发完,并发送disconnected()信号
m_Socket->disconnectFromHost();
if (m_Socket->state() == QAbstractSocket::UnconnectedState ||
m_Socket->waitForDisconnected(1000))
qDebug("Server has been Disconnected!");
}
void TestClient::slotReadyRead()
{
//获取对方发送的内容
QByteArray array = m_Socket->readAll();
if(!array.isEmpty())
{
QString recvMsg=array;
recvMsg.replace(" ","");//去掉空格
qDebug()<<"recv msg:"<<recvMsg;
}
}
void TestClient::sendheart()
{
//连接上才发送心跳
if(bislink == true)
{
QString heart="ok";
m_Socket->write(heart.toLatin1());
m_Socket->flush();
heartnum += 1;
}
else if(heartnum == 60)//1h
{
qDebug()<<"not revc heart 1h";
//重连
slotCloseLink();
initClient();
}
}
服务端:
参考:心跳包实现思路
服务端的话基本与客户端差不多,只是多了个新建连接槽函数。
还有一个定时检测客户端是否存活的问题。
逻辑:
服务端定时检测客户端的有限期。即当前的时间戳
客户端发送心跳包,服务端接收后,更新接收心跳的时间戳
如果检测时,当前时间戳减去最新接收心跳的时间大于间隔发送心跳的时间,则判定客户端已失去连接。
下面以客户端1分钟发送心跳,和服务端五分钟检测为例子
头文件和cpp
#ifndef TESTSERVER_H
#define TESTSERVER_H
#include <QTcpServer>
#include <QTcpSocket>
#include <QTimer>
class TestServer: public QObject
{
Q_OBJECT //添加这个宏是为了实现信号和槽的通信
public:
TestServer();
void initServer();
private slots:
//读取信息
void ReadData();
//新建连接
void NewConnection();
//关闭socket
void closeSocket();
void checkLink();
private:
qint64 curTime;
int port;
QTimer *heartTimer;
qint64 getLongUnixTime();
//监听套接字
QTcpServer *m_server;
//通信套接字
QTcpSocket *m_socket;
};
#endif // TESTSERVER_H
#include "testserver.h"
#include <QSettings>
#include <QDateTime>
TestServer::TestServer()
{
QSettings *config = new QSettings("./config.ini", QSettings::IniFormat);
port = config->value("GZ/port").toInt();
delete config;
}
void TestServer::initServer()
{
m_server=NULL;
m_socket=NULL;
m_server = new QTcpServer();
//将套接字设置为监听模式
if(!m_server->listen(QHostAddress::Any, port))
{
qDebug()<<"wait listening error:"<<m_server;
}else
{
qDebug()<<"wait listening ok:"<<m_server;
}
//通过信号接收客户端请求
connect(m_server, &QTcpServer::newConnection,this, &TestServer::NewConnection);
}
void TestServer::ReadData()
{
//读取缓冲区数据
QByteArray buffer= m_socket->readAll();
if(!buffer.isEmpty())
{
QString recvMsg=buffer;
recvMsg.replace(" ","");
qDebug()<<"recv msg: "<<recvMsg;
//接收到心跳包FE
if (recvMsg.contains("FE", Qt::CaseSensitive))
{
curTime = getLongUnixTime();
}
}
}
void TestServer::NewConnection()
{
//处理客户端的连接请求
m_socket=m_server->nextPendingConnection();
qDebug()<<"link Client success";
heartTimer = new QTimer();
connect(heartTimer, SIGNAL(timeout()), this, SLOT(checkLink()));
heartTimer->start(5 * 60 * 1000);//5min
//连接信号, 接收客户端数据
connect(m_socket, &QTcpSocket::readyRead,this, &TestServer::ReadData);
//断开连接
connect(m_socket, &QTcpSocket::disconnected,this, &TestServer::closeSocket);
}
void TestServer::closeSocket()
{
qDebug() << "link Client break";
//释放socket
m_socket->close();
}
void TestServer::checkLink()
{
//每5min检查一次心跳,如果超过有效期1min,则判客户端断开
if (getLongUnixTime() - curTime > 2 * 60 * 1000)
{
heartTimer->stop();
closeSocket();
}
}
qint64 TestServer::getLongUnixTime()
{
QString timestamp = QString::number(QDateTime::currentMSecsSinceEpoch());
qint64 time = timestamp.toLongLong();
return time;
}
服务端控制用户连接:
持续更新~