-
Notifications
You must be signed in to change notification settings - Fork 2
/
Client.cs
178 lines (147 loc) · 4.78 KB
/
Client.cs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
using System.Net;
using System.Net.Sockets;
using System.Text;
using Microsoft.Extensions.Logging;
using System.Text.Json;
namespace PimaxCrystalAdvanced.BrokenEye;
public class Client : IDisposable
{
private readonly ILogger _logger;
private TcpClient? _client;
private IPAddress? _ipAddress;
private short _port;
private readonly CancellationTokenSource _cancellationTokenSource = new();
public event Action<EyeData>? OnData;
public Client(ILogger logger)
{
_logger = logger;
}
public bool Connect(string ip, int port)
{
_client = new TcpClient();
_ipAddress = IPAddress.Parse(ip);
_port = Convert.ToInt16(port);
_logger.LogInformation($"Connecting to {ip}:{port}...");
try
{
var result = _client.ConnectAsync(_ipAddress, _port).Wait(TimeSpan.FromSeconds(3));
if (!result)
{
_logger.LogError("Failed to connect to server");
return false;
}
}
catch (Exception e)
{
_logger.LogError($"Failed to connect to server ({e.Message})");
return false;
}
_logger.LogInformation("Connected to server");
Task.Run(HandleAsyncData, _cancellationTokenSource.Token);
return true;
}
private async void HandleAsyncData()
{
var reconnectAttempts = 0;
const int maxReconnectAttempts = 50;
while (true)
{
// Try to reconnect every 5 seconds
try
{
if (reconnectAttempts > maxReconnectAttempts)
{
_logger.LogError(
$"Failed to reconnect to server after {maxReconnectAttempts} attempts, giving up :(");
return;
}
if (_client is not { Connected: true })
{
_logger.LogInformation("Reconnecting to server...");
_client?.Close();
_client = new TcpClient();
reconnectAttempts++;
_client.ConnectAsync(_ipAddress!, _port).Wait(TimeSpan.FromSeconds(3));
_logger.LogInformation("Reconnected to server");
reconnectAttempts = 0;
}
}
catch (Exception e)
{
_logger.LogError($"Failed to reconnect to server, retrying in 5 seconds... ({e.Message})");
await Task.Delay(TimeSpan.FromSeconds(5));
}
if (_client == null)
{
continue;
}
// Process stream
try
{
var stream = _client.GetStream();
stream.ReadTimeout = 10000;
stream.WriteTimeout = 10000;
// Request advanced data stream
const byte requestId = 0x00;
var request = new Memory<byte>(new[] { requestId });
await stream.WriteAsync(request);
while (true)
{
await ReadData(stream, requestId);
}
}
catch (Exception e)
{
_logger.LogError($"Failed to read data from server ({e.Message})");
}
}
}
private async Task ReadData(Stream stream, byte requestId)
{
// Read id(1 byte) and length (4 bytes big endian)
var buffer = new byte[5];
var read = await stream.ReadAsync(buffer);
if (read != buffer.Length)
{
throw new Exception("Failed to read data");
}
var id = buffer[0];
if (id != requestId)
{
throw new Exception("Invalid response id");
}
var lengthBytes = buffer[1..];
var length = BitConverter.ToUInt32(lengthBytes);
var data = new byte[length];
read = await stream.ReadAsync(data);
if (read != length)
{
throw new Exception("Failed to read data");
}
var jsonString = Encoding.UTF8.GetString(data);
try
{
EyeData? eyeDataValue = JsonSerializer.Deserialize<EyeData>(jsonString);
if (!eyeDataValue.HasValue)
{
throw new Exception("Failed to deserialize data");
}
var eyeData = eyeDataValue.Value;
OnData?.Invoke(eyeData);
}
catch (Exception e)
{
_logger.LogError($"Failed to deserialize data ({e.Message}): {jsonString}");
throw;
}
}
public void Dispose()
{
_cancellationTokenSource.Cancel();
_client?.Close();
}
public bool IsConnected()
{
return _client?.Connected ?? false;
}
}