优化后的TCP client 增加心跳包

优化后的TCP client 增加心跳包

cpp 复制代码
#include <iostream>
#include <winsock2.h>
#include <ws2tcpip.h>
#include <windows.h>
#include <thread>
#include <chrono>

#pragma comment(lib, "ws2_32.lib")

#define SERVER_IP "127.0.0.1"
#define SERVER_PORT 8080
#define RECV_BUF_SIZE 1024
#define HEARTBEAT_INTERVAL 5000  // 心跳包发送间隔,单位:毫秒
#define HEARTBEAT_TIMEOUT 10000  // 心跳包超时时间,单位:毫秒

class TCPClient {
public:
    TCPClient();
    ~TCPClient();

    bool connectToServer();
    void disconnect();
    int sendData(const char* data, int len);
    int receiveData(char* buffer, int bufLen);

private:
    SOCKET m_socket;
    sockaddr_in m_serverAddr;
    
public:
    bool m_bHeartBeat;//是否启动心跳包
    bool m_connected;
    // 心跳包相关函数
    static DWORD WINAPI ThreadstartHeartbeat(LPVOID lpParam);
    int startHeartbeat();
    void sendHeartbeat();
    bool checkHeartbeatResponse();

    // 重连相关函数
    bool reconnect();
};

TCPClient::TCPClient() : m_socket(INVALID_SOCKET), 
m_connected(false), m_bHeartBeat(false)
{
    WSADATA wsaData;
    if (WSAStartup(MAKEWORD(2, 2), &wsaData) != 0) 
    {
        std::cerr << "WSAStartup failed." << std::endl;
        // 可以在这里抛出异常或者采取其他错误处理方式
    }

    m_serverAddr.sin_family = AF_INET;
    m_serverAddr.sin_port = htons(SERVER_PORT);
    if (inet_pton(AF_INET, SERVER_IP, &(m_serverAddr.sin_addr)) <= 0) 
    {
        std::cerr << "inet_pton error." << std::endl;
        // 可以在这里添加更合适的错误处理逻辑
    }
}

TCPClient::~TCPClient()
{
    disconnect();
    WSACleanup();
}

bool TCPClient::connectToServer()
{
    m_socket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
    if (m_socket == INVALID_SOCKET) {
        std::cerr << "Socket creation failed." << WSAGetLastError() << std::endl;
        return false;
    }

    int ret = connect(m_socket, (struct sockaddr*)&m_serverAddr, sizeof(m_serverAddr));
    if (ret == SOCKET_ERROR) {
        int errCode = WSAGetLastError();
        if (errCode == WSAECONNREFUSED) {
            std::cerr << "Connection refused. Will attempt to reconnect." << std::endl;
            // 尝试重连
            return reconnect();
        }
        else {
            std::cerr << "Connect failed. Error code: " << errCode << std::endl;
            closesocket(m_socket);
            m_socket = INVALID_SOCKET;
            return false;
        }
    }

    m_connected = true;
    std::cout << "Connected to server." << std::endl;

    // 连接成功后启动心跳包线程
    if (m_bHeartBeat == true)
    {
        HANDLE heartBeatThreadHandle = CreateThread(NULL, 0, ThreadstartHeartbeat, this, 0, NULL);
        if (heartBeatThreadHandle == NULL)
        {
            std::cerr << "Create heartBeat thread failed: " << GetLastError() << std::endl;
        }
        else
        {
            std::cerr << "Create heartBeat thread success: " << heartBeatThreadHandle << std::endl;
        }
    }
    return true;
}

void TCPClient::disconnect() 
{
    if (m_connected) {
        closesocket(m_socket);
        m_connected = false;
        std::cout << "Disconnected from server." << std::endl;
    }
}

int TCPClient::sendData(const char* data, int len)
{
    if (!m_connected)
    {
        std::cerr << "Not connected. Cannot send data." << std::endl;
        return SOCKET_ERROR;
    }

    int ret = send(m_socket, data, len, 0);
    if (ret == SOCKET_ERROR)
    {
        std::cerr << "Send failed. Error code: " << WSAGetLastError() << std::endl;
        // 检查错误是否是连接断开,如果是则尝试重连
        int errCode = WSAGetLastError();
        if (errCode == WSAECONNRESET || errCode == WSAENETRESET)
        {
            std::cerr << "Connection reset. Will attempt to reconnect." << std::endl;
            if (reconnect()) {
                // 重连成功后重新发送数据
                return sendData(data, len);
            }
        }
    }
    return ret;
}

int TCPClient::receiveData(char* buffer, int bufLen)
{
    if (!m_connected)
    {
        std::cerr << "Not connected. Cannot receive data." << std::endl;
        return SOCKET_ERROR;
    }

    int ret = recv(m_socket, buffer, bufLen, 0);
    if (ret == SOCKET_ERROR)
    {
        std::cerr << "Receive failed. Error code: " << WSAGetLastError() << std::endl;
        // 检查错误是否是连接断开,如果是则尝试重连
        int errCode = WSAGetLastError();
        if (errCode == WSAECONNRESET || errCode == WSAENETRESET) {
            std::cerr << "Connection reset. Will attempt to reconnect." << std::endl;
            if (reconnect()) {
                // 重连成功后重新接收数据
                return receiveData(buffer, bufLen);
            }
        }
    }
    return ret;
}

