Skip to content

Commit 557c3b3

Browse files
committed
Fixes DSClient connection issues
Switches partly to a new scheme of writing the networking code, but to invasive to do now. This change was more necessary however.
1 parent 4ee43b2 commit 557c3b3

File tree

4 files changed

+192
-132
lines changed

4 files changed

+192
-132
lines changed

src/FRC.NetworkTables/DSClient.cs

Lines changed: 172 additions & 129 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,13 @@
11
using NetworkTables.Logging;
22
using System;
3-
using System.Collections.Generic;
4-
using System.Linq;
53
using System.Threading;
64
using System.Threading.Tasks;
7-
using NetworkTables.Extensions;
8-
using NetworkTables.TcpSockets;
95
using System.Net.Sockets;
106
using System.IO;
11-
using NetworkTables.Streams;
127
using System.Text;
138
using static NetworkTables.Logging.Logger;
149
using System.Net;
10+
using Nito.AsyncEx;
1511
using Nito.AsyncEx.Synchronous;
1612

1713
namespace NetworkTables
@@ -33,171 +29,218 @@ public static DsClient Instance
3329
}
3430
}
3531

36-
public void Dispose()
32+
//private IServerOverridable m_serverOverridable;
33+
34+
public DsClient(/*IServerOverridable serverOverridable*/)
3735
{
38-
Stop();
36+
//m_serverOverridable = serverOverridable;
3937
}
4038

41-
public void Start(int port)
39+
private int m_port;
40+
41+
private Task m_task;
42+
private CancellationTokenSource m_tokenSource;
43+
44+
public void Dispose()
4245
{
43-
lock (m_mutex)
44-
{
45-
m_port = port;
46-
if (m_task == null)
47-
{
48-
m_active = true;
49-
m_task = Task.Factory.StartNew(TaskMain, TaskCreationOptions.LongRunning);
50-
}
51-
}
46+
Stop();
5247
}
5348

5449
public void Stop()
5550
{
56-
m_active = false;
51+
m_tokenSource?.Cancel();
5752
m_task?.WaitAndUnwrapException();
5853
m_task = null;
54+
m_tokenSource = null;
5955
}
6056

61-
private DsClient()
57+
public void Start(int port)
6258
{
63-
59+
Interlocked.Exchange(ref m_port, port);
60+
if (m_task == null)
61+
{
62+
if (m_tokenSource == null || m_tokenSource.IsCancellationRequested)
63+
{
64+
m_tokenSource = new CancellationTokenSource();
65+
}
66+
m_task = Task.Factory.StartNew(ThreadMain, m_tokenSource.Token, TaskCreationOptions.LongRunning);
67+
}
6468
}
6569

66-
private int m_port;
67-
68-
private Task m_task;
69-
70-
private bool m_active;
71-
72-
private readonly object m_mutex = new object();
73-
private readonly AutoResetEvent m_cond = new AutoResetEvent(false);
74-
75-
NtTcpClient m_client;
76-
77-
private void TaskMain()
70+
public void ThreadMain(object token)
7871
{
79-
uint oldIp = 0;
80-
Logger logger = new Logger(); // To silence log messages
81-
logger.SetLogger(null);
8272

83-
while (m_active)
73+
if (token is CancellationToken)
8474
{
85-
int port;
86-
bool lockEntered = false;
8775
try
8876
{
89-
Monitor.Enter(m_mutex, ref lockEntered);
90-
m_cond.WaitTimeout(m_mutex, ref lockEntered, TimeSpan.FromMilliseconds(500), () => !m_active);
91-
port = m_port;
77+
AsyncContext.Run(async () =>
78+
{
79+
await ThreadMainAsync((CancellationToken)token);
80+
});
9281
}
93-
finally
82+
catch (OperationCanceledException)
9483
{
95-
if (lockEntered) Monitor.Exit(m_mutex);
84+
// Ignore operation cancelled
9685
}
86+
}
87+
}
9788

