Files
HDRobotics/Assets/Scripts/Network/SingleUdpClient.cs

220 lines
7.0 KiB
C#

using System;
using System.Collections.Generic;
using System.Net;
using System.Net.Sockets;
using System.Text;
using System.Threading;
using Newtonsoft.Json;
using System.Threading.Tasks;
namespace UdpClientLib
{
public class SingleUdpClient : NetworkClientBase, IDisposable
{
private readonly UdpClient _udpClient;
private readonly Queue<Func<Task>> _sendQueue;
private readonly SemaphoreSlim _sendSemaphore;
private bool _disposed;
private byte _counter = 1; // 1 ~ 255 범위의 카운터
public string jsonResponse;
public SingleUdpClient(string name, string serverIp, int serverPort)
{
Name = name;
_serverEndPoint = new IPEndPoint(IPAddress.Parse(serverIp), serverPort);
_sendQueue = new Queue<Func<Task>>();
_sendSemaphore = new SemaphoreSlim(1, 1);
_udpClient = new UdpClient();
Console.WriteLine($"[{Name}] UDP 클라이언트 생성, 서버: {_serverEndPoint}");
}
public async Task RunAsync(CancellationToken cancellationToken)
{
Console.WriteLine($"[{Name}] 클라이언트 Task 시작");
try
{
while (!cancellationToken.IsCancellationRequested)
{
await ProcessSendQueueAsync();
await CheckForIncomingMessages();
await Task.Delay(10, cancellationToken);
}
}
catch (OperationCanceledException)
{
Console.WriteLine($"[{Name}] 클라이언트 Task 정상 종료");
}
catch (Exception ex)
{
Console.WriteLine($"[{Name}] 클라이언트 Task 오류: {ex.Message}");
}
}
private async Task ProcessSendQueueAsync()
{
await _sendSemaphore.WaitAsync();
try
{
while (_sendQueue.Count > 0)
{
var sendAction = _sendQueue.Dequeue();
await sendAction();
}
}
finally
{
_sendSemaphore.Release();
}
}
private async Task CheckForIncomingMessages()
{
try
{
if (_udpClient.Available > 0)
{
var result = await _udpClient.ReceiveAsync();
jsonResponse = Encoding.UTF8.GetString(result.Buffer);
Console.WriteLine($"[{Name}] 수신: \"{jsonResponse}\" (from {result.RemoteEndPoint})");
}
}
catch (Exception ex)
{
Console.WriteLine($"[{Name}] 수신 오류: {ex.Message}");
}
}
public Task SendMessageAsync(string message)
{
var tcs = new TaskCompletionSource<bool>();
_sendQueue.Enqueue(async () =>
{
try
{
byte[] data = Encoding.UTF8.GetBytes(message);
await _udpClient.SendAsync(data, data.Length, _serverEndPoint);
Console.WriteLine($"[{Name}] 메시지 전송: \"{message}\"");
tcs.SetResult(true);
}
catch (Exception ex)
{
Console.WriteLine($"[{Name}] 전송 실패: {ex.Message}");
tcs.SetException(ex);
}
});
return tcs.Task;
}
public Task SendBytesAsync(byte[] data)
{
var tcs = new TaskCompletionSource<bool>();
_sendQueue.Enqueue(async () =>
{
try
{
String hexString = BitConverter.ToString(data).Replace("-", "");
byte[] packet = Encoding.UTF8.GetBytes(hexString);
await _udpClient.SendAsync(packet, packet.Length, _serverEndPoint);
Console.WriteLine($"[{Name}] 바이트 전송: {data.Length} bytes");
if (data.Length <= 20)
{
Console.WriteLine($"데이터: [{string.Join(", ", data)}]");
}
tcs.SetResult(true);
}
catch (Exception ex)
{
Console.WriteLine($"[{Name}] 바이트 전송 실패: {ex.Message}");
tcs.SetException(ex);
}
});
return tcs.Task;
}
public Task SendFilledBytesAsync(Dictionary<int, byte> customValues = null)
{
// 기본값: 0000000058000000000000 (12바이트)
byte[] data = { 0x00, 0x00, 0x00, 0x00, 0x58, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
data[11] = _counter; // 마지막 바이트에 더하기
if (customValues != null)
{
foreach (var kvp in customValues)
{
int index = kvp.Key;
byte value = kvp.Value;
if (index >= 0 && index < data.Length)
{
data[index] = value;
}
else
{
throw new ArgumentOutOfRangeException(nameof(customValues),
$"인덱스 {index}는 유효하지 않습니다. (0 ~ {data.Length - 1} 범위)");
}
}
}
// 카운터 증가 (255 다음엔 1로 되돌아감)
_counter++;
if (_counter > 255)
_counter = 1;
return SendBytesAsync(data);
}
// HTTP 요청 전송
public Task SendHttpRequestAsync(HttpRequest request)
{
//string httpPacket = CreateHttpRequest(request);
return SendMessageAsync($"{request.Method} {request.Path}\r\nContent-Type: application/json; charset=utf-8\r\n\r\n");
}
public Task SendGetRequestAsync(string path, Dictionary<string, string> headers = null)
=> SendHttpRequestAsync(new HttpRequest(HttpMethod.GET, path, headers));
public Task SendPostRequestAsync(string path, object body = null, Dictionary<string, string> headers = null)
{
string jsonBody;
if (body == null)
{
jsonBody = null;
}
else if (body is string str)
{
jsonBody = str;
}
else
{
jsonBody = JsonConvert.SerializeObject(body);
}
return SendHttpRequestAsync(new HttpRequest(HttpMethod.POST, path, headers, jsonBody));
}
public void Dispose()
{
if (!_disposed)
{
_udpClient?.Dispose();
_disposed = true;
Console.WriteLine($"[{Name}] UDP 클라이언트 종료");
}
}
}
}