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;
}