256 lines
8.0 KiB
C++
256 lines
8.0 KiB
C++
// NetClient.h
|
||
#pragma once
|
||
|
||
#include <Windows.h>
|
||
#include <vcclr.h>
|
||
|
||
#using <System.dll>
|
||
#using <System.Net.dll>
|
||
|
||
using namespace System;
|
||
using namespace System::Net;
|
||
using namespace System::Net::Sockets;
|
||
using namespace System::Runtime::InteropServices;
|
||
|
||
namespace DroneSimulator
|
||
{
|
||
public ref class NetClient
|
||
{
|
||
public:
|
||
// Вложенный класс для данных подключения
|
||
ref class ConnectData
|
||
{
|
||
public:
|
||
property bool Connect;
|
||
property Socket^ Server;
|
||
};
|
||
|
||
// Вложенный класс для данных приема
|
||
ref class ReceiveData
|
||
{
|
||
public:
|
||
property array<Byte>^ Buffer;
|
||
property int Size;
|
||
property Socket^ Server;
|
||
};
|
||
|
||
// Состояния клиента
|
||
enum class ClientState { Error, Connected, Stop };
|
||
|
||
// Делегат для callback-функций
|
||
delegate void ClientCallback(Object^ o);
|
||
|
||
NetClient()
|
||
{
|
||
Connected = false;
|
||
ServerSocket = nullptr;
|
||
DataServer = gcnew ServerData();
|
||
}
|
||
|
||
~NetClient()
|
||
{
|
||
// Вызываем Close() для корректного закрытия соединения
|
||
Close();
|
||
|
||
// Дополнительная очистка ресурсов
|
||
if (DataServer != nullptr)
|
||
{
|
||
// Очищаем буфер данных
|
||
DataServer->buffer = nullptr;
|
||
DataServer = nullptr;
|
||
}
|
||
|
||
// Обнуляем callback-делегаты
|
||
ConnectionCallback = nullptr;
|
||
ReceiveCallback = nullptr;
|
||
}
|
||
|
||
ClientState Connect(String^ addr, int port, ClientCallback^ connectionCallback, ClientCallback^ receiveCallback)
|
||
{
|
||
if (Connected)
|
||
{
|
||
Disconnect();
|
||
return ClientState::Stop;
|
||
}
|
||
|
||
ConnectionCallback = connectionCallback;
|
||
ReceiveCallback = receiveCallback;
|
||
|
||
try
|
||
{
|
||
IPEndPoint^ ep = gcnew IPEndPoint(IPAddress::Parse(addr), port);
|
||
ServerSocket = gcnew Socket(AddressFamily::InterNetwork, SocketType::Stream, ProtocolType::Tcp);
|
||
ServerSocket->Connect(ep);
|
||
|
||
Connected = true;
|
||
|
||
// Уведомление о подключении
|
||
ConnectData^ connectData = gcnew ConnectData();
|
||
connectData->Connect = true;
|
||
connectData->Server = ServerSocket;
|
||
ConnectionCallback(connectData);
|
||
|
||
// Начало приема данных
|
||
ReceiveData^ receiveData = gcnew ReceiveData();
|
||
receiveData->Buffer = DataServer->buffer;
|
||
receiveData->Size = ServerData::size;
|
||
receiveData->Server = ServerSocket;
|
||
|
||
ServerSocket->BeginReceive(DataServer->buffer, 0, ServerData::size, SocketFlags::None, gcnew AsyncCallback(this, &NetClient::ReadCallback), receiveData);
|
||
|
||
return ClientState::Connected;
|
||
}
|
||
catch (Exception^)
|
||
{
|
||
if (ServerSocket != nullptr)
|
||
{
|
||
ServerSocket->Close();
|
||
}
|
||
return ClientState::Error;
|
||
}
|
||
}
|
||
|
||
void Close()
|
||
{
|
||
Disconnect();
|
||
}
|
||
|
||
private:
|
||
ref class ServerData
|
||
{
|
||
public:
|
||
static const int size = 1024;
|
||
array<Byte>^ buffer = gcnew array<Byte>(size);
|
||
};
|
||
|
||
bool Connected;
|
||
Socket^ ServerSocket;
|
||
ServerData^ DataServer;
|
||
ClientCallback^ ConnectionCallback;
|
||
ClientCallback^ ReceiveCallback;
|
||
|
||
void Disconnect()
|
||
{
|
||
if (ServerSocket != nullptr)
|
||
{
|
||
try { ServerSocket->Shutdown(SocketShutdown::Both); }
|
||
catch (...) {}
|
||
ServerSocket->Close();
|
||
ServerSocket = nullptr;
|
||
}
|
||
Connected = false;
|
||
}
|
||
|
||
void ReadCallback(IAsyncResult^ ar)
|
||
{
|
||
try
|
||
{
|
||
// Проверка на null входного параметра
|
||
if (ar == nullptr) return;
|
||
|
||
// Безопасное приведение типа
|
||
ReceiveData^ cd = dynamic_cast<ReceiveData^>(ar->AsyncState);
|
||
if (cd == nullptr || ServerSocket == nullptr || !ServerSocket->Connected)
|
||
{
|
||
Disconnect();
|
||
return;
|
||
}
|
||
|
||
int bytes = 0;
|
||
try
|
||
{
|
||
bytes = ServerSocket->EndReceive(ar);
|
||
}
|
||
catch (ObjectDisposedException^)
|
||
{
|
||
// Сокет был закрыт
|
||
Disconnect();
|
||
return;
|
||
}
|
||
catch (SocketException^ ex)
|
||
{
|
||
// Логирование ошибки сокета
|
||
System::Diagnostics::Debug::WriteLine(
|
||
"Socket receive error: " + ex->Message);
|
||
Disconnect();
|
||
return;
|
||
}
|
||
catch (...)
|
||
{
|
||
Disconnect();
|
||
return;
|
||
}
|
||
|
||
// Проверка на разрыв соединения
|
||
if (bytes == 0)
|
||
{
|
||
Disconnect();
|
||
|
||
if (ConnectionCallback != nullptr)
|
||
{
|
||
ConnectData^ disconnectData = gcnew ConnectData();
|
||
disconnectData->Connect = false;
|
||
disconnectData->Server = nullptr;
|
||
ConnectionCallback(disconnectData);
|
||
}
|
||
return;
|
||
}
|
||
|
||
// Вызов callback получения данных
|
||
if (ReceiveCallback != nullptr && cd->Buffer != nullptr)
|
||
{
|
||
try
|
||
{
|
||
ReceiveCallback(cd);
|
||
}
|
||
catch (Exception^ ex)
|
||
{
|
||
System::Diagnostics::Debug::WriteLine(
|
||
"ReceiveCallback error: " + ex->Message);
|
||
}
|
||
}
|
||
|
||
// Продолжаем прием данных
|
||
if (ServerSocket != nullptr && ServerSocket->Connected)
|
||
{
|
||
try
|
||
{
|
||
IAsyncResult^ result = ServerSocket->BeginReceive(
|
||
cd->Buffer,
|
||
0,
|
||
ServerData::size,
|
||
SocketFlags::None,
|
||
gcnew AsyncCallback(this, &NetClient::ReadCallback),
|
||
cd);
|
||
|
||
// Проверка на ошибку асинхронной операции
|
||
if (result == nullptr)
|
||
{
|
||
Disconnect();
|
||
}
|
||
}
|
||
catch (ObjectDisposedException^)
|
||
{
|
||
Disconnect();
|
||
}
|
||
catch (SocketException^ ex)
|
||
{
|
||
System::Diagnostics::Debug::WriteLine(
|
||
"BeginReceive error: " + ex->Message);
|
||
Disconnect();
|
||
}
|
||
catch (...)
|
||
{
|
||
Disconnect();
|
||
}
|
||
}
|
||
}
|
||
catch (Exception^ ex)
|
||
{
|
||
System::Diagnostics::Debug::WriteLine(
|
||
"ReadCallback general error: " + ex->Message);
|
||
Disconnect();
|
||
}
|
||
}
|
||
};
|
||
} |