int TCPClient::startHeartbeat()
{
    while (m_connected) 
    {
        sendHeartbeat();
        if (!checkHeartbeatResponse()) 
        {
            std::cerr << "Heartbeat timeout. Will attempt to reconnect." << std::endl;
            if (reconnect())
            {
                continue;
            }
            else
            {
                // 重连失败,退出心跳包线程
                std::cout << "Heartbeat timeout. exit." << std::endl;
                m_connected = false;
                break;
            }
        }
       Sleep(HEARTBEAT_INTERVAL);
    }
    return 0;
}

// 心跳包线程函数
DWORD WINAPI TCPClient::ThreadstartHeartbeat(LPVOID lpParam)
{
    TCPClient* t_Clienet = static_cast<TCPClient*>(lpParam);
    t_Clienet->startHeartbeat();
    return 0;
}

// 发送心跳包
void TCPClient::sendHeartbeat() 
{
    const char* heartbeatData = "HEARTBEAT_CLIENT";
    int ret = sendData(heartbeatData, strlen(heartbeatData));
    if (ret > 0)
    {
        std::cout << "sent heart beat: " << heartbeatData << std::endl;
    }
    else
    {
        std::cout << "sent heart beat: " << heartbeatData << std::endl;
    }
}

// 检查心跳包响应
bool TCPClient::checkHeartbeatResponse()
{
    char buffer[RECV_BUF_SIZE] = {0};
    int ret = receiveData(buffer, RECV_BUF_SIZE);
    if (ret > 0)
    {
        std::cout << "heart beat response: " << buffer << std::endl;
        return 1;
        buffer[ret] = '\0';
        if (strcmp(buffer, "HEARTBEAT_ACK") == 0) 
        {
            return true;
        }
    }
    return false;
}

bool TCPClient::reconnect() 
{
    // 关闭当前无效的套接字
    if (m_socket != INVALID_SOCKET) {
        closesocket(m_socket);
        m_socket = INVALID_SOCKET;
    }

    // 等待一段时间后尝试重连
    Sleep(5000);  // 这里等待 5 秒,可以根据实际情况调整

    // 重新创建套接字并连接
    m_socket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
    if (m_socket == INVALID_SOCKET)
    {
        std::cerr << "Socket creation failed during reconnect." << WSAGetLastError() << std::endl;
        return false;
    }

    int ret = connect(m_socket, (struct sockaddr*)&m_serverAddr, sizeof(m_serverAddr));
    if (ret == SOCKET_ERROR) {
        int errCode = WSAGetLastError();
        if (errCode == WSAECONNREFUSED) {
            std::cerr << "Reconnection refused. Will try again later." << std::endl;
            // 可以在这里设置更复杂的重连策略,比如增加等待时间后再次重连
            return false;
        }
        else {
            std::cerr << "Reconnect failed. Error code: " << errCode << std::endl;
            closesocket(m_socket);
            m_socket = INVALID_SOCKET;
            return false;
        }
    }

    m_connected = true;
    std::cout << "Reconnected to server." << std::endl;

    if (m_bHeartBeat == true)
    {
        std::thread heartbeatThread(&TCPClient::startHeartbeat, this);
        heartbeatThread.detach();
    }

    if (m_bHeartBeat == true)
    {
        HANDLE heartBeatThreadHandle = CreateThread(NULL, 0,ThreadstartHeartbeat, this, 0, NULL);
        if (heartBeatThreadHandle == NULL)
        {
            std::cerr << "Create heartBeat thread failed: " << GetLastError() << std::endl;
        }
        else
        {
            std::cerr << "Create heartBeat thread success: " << heartBeatThreadHandle << std::endl;
        }
    }

    return true;
}

int main()
{
    TCPClient client;
    client.m_bHeartBeat = false;
    static int m = 0;
    if (client.connectToServer()) 
    {
        while (client.m_connected)
        {
            Sleep(1000);
            char sendBuf[1024] = {0}; 
            sprintf(sendBuf, "Hello, server  %d", m++);
            client.sendData(sendBuf, strlen(sendBuf));

            char recvBuf[RECV_BUF_SIZE];
            int recvLen = client.receiveData(recvBuf, RECV_BUF_SIZE);
            if (recvLen > 0)
            {
                recvBuf[recvLen] = '\0';
                std::cout << "Received from server: " << recvBuf << std::endl;
            }
        }
    }
    else
    {
        std::cout << "connect to server failed" << std::endl;
    }

    return 0;
}
相关推荐
brave and determined29 分钟前
接口通讯学习(day04):RS-232与RS-485:通信接口全解析
网络·uart·通讯·emc·rs232·rs485·嵌入式设计
檀越剑指大厂29 分钟前
在家也能远程调代码?WSL+cpolar 的实用技巧分享
网络
秋邱35 分钟前
价值升维!公益赋能 + 绿色技术 + 终身学习,构建可持续教育 AI 生态
网络·数据库·人工智能·redis·python·学习·docker
爱学习的大牛1231 小时前
如何系统学习网络渗透测试:从入门到精通的完整指南
网络·学习
程序猿编码1 小时前
PRINCE算法的密码生成器:原理与设计思路(C/C++代码实现)
c语言·网络·c++·算法·安全·prince
飞行增长手记2 小时前
什么是高匿代理IP?安全吗?怎么选?
网络协议·tcp/ip·安全
white-persist2 小时前
【攻防世界】reverse | Reversing-x64Elf-100 详细题解 WP
c语言·开发语言·网络·python·学习·安全·php
HKT_China2 小时前
香港电讯与Fortinet推出100G高效加密网络托管服务,迈进量子安全新时代
网络·安全
上海云盾安全满满6 小时前
高防 IP 是如何帮助数藏行业防刷
网络·网络协议·tcp/ip
吠品8 小时前
免费SSL证书自动化申请:DNS代理验证
网络协议·自动化·ssl