优化后的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;
}
相关推荐
Dynadot_tech5 分钟前
使用Dynadot API为文件夹中的域名设置域名服务器(NS)ip信息
网络·域名注册·dynadot
程序员黄同学1 小时前
如何使用 Python 实现 UDP 通信?
网络·python·udp
小红卒2 小时前
计算机网络技术基础:1.计算机网络的产生与发展
网络·计算机网络
是理不是里_2 小时前
计算机网络中的SIP协议是什么?
网络
风掣长空2 小时前
应用层协议HTTP
网络·网络协议·http
烁月_o93 小时前
网络安全之漏洞
linux·网络·安全·web安全·信息与通信
luoganttcc3 小时前
FPGA有哪些优质的带源码的IP开源网站
网络协议·tcp/ip·fpga开发
19999er4 小时前
域名信息收集(小迪网络安全笔记~
网络·笔记·安全·web安全·网络安全