98-
if (!m_active) goto done;
99-
100-
m_client = TcpConnector.Connect("127.0.0.1", 1742, logger, 1);
101-
if (!m_active) goto done;
102-
if (m_client == null) continue;
103-
104-
Logger.Debug3(Logger.Instance, "connected to DS");
105-
Stream stream = m_client.GetStream();
106-
while (m_active && stream.CanRead)
89+
public async Task ThreadMainAsync(CancellationToken token)
90+
{
91+
uint oldIp = 0;
92+
try
93+
{
94+
while (!token.IsCancellationRequested)
10795
{
108-
StringBuilder json = new StringBuilder(128);
109-
byte ch = 0;
110-
do
111-
{
112-
bool success = stream.ReceiveByte(out ch);
113-
if (!success) break;
114-
if (!m_active) goto done;
115-
} while (ch != (byte)'{');
116-
json.Append('{');
117-
118-
if (!stream.CanRead)
119-
{
120-
m_client = null;
121-
break;
122-
}
96+
int port;
12397

124-
// Read characters until }
125-
do
98+
using (TcpClient client = new TcpClient())
12699
{
127-
bool success = stream.ReceiveByte(out ch);
128-
if (!success) break;
129-
if (!m_active) goto done;
130-
json.Append((char)ch);
131-
} while (ch != (byte)'}');
100+
Task connection = client.ConnectAsync("127.0.0.1", 1742);
101+
Task delayTask = Task.Delay(2000, token);
132102

133-
if (!stream.CanRead)
134-
{
135-
m_client = null;
136-
break;
137-
}
103+
try
104+
{
105+
var finished = await Task.WhenAny(connection, delayTask);
106+
if (finished == delayTask)
107+
{
108+
// Timed Out
109+
continue;
110+
}
111+
if (!finished.IsCompleted || finished.IsFaulted || finished.IsCanceled)
112+
{
113+
continue;
114+
}
115+
}
138116

139-
string jsonString = json.ToString();
140-
Debug3(Logger.Instance, $"json={jsonString}");
141-
142-
// Look for "robotIP":12345, and get 12345 portion
143-
int pos = jsonString.IndexOf("\"robotIP\"");
144-
if (pos < 0) continue; // could not find?
145-
pos += 9;
146-
pos = jsonString.IndexOf(':', pos);
147-
if (pos < 0) continue; // could not find?
148-
// Find first not of
149-
int endpos = -1;
150-
for (int i = pos + 1; i < jsonString.Length; i++)
151-
{
152-
if (jsonString[i] < '0' || jsonString[i] > '9')
117+
catch (OperationCanceledException)
153118
{
154-
endpos = i;
155-
break;
119+
goto done;
156120
}
157-
}
158-
string ipString = jsonString.Substring(pos + 1, endpos - (pos + 1));
159-
Debug3(Logger.Instance, $"found robotIP={ipString}");
160121

161-
// Parse into number
162-
uint ip;
163-
if (!uint.TryParse(ipString, out ip)) continue;
122+
if (token.IsCancellationRequested)
123+
{
124+
goto done;
125+
}
164126

165-
if (BitConverter.IsLittleEndian)
166-
{
167-
ip = (uint)IPAddress.NetworkToHostOrder((int)ip);
168-
}
127+
Logger.Debug3(Logger.Instance, "Connected to DS");
128+
Stream stream = client.GetStream();
169129

170-
// If 0 clear the override
171-
if (ip == 0)
172-
{
173-
Dispatcher.Instance.ClearServerOverride();
174-
oldIp = 0;
175-
continue;
130+
while (!token.IsCancellationRequested && stream.CanRead)
131+
{
132+
StringBuilder json = new StringBuilder();
133+
int chars = 0;
134+
byte[] ch = new byte[1];
135+
do
136+
{
137+
chars = 0;
138+
try
139+
{
140+
chars = await stream.ReadAsync(ch, 0, 1, token);
141+
}
142+
catch (OperationCanceledException)
143+
{
144+
goto done;
145+
}
146+
if (chars != 1) break;
147+
if (token.IsCancellationRequested) goto done;
148+
} while (ch[0] != (byte)'{');
149+
json.Append('{');
150+
151+
if (!stream.CanRead || !client.Connected || chars != 1)
152+
{
153+
break;
154+
}
155+
156+
do
157+
{
158+
chars = 0;
159+
try
160+
{
161+
chars = await stream.ReadAsync(ch, 0, 1, token);
162+
if (chars != 1) break;
163+
if (token.IsCancellationRequested) goto done;
164+
json.Append((char)ch[0]);
165+
}
166+
catch (OperationCanceledException)
167+
{
168+
goto done;
169+
}
170+
} while (ch[0] != (byte)'}');
171+
172+
if (!stream.CanRead || !client.Connected || chars != 1)
173+
{
174+
break;
175+
}
176+
177+
string jsonString = json.ToString();
178+
Debug3(Logger.Instance, $"json={jsonString}");
179+
180+
// Look for "robotIP":12345, and get 12345 portion
181+
int pos = jsonString.IndexOf("\"robotIP\"");
182+
if (pos < 0) continue; // could not find?
183+
pos += 9;
184+
pos = jsonString.IndexOf(':', pos);
185+
if (pos < 0) continue; // could not find?
186+
// Find first not of
187+
int endpos = -1;
188+
for (int i = pos + 1; i < jsonString.Length; i++)
189+
{
190+
if (jsonString[i] < '0' || jsonString[i] > '9')
191+
{
192+
endpos = i;
193+
break;
194+
}
195+
}
196+
string ipString = jsonString.Substring(pos + 1, endpos - (pos + 1));
197+
Debug3(Logger.Instance, $"found robotIP={ipString}");
198+
199+
// Parse into number
200+
uint ip;
201+
if (!uint.TryParse(ipString, out ip)) continue;
202+
203+
if (BitConverter.IsLittleEndian)
204+
{
205+
ip = (uint)IPAddress.NetworkToHostOrder((int)ip);
206+
}
207+
208+
if (ip == 0)
209+
{
210+
//m_serverOverridable.ClearServerOverride();
211+
Dispatcher.Instance.ClearServerOverride();
212+
oldIp = 0;
213+
continue;
214+
}
215+
216+
if (ip == oldIp) continue;
217+
oldIp = ip;
218+
219+
json.Clear();
220+
221+
IPAddress address = new IPAddress(oldIp);
222+
Info(Logger.Instance, $"client: DS overriding server IP to {address.ToString()}");
223+
port = Interlocked.CompareExchange(ref m_port, 0, 0);
224+
//m_serverOverridable.SetServerOverride(address, port);
225+
Dispatcher.Instance.SetServerOverride(address, port);
226+
}
176227
}
177228

178-
// If unchanged, don't reconnect
179-
if (ip == oldIp) continue;
180-
oldIp = ip;
181-
182-
json.Clear();
183-
184-
IPAddress address = new IPAddress(oldIp);
185-
Info(Logger.Instance, $"client: DS overriding server IP to {address.ToString()}");
186-
Dispatcher.Instance.SetServerOverride(address.ToString(), port);
187-
188-
229+
Dispatcher.Instance.ClearServerOverride();
230+
//m_serverOverridable.ClearServerOverride();
231+
oldIp = 0;
189232
}
190-
191-
Dispatcher.Instance.ClearServerOverride();
192-
oldIp = 0;
193-
194233
}
195-
196-
done:
234+
catch (ObjectDisposedException)
235+
{
236+
}
237+
catch (NullReferenceException)
197238
{
198-
Dispatcher.Instance.ClearServerOverride();
199239
}
200240

241+
done:
242+
Dispatcher.Instance.ClearServerOverride();
243+
//m_serverOverridable.ClearServerOverride();
201244
}
202245
}
203246
}

src/FRC.NetworkTables/Dispatcher.cs

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,13 @@
11
using System.Collections.Generic;
2+
using System.Net;
23
using System.Threading;
4+
using NetworkTables.Interfaces;
35
using NetworkTables.TcpSockets;
46
using NetworkTables.Logging;
57

68
namespace NetworkTables
79
{
8-
internal class Dispatcher : DispatcherBase
10+
internal class Dispatcher : DispatcherBase, IServerOverridable
911
{
1012
private static Dispatcher s_instance;
1113

@@ -56,9 +58,9 @@ public void SetServer(IList<NtIPAddress> servers)
5658
SetConnector(connectors);
5759
}
5860

59-
public void SetServerOverride(string serverName, int port)
61+
public void SetServerOverride(IPAddress address, int port)
6062
{
61-
SetConnectorOverride(() => TcpConnector.Connect(serverName, port, Logger.Instance, 1));
63+
SetConnectorOverride(() => TcpConnector.Connect(address.ToString(), port, Logger.Instance, 1));
6264
}
6365

6466
public void ClearServerOverride()

0 commit comments

Comments
 (0)