windows TCP client demo

TcpClient.h

#ifndef TCPCLIENT_H
#define TCPCLIENT_H

#include <iostream>
#include <winsock2.h>
#include <ws2tcpip.h>
#include <vector>

// 引入静态链接库
#pragma comment(lib, "ws2_32.lib")

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

    // 连接到指定服务器
    bool connectToServer(const char* serverIp, int port);

    // 发送数据,非阻塞方式
    int sendData(const char* data, int dataLength);

    // 接收数据,非阻塞方式
    int receiveData(char* buffer, int bufferSize);

    // 启动心跳包机制,参数为心跳间隔时间(单位:秒)
    void startHeartbeat(int interval);

    // 停止心跳包机制
    void stopHeartbeat();

private:
    SOCKET clientSocket;
    bool isHeartbeatRunning;
    int heartbeatInterval;
    HANDLE heartbeatThreadHandle;

    // 发送心跳包的函数(需要定义为静态函数,以便在线程函数中调用)
    static DWORD WINAPI SendHeartbeat(LPVOID lpParam);

    // 辅助函数,用于非阻塞模式下完整发送数据
    int sendAllData(const char* data, int dataLength);
    // 辅助函数,用于非阻塞模式下完整接收数据
    int receiveAllData(char* buffer, int bufferSize);
};

#endif

TcpClient.cpp

#include "TcpClient.h"

// 构造函数,初始化客户端套接字
TcpClient::TcpClient() : clientSocket(INVALID_SOCKET), isHeartbeatRunning(false), heartbeatInterval(0),
heartbeatThreadHandle(NULL) {
    WSADATA wsaData;
    int result = WSAStartup(MAKEWORD(2, 2), &wsaData);
    if (result != 0) {
        std::cerr << "WSAStartup failed: " << result << std::endl;
    }
}

// 析构函数,关闭套接字并清理WinSock环境
TcpClient::~TcpClient() {
    if (clientSocket != INVALID_SOCKET) {
        closesocket(clientSocket);
    }
    stopHeartbeat();
    WSACleanup();
}

// 连接到指定服务器
bool TcpClient::connectToServer(const char* serverIp, int port) {
    clientSocket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
    if (clientSocket == INVALID_SOCKET) {
        std::cerr << "Socket creation failed: " << WSAGetLastError() << std::endl;
        return false;
    }

    // 设置套接字为非阻塞模式
    u_long iMode = 1;
    int result = ioctlsocket(clientSocket, FIONBIO, &iMode);
    if (result == SOCKET_ERROR) {
        std::cerr << "ioctlsocket failed: " << WSAGetLastError() << std::endl;
        closesocket(clientSocket);
        return false;
    }

    sockaddr_in serverAddr;
    serverAddr.sin_family = AF_INET;
    serverAddr.sin_port = htons(port);
    result = inet_pton(AF_INET, serverIp, &serverAddr.sin_addr);
    if (result <= 0) {
        std::cerr << "Invalid server IP address" << std::endl;
        closesocket(clientSocket);
        return false;
    }

    result = ::connect(clientSocket, (sockaddr*)&serverAddr, sizeof(serverAddr));
    if (result == SOCKET_ERROR) {
        if (WSAGetLastError() != WSAEWOULDBLOCK) {
            std::cerr << "Connect to server failed: " << WSAGetLastError() << std::endl;
            closesocket(clientSocket);
            return false;
        }
    }

    // 使用select等待连接完成
    fd_set writefds;
    FD_ZERO(&writefds);
    FD_SET(clientSocket, &writefds);
    struct timeval timeout;
    timeout.tv_sec = 5;  // 设置超时时间为5秒,可根据实际调整
    timeout.tv_usec = 0;
    result = select(clientSocket + 1, NULL, &writefds, NULL, &timeout);
    if (result == SOCKET_ERROR) {
        std::cerr << "Select failed while waiting for connection: " << WSAGetLastError() << std::endl;
        closesocket(clientSocket);
        return false;
    }
    else if (result == 0) {
        std::cerr << "Connection timeout" << std::endl;
        closesocket(clientSocket);
        return false;
    }

    return true;
}

// 发送数据,非阻塞方式
int TcpClient::sendData(const char* data, int dataLength) {
    if (clientSocket == INVALID_SOCKET) {
        std::cerr << "Client socket is not valid, cannot send data" << std::endl;
        return SOCKET_ERROR;
    }
    return sendAllData(data, dataLength);
}

// 接收数据,非阻塞方式
int TcpClient::receiveData(char* buffer, int bufferSize) {
    if (clientSocket == INVALID_SOCKET) {
        std::cerr << "Client socket is not valid, cannot receive data" << std::endl;
        return SOCKET_ERROR;
    }
    return receiveAllData(buffer, bufferSize);
}

