// HeartbeatClient.cs // Namhyeon Go // https://github.com/gnh1201/welsonjs using Grpc.Core; using System.Threading.Tasks; using System; using System.Management; using System.ServiceProcess; using Grpc.Net.Client; using Grpc.Net.Client.Web; using System.Net.Http; using Microsoft.Extensions.Logging; namespace WelsonJS.Service { public class HeartbeatClient { private readonly HeartbeatService.HeartbeatServiceClient _client; private ILogger logger; private readonly GrpcChannel _channel; private int HeartbeatInterval; private ServiceMain parent; private string clientId; private string serverAddress; public HeartbeatClient(ServiceBase _parent, ILogger _logger) { parent = (ServiceMain)_parent; logger = _logger; HeartbeatInterval = int.Parse(parent.ReadSettingsValue("HEARTBEAT_INTERVAL") ?? "2000"); try { serverAddress = parent.ReadSettingsValue("GRPC_HOST"); if (String.IsNullOrEmpty(serverAddress)) { throw new Exception("The server address could not be empty."); } } catch (Exception ex) { serverAddress = "http://localhost:50051"; logger.LogInformation($"Failed to read the address because of {ex.Message}. Set default: {serverAddress}"); } var httpClientHandler = new HttpClientHandler(); var grpcWebHandler = new GrpcWebHandler(GrpcWebMode.GrpcWebText, httpClientHandler); _channel = GrpcChannel.ForAddress(serverAddress, new GrpcChannelOptions { HttpHandler = grpcWebHandler, Credentials = ChannelCredentials.Insecure }); _client = new HeartbeatService.HeartbeatServiceClient(_channel); clientId = GetSystemUUID().ToLower(); logger.LogInformation($"Use the client ID: {clientId}"); } public async Task StartHeartbeatAsync() { while (true) { var call = _client.CheckHeartbeat(); try { var request = new HeartbeatRequest { IsAlive = true }; await call.RequestStream.WriteAsync(request); await call.RequestStream.CompleteAsync(); logger.LogInformation("Sent heartbeat"); await Task.Delay(HeartbeatInterval); // Wait for HeartbeatInterval } catch (Exception ex) { logger.LogInformation("Heartbeat request stream failed: " + ex.Message); } // 서버 응답을 수신하는 작업 try { while (await call.ResponseStream.MoveNext()) { var response = call.ResponseStream.Current; logger.LogInformation("Heartbeat response received: " + response.IsAlive); } } catch (RpcException ex) { logger.LogInformation($"gRPC error: {ex.Status.Detail}"); } catch (Exception ex) { logger.LogInformation($"Unexpected error: {ex.Message}"); } finally { // 잠시 대기 후 재연결 await Task.Delay(TimeSpan.FromMilliseconds(HeartbeatInterval)); } } } public async Task StartEventListenerAsync() { var eventRequest = new FetchEventsRequest { ClientId = clientId }; while (true) { var eventCall = _client.FetchPendingEvents(eventRequest); try { while (await eventCall.ResponseStream.MoveNext()) { var response = eventCall.ResponseStream.Current; logger.LogInformation($"Received event from server: {response.EventType} with args: {string.Join(", ", response.Args)}"); } } finally { await Task.Delay(TimeSpan.FromMilliseconds(HeartbeatInterval)); } } } public async Task ShutdownAsync() { await _channel.ShutdownAsync(); } private string GetSystemUUID() { try { using (var searcher = new ManagementObjectSearcher("SELECT UUID FROM Win32_ComputerSystemProduct")) { foreach (var mo in searcher.Get()) { return mo["UUID"].ToString(); } } } catch (Exception ex) { logger.LogInformation($"An error occurred while retrieving the system UUID: {ex.Message}"); } return "UNKNOWN"; } } }