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> _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>(); _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(); _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(); _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 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 headers = null) => SendHttpRequestAsync(new HttpRequest(HttpMethod.GET, path, headers)); public Task SendPostRequestAsync(string path, object body = null, Dictionary 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 클라이언트 종료"); } } } }