QT的socket客户端服务端通信(槽函数触发式)

**

更新时间: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;
}


服务端控制用户连接:

持续更新~

Qt是一个跨平台的C++应用程序框架,它提供了丰富的库和工具来帮助开发者创建各种类型的应用程序,包括网络通信应用。 在Qt中实现TCP服务端客户端通信非常简单。首先,我们需要创建一个QTcpServer对象来作为服务端,该对象负责监听客户端的连接请求。接下来,我们需要为QTcpServer对象绑定一个IP地址和端口号,以便客户端能够连接到该地址和端口。当有新的客户端连接时,QTcpServer对象会发出newConnection()信号。 在服务端函数中,我们可以使用QTcpServer的nextPendingConnection()函数获取新连接的QTcpSocket对象,该对象用于和客户端进行通信。我们可以在该对象上发送和接收数据,以实现服务端客户端之间的数据交互。 接下来,我们需要创建一个QTcpSocket对象作为客户端,该对象负责和服务端建立连接。我们需要使用connectToHost()函数来指定要连接的服务端的IP地址和端口号。一旦连接成功,QTcpSocket对象会发出connected()信号。 在客户端函数中,我们可以使用QTcpSocket对象发送和接收数据。我们可以使用write()函数发送数据,使用readyRead()信号和read()函数接收数据。 通过这种方式,我们可以在服务端客户端之间进行双向的数据通信服务端可以同时接受多个客户端的连接,每个客户端都可以和服务端进行独立的数据交互。 总的来说,使用Qt实现TCP服务端客户端通信非常方便。通过使用QTcpServerQTcpSocket类,我们可以轻松地建立连接,发送和接收数据。这种通信方式可以应用于多种场景,如实时聊天、远程控制等。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值