// 启动心跳包机制,参数为心跳间隔时间(单位:秒)
void TcpClient::startHeartbeat(int interval) {
    if (isHeartbeatRunning) {
        return;
    }
    isHeartbeatRunning = true;
    heartbeatInterval = interval;
    // 创建线程,传入当前对象指针作为参数,线程启动函数为 SendHeartbeat
    heartbeatThreadHandle = CreateThread(NULL, 0, SendHeartbeat, this, 0, NULL);
    if (heartbeatThreadHandle == NULL) {
        isHeartbeatRunning = false;
        std::cerr << "Create heartbeat thread failed: " << GetLastError() << std::endl;
    }
}

// 停止心跳包机制
void TcpClient::stopHeartbeat() {
    isHeartbeatRunning = false;
    if (heartbeatThreadHandle != NULL) {
        // 等待线程结束
        WaitForSingleObject(heartbeatThreadHandle, INFINITE);
        CloseHandle(heartbeatThreadHandle);
        heartbeatThreadHandle = NULL;
    }
}

// 发送心跳包的函数(静态成员函数)
DWORD WINAPI TcpClient::SendHeartbeat(LPVOID lpParam) {
    TcpClient* client = static_cast<TcpClient*>(lpParam);
    const char* heartbeatData = "HEARTBEAT";  // 简单的心跳包内容,可自定义
    int dataLength = strlen(heartbeatData);
    while (client->isHeartbeatRunning)
    {
        if (client->clientSocket != INVALID_SOCKET)
        {
            int sentBytes = client->sendAllData(heartbeatData, dataLength);
            if (sentBytes == SOCKET_ERROR)
            {
                // 发送心跳包失败,可以在这里添加相应的处理逻辑,比如尝试重连等
                std::cerr << "Send heartbeat failed: " << WSAGetLastError() << std::endl;
            }
            else
            {
                // 尝试接收服务器对心跳包的响应
                char responseBuffer[10] = {0};
                int receivedBytes = client->receiveAllData(responseBuffer, sizeof(responseBuffer));
                if (receivedBytes <= 0) {
                    // 没收到响应或者接收出错,可认为连接可能有问题,添加相应处理逻辑
                    std::cerr << "No response for heartbeat, connection may be broken" << std::endl;
                }
                else
                {
                    std::cout << responseBuffer << std::endl;
                }
            }
        }
        // 使用 Sleep 函数替代 std::this_thread::sleep_for,单位是毫秒,所以要换算成毫秒
        Sleep(client->heartbeatInterval * 1000);
    }
    return 0;
}

// 辅助函数,用于非阻塞模式下完整发送数据
int TcpClient::sendAllData(const char* data, int dataLength) {
    int totalBytesSent = 0;
    while (totalBytesSent < dataLength) {
        int bytesSent = ::send(clientSocket, data + totalBytesSent, dataLength - totalBytesSent, 0);
        if (bytesSent == SOCKET_ERROR) {
            if (WSAGetLastError() == WSAEWOULDBLOCK) {
                // 暂时无法发送,等待下次尝试
                continue;
            }
            return SOCKET_ERROR;
        }
        totalBytesSent += bytesSent;
    }
    return totalBytesSent;
}

// 辅助函数,用于非阻塞模式下完整接收数据
int TcpClient::receiveAllData(char* buffer, int bufferSize) {
    int totalBytesReceived = 0;
    while (totalBytesReceived < bufferSize) 
    {
        int bytesReceived = ::recv(clientSocket, buffer + totalBytesReceived, bufferSize - totalBytesReceived, 0);
        if (bytesReceived == SOCKET_ERROR)
        {
            if (WSAGetLastError() == WSAEWOULDBLOCK)
            {
                // 暂时无数据可读,等待下次尝试
                continue;
            }
            return SOCKET_ERROR;
        }
        else if (bytesReceived == 0)
        {
            // 客户端关闭连接
            return totalBytesReceived;
        }
        totalBytesReceived += bytesReceived;
        //if (totalBytesReceived > 0)
        //{
        //    break;
        //}
    }
    return totalBytesReceived;
}

main.cpp

#include "TcpClient.h"
#include <string>



int main() {
    TcpClient client;
    if (client.connectToServer("127.0.0.1", 6001))
    {
       client.startHeartbeat(1);  // 启动心跳包机制,间隔时间为5秒
        while (1)
        {
         /*   std::string message = "Hello, server!";
            int sentBytes = client.sendData(message.c_str(), message.length());
            if (sentBytes > 0)
            {
                char buffer[1024] = {0};
                int receivedBytes = client.receiveData(buffer, sizeof(buffer));
                if (receivedBytes > 0)
                {
                    buffer[receivedBytes] = '\0';
                    std::cout << "Received from server: " << buffer << std::endl;
                }
                else
                {
                    std::cout << "Received from server: " << buffer << std::endl;
                }
            }*/
        }
      

        client.stopHeartbeat();  // 停止心跳包机制
    }

    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值