392 lines
12 KiB
C++
392 lines
12 KiB
C++
|
|
#include <iostream>
|
|||
|
|
#include <thread>
|
|||
|
|
#include <chrono>
|
|||
|
|
#include <atomic>
|
|||
|
|
#include <string>
|
|||
|
|
#include <iomanip>
|
|||
|
|
#include <ctime>
|
|||
|
|
#include <sstream>
|
|||
|
|
#include <mutex>
|
|||
|
|
|
|||
|
|
// 包含TCPClient头文件
|
|||
|
|
#include "IVrTCPClient.h"
|
|||
|
|
|
|||
|
|
class TCPClientTest
|
|||
|
|
{
|
|||
|
|
private:
|
|||
|
|
IVrTCPClient* m_pClient;
|
|||
|
|
std::atomic<bool> m_bRunning;
|
|||
|
|
std::atomic<bool> m_bConnected;
|
|||
|
|
std::string m_sServerIP;
|
|||
|
|
int m_nPort;
|
|||
|
|
std::atomic<int> m_nRecvCount;
|
|||
|
|
std::atomic<int> m_nSendCount;
|
|||
|
|
std::atomic<size_t> m_nTotalRecvBytes;
|
|||
|
|
std::thread m_sendThread;
|
|||
|
|
std::thread m_statsThread;
|
|||
|
|
std::mutex m_consoleMutex;
|
|||
|
|
std::chrono::steady_clock::time_point m_startTime;
|
|||
|
|
|
|||
|
|
// 测试配置
|
|||
|
|
bool m_bAutoSend; // 是否自动发送测试数据
|
|||
|
|
int m_nSendInterval; // 发送间隔(毫秒)
|
|||
|
|
int m_nSendSize; // 每次发送的数据大小(字节)
|
|||
|
|
|
|||
|
|
public:
|
|||
|
|
/**
|
|||
|
|
* @brief 构造函数
|
|||
|
|
* @param serverIP 服务器IP地址
|
|||
|
|
* @param port 服务器端口
|
|||
|
|
* @param autoSend 是否自动发送测试数据
|
|||
|
|
* @param sendInterval 发送间隔(毫秒)
|
|||
|
|
* @param sendSize 每次发送的数据大小(字节)
|
|||
|
|
*/
|
|||
|
|
TCPClientTest(const std::string& serverIP, int port, bool autoSend = false,
|
|||
|
|
int sendInterval = 1000, int sendSize = 64)
|
|||
|
|
: m_pClient(nullptr), m_bRunning(false), m_bConnected(false),
|
|||
|
|
m_sServerIP(serverIP), m_nPort(port), m_nRecvCount(0),
|
|||
|
|
m_nSendCount(0), m_nTotalRecvBytes(0),
|
|||
|
|
m_bAutoSend(autoSend), m_nSendInterval(sendInterval), m_nSendSize(sendSize)
|
|||
|
|
{
|
|||
|
|
// 创建TCPClient实例
|
|||
|
|
m_pClient = IVrTCPClient::CreateInstance();
|
|||
|
|
if (!m_pClient) {
|
|||
|
|
std::cerr << "❌ Failed to create TCPClient instance" << std::endl;
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* @brief 析构函数
|
|||
|
|
*/
|
|||
|
|
~TCPClientTest()
|
|||
|
|
{
|
|||
|
|
Stop();
|
|||
|
|
|
|||
|
|
// 等待线程结束
|
|||
|
|
if (m_sendThread.joinable()) {
|
|||
|
|
m_sendThread.join();
|
|||
|
|
}
|
|||
|
|
if (m_statsThread.joinable()) {
|
|||
|
|
m_statsThread.join();
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
if (m_pClient) {
|
|||
|
|
m_pClient->CloseDevice();
|
|||
|
|
IVrTCPClient::DestroyInstance(m_pClient);
|
|||
|
|
m_pClient = nullptr;
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* @brief 初始化客户端
|
|||
|
|
* @return true表示成功,false表示失败
|
|||
|
|
*/
|
|||
|
|
bool Initialize()
|
|||
|
|
{
|
|||
|
|
if (!m_pClient) {
|
|||
|
|
return false;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
PrintLog("🔌 Connecting to " + m_sServerIP + ":" + std::to_string(m_nPort));
|
|||
|
|
|
|||
|
|
// 连接设备
|
|||
|
|
int result = m_pClient->LinkDevice(m_sServerIP, m_nPort, true,
|
|||
|
|
[this](IVrTCPClient* pClient, bool connected, void* pParam) {
|
|||
|
|
this->OnConnectionEvent(pClient, connected, pParam);
|
|||
|
|
}, nullptr);
|
|||
|
|
|
|||
|
|
if (result != 0) {
|
|||
|
|
PrintLog("❌ Failed to connect to server, error code: " + std::to_string(result));
|
|||
|
|
return false;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 启动工作线程
|
|||
|
|
result = m_pClient->StartWork(
|
|||
|
|
[this](IVrTCPClient* pClient, const char* pData, const int nLen, void* pParam) {
|
|||
|
|
this->OnDataReceived(pClient, pData, nLen, pParam);
|
|||
|
|
}, nullptr);
|
|||
|
|
|
|||
|
|
if (result != 0) {
|
|||
|
|
PrintLog("❌ Failed to start client work thread, error code: " + std::to_string(result));
|
|||
|
|
return false;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
PrintLog("✅ Client initialized successfully");
|
|||
|
|
return true;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* @brief 启动客户端
|
|||
|
|
* @return true表示成功,false表示失败
|
|||
|
|
*/
|
|||
|
|
bool Start()
|
|||
|
|
{
|
|||
|
|
m_bRunning = true;
|
|||
|
|
m_startTime = std::chrono::steady_clock::now();
|
|||
|
|
|
|||
|
|
// 启动统计线程
|
|||
|
|
m_statsThread = std::thread(&TCPClientTest::StatsLoop, this);
|
|||
|
|
|
|||
|
|
// 如果启用自动发送,启动发送线程
|
|||
|
|
if (m_bAutoSend) {
|
|||
|
|
m_sendThread = std::thread(&TCPClientTest::SendDataLoop, this);
|
|||
|
|
PrintLog("📤 Auto-send enabled: " + std::to_string(m_nSendSize) +
|
|||
|
|
" bytes every " + std::to_string(m_nSendInterval) + " ms");
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
PrintLog("🚀 Client started, waiting for data...");
|
|||
|
|
return true;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* @brief 停止客户端
|
|||
|
|
*/
|
|||
|
|
void Stop()
|
|||
|
|
{
|
|||
|
|
m_bRunning = false;
|
|||
|
|
|
|||
|
|
// 等待线程结束
|
|||
|
|
if (m_sendThread.joinable()) {
|
|||
|
|
m_sendThread.join();
|
|||
|
|
}
|
|||
|
|
if (m_statsThread.joinable()) {
|
|||
|
|
m_statsThread.join();
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
PrintLog("🛑 Client stopped");
|
|||
|
|
PrintFinalStats();
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* @brief 获取接收计数
|
|||
|
|
* @return 接收的消息数量
|
|||
|
|
*/
|
|||
|
|
int GetRecvCount() const
|
|||
|
|
{
|
|||
|
|
return m_nRecvCount.load();
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* @brief 获取发送计数
|
|||
|
|
* @return 发送的消息数量
|
|||
|
|
*/
|
|||
|
|
int GetSendCount() const
|
|||
|
|
{
|
|||
|
|
return m_nSendCount.load();
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* @brief 获取连接状态
|
|||
|
|
* @return true表示已连接,false表示未连接
|
|||
|
|
*/
|
|||
|
|
bool IsConnected() const
|
|||
|
|
{
|
|||
|
|
return m_bConnected.load();
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* @brief 手动发送测试数据
|
|||
|
|
* @param message 要发送的消息
|
|||
|
|
* @return true表示成功,false表示失败
|
|||
|
|
*/
|
|||
|
|
bool SendTestData(const std::string& message)
|
|||
|
|
{
|
|||
|
|
if (!m_pClient || !m_bConnected) {
|
|||
|
|
PrintLog("⚠️ Cannot send: not connected");
|
|||
|
|
return false;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
bool result = m_pClient->SendData(message.c_str(), static_cast<int>(message.length()));
|
|||
|
|
if (result) {
|
|||
|
|
m_nSendCount++;
|
|||
|
|
PrintLog("📤 Sent: " + message + " (" + std::to_string(message.length()) + " bytes)");
|
|||
|
|
} else {
|
|||
|
|
PrintLog("❌ Failed to send data");
|
|||
|
|
}
|
|||
|
|
return result;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
private:
|
|||
|
|
/**
|
|||
|
|
* @brief 连接状态回调函数
|
|||
|
|
* @param pClient 客户端指针
|
|||
|
|
* @param connected 是否连接
|
|||
|
|
* @param pParam 参数指针
|
|||
|
|
*/
|
|||
|
|
void OnConnectionEvent(IVrTCPClient* pClient, bool connected, void* pParam)
|
|||
|
|
{
|
|||
|
|
m_bConnected = connected;
|
|||
|
|
if (connected) {
|
|||
|
|
PrintLog("✅ Connected to server");
|
|||
|
|
} else {
|
|||
|
|
PrintLog("❌ Disconnected from server (Auto-reconnect enabled)");
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* @brief 数据接收回调函数
|
|||
|
|
* @param pClient 客户端指针
|
|||
|
|
* @param pData 数据指针
|
|||
|
|
* @param nLen 数据长度
|
|||
|
|
* @param pParam 参数指针
|
|||
|
|
*/
|
|||
|
|
void OnDataReceived(IVrTCPClient* pClient, const char* pData, const int nLen, void* pParam)
|
|||
|
|
{
|
|||
|
|
m_nRecvCount++;
|
|||
|
|
m_nTotalRecvBytes += nLen;
|
|||
|
|
|
|||
|
|
// 打印接收到的数据(限制长度)
|
|||
|
|
std::string data(pData, std::min(nLen, 100));
|
|||
|
|
if (nLen > 100) {
|
|||
|
|
data += "...";
|
|||
|
|
}
|
|||
|
|
PrintLog("📥 Received #" + std::to_string(m_nRecvCount.load()) + ": [" + std::to_string(nLen) + " bytes] " + data);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* @brief 自动发送数据循环
|
|||
|
|
*/
|
|||
|
|
void SendDataLoop()
|
|||
|
|
{
|
|||
|
|
int counter = 0;
|
|||
|
|
while (m_bRunning) {
|
|||
|
|
if (m_bConnected && m_pClient) {
|
|||
|
|
// 构造测试数据
|
|||
|
|
std::string message = "Client-Test-" + std::to_string(++counter);
|
|||
|
|
// 填充到指定大小
|
|||
|
|
if (message.length() < static_cast<size_t>(m_nSendSize)) {
|
|||
|
|
message.append(m_nSendSize - message.length(), 'X');
|
|||
|
|
} else if (message.length() > static_cast<size_t>(m_nSendSize)) {
|
|||
|
|
message.resize(m_nSendSize);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
bool result = m_pClient->SendData(message.c_str(), static_cast<int>(message.length()));
|
|||
|
|
if (result) {
|
|||
|
|
m_nSendCount++;
|
|||
|
|
} else {
|
|||
|
|
PrintLog("❌ Failed to send data");
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
std::this_thread::sleep_for(std::chrono::milliseconds(m_nSendInterval));
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* @brief 统计信息循环打印
|
|||
|
|
*/
|
|||
|
|
void StatsLoop()
|
|||
|
|
{
|
|||
|
|
while (m_bRunning) {
|
|||
|
|
std::this_thread::sleep_for(std::chrono::seconds(5));
|
|||
|
|
if (m_bRunning) {
|
|||
|
|
PrintStats();
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* @brief 打印当前统计信息
|
|||
|
|
*/
|
|||
|
|
void PrintStats()
|
|||
|
|
{
|
|||
|
|
auto now = std::chrono::steady_clock::now();
|
|||
|
|
auto duration = std::chrono::duration_cast<std::chrono::seconds>(now - m_startTime).count();
|
|||
|
|
if (duration == 0) duration = 1; // 避免除零
|
|||
|
|
|
|||
|
|
double recvRate = static_cast<double>(m_nRecvCount.load()) / duration;
|
|||
|
|
double sendRate = static_cast<double>(m_nSendCount.load()) / duration;
|
|||
|
|
double bandwidth = static_cast<double>(m_nTotalRecvBytes.load()) / duration / 1024.0; // KB/s
|
|||
|
|
|
|||
|
|
std::ostringstream oss;
|
|||
|
|
oss << "\n📊 Statistics (Running " << duration << "s)"
|
|||
|
|
<< "\n Connected: " << (m_bConnected ? "Yes" : "No")
|
|||
|
|
<< "\n Received: " << m_nRecvCount.load() << " msgs (" << std::fixed << std::setprecision(2) << recvRate << " msg/s)"
|
|||
|
|
<< "\n Sent: " << m_nSendCount.load() << " msgs (" << std::fixed << std::setprecision(2) << sendRate << " msg/s)"
|
|||
|
|
<< "\n Bandwidth: " << std::fixed << std::setprecision(2) << bandwidth << " KB/s"
|
|||
|
|
<< "\n Total Recv: " << m_nTotalRecvBytes.load() << " bytes";
|
|||
|
|
|
|||
|
|
PrintLog(oss.str());
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* @brief 打印最终统计信息
|
|||
|
|
*/
|
|||
|
|
void PrintFinalStats()
|
|||
|
|
{
|
|||
|
|
auto now = std::chrono::steady_clock::now();
|
|||
|
|
auto duration = std::chrono::duration_cast<std::chrono::seconds>(now - m_startTime).count();
|
|||
|
|
if (duration == 0) duration = 1;
|
|||
|
|
|
|||
|
|
std::ostringstream oss;
|
|||
|
|
oss << "\n📈 Final Statistics:"
|
|||
|
|
<< "\n Total Runtime: " << duration << " seconds"
|
|||
|
|
<< "\n Total Received: " << m_nRecvCount.load() << " messages"
|
|||
|
|
<< "\n Total Sent: " << m_nSendCount.load() << " messages"
|
|||
|
|
<< "\n Total Bytes Received: " << m_nTotalRecvBytes.load() << " bytes"
|
|||
|
|
<< "\n Average Receive Rate: " << std::fixed << std::setprecision(2)
|
|||
|
|
<< (static_cast<double>(m_nRecvCount.load()) / duration) << " msg/s"
|
|||
|
|
<< "\n Average Send Rate: " << std::fixed << std::setprecision(2)
|
|||
|
|
<< (static_cast<double>(m_nSendCount.load()) / duration) << " msg/s";
|
|||
|
|
|
|||
|
|
PrintLog(oss.str());
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* @brief 线程安全的日志打印
|
|||
|
|
* @param message 日志消息
|
|||
|
|
*/
|
|||
|
|
void PrintLog(const std::string& message)
|
|||
|
|
{
|
|||
|
|
std::lock_guard<std::mutex> lock(m_consoleMutex);
|
|||
|
|
auto now = std::time(nullptr);
|
|||
|
|
char timeStr[20];
|
|||
|
|
std::strftime(timeStr, sizeof(timeStr), "%H:%M:%S", std::localtime(&now));
|
|||
|
|
std::cout << "[" << timeStr << "] " << message << std::endl;
|
|||
|
|
}
|
|||
|
|
};
|
|||
|
|
|
|||
|
|
int main(int argc, char* argv[])
|
|||
|
|
{
|
|||
|
|
std::cout << "TCP Client Test Program" << std::endl;
|
|||
|
|
std::cout << "=======================" << std::endl;
|
|||
|
|
|
|||
|
|
// 默认连接参数
|
|||
|
|
std::string serverIP = "127.0.0.1";
|
|||
|
|
int port = 8080;
|
|||
|
|
|
|||
|
|
if (argc > 1) {
|
|||
|
|
serverIP = argv[1];
|
|||
|
|
}
|
|||
|
|
if (argc > 2) {
|
|||
|
|
port = std::stoi(argv[2]);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
TCPClientTest clientTest(serverIP, port);
|
|||
|
|
|
|||
|
|
// 初始化客户端
|
|||
|
|
if (!clientTest.Initialize()) {
|
|||
|
|
std::cerr << "Failed to initialize client" << std::endl;
|
|||
|
|
return -1;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 启动客户端
|
|||
|
|
if (!clientTest.Start()) {
|
|||
|
|
std::cerr << "Failed to start client" << std::endl;
|
|||
|
|
return -1;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 运行一段时间或等待用户输入
|
|||
|
|
std::cout << "Client is running. Press Enter to stop..." << std::endl;
|
|||
|
|
while(true){
|
|||
|
|
std::cin.get();
|
|||
|
|
std::this_thread::sleep_for(std::chrono::seconds(10));
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 停止客户端
|
|||
|
|
clientTest.Stop();
|
|||
|
|
|
|||
|
|
std::cout << "TCP Client Test completed!" << std::endl;
|
|||
|
|
return 0;
|
|||
|
|
}
|