diff --git a/Network/ESPOta.cs b/Network/ESPOta.cs index 39272528..b2abace4 100644 --- a/Network/ESPOta.cs +++ b/Network/ESPOta.cs @@ -35,15 +35,7 @@ public async Task Update(string remoteIp, int remotePort, EspOtaCommand co { _fs.Seek(0, SeekOrigin.Begin); - var listener = new TcpListener(IPAddress.Any, _localPort ?? 0) - { - Server = - { - SendTimeout = 10000, - ReceiveTimeout = 10000, - } - }; - + var listener = new TcpListener(IPAddress.Any, _localPort ?? 0); try { listener.Start(); @@ -52,27 +44,22 @@ public async Task Update(string remoteIp, int remotePort, EspOtaCommand co await _progress($"Server started. Listening to TCP clients at 0.0.0.0:{_localPort}", 0); await _progress($"Upload size {_contentSize}", 0); + _ = Invite(ct); + DateTime startTime = DateTime.UtcNow; TimeSpan timeout = TimeSpan.FromSeconds(30); - - while (DateTime.UtcNow - startTime < timeout) + while (true) { - var retryPolicy = Policy - .Handle() - .WaitAndRetryAsync(3, i => TimeSpan.FromSeconds(1)); - if (!await retryPolicy.ExecuteAsync(Invite)) - return false; - - await _progress("Waiting for device to connect...", 0); + var remainingTime = timeout - (DateTime.UtcNow - startTime); + if (remainingTime <= TimeSpan.Zero) break; var acceptTask = listener.AcceptTcpClientAsync(ct).AsTask(); - var delayTask = Task.Delay(10000, ct); - - var completedTask = await Task.WhenAny(acceptTask, delayTask); - - if (completedTask != acceptTask) continue; - using var client = await acceptTask; - if (await Handle(client)) return true; + var t = await Task.WhenAny(acceptTask, Task.Delay(remainingTime, ct)); + if (t == acceptTask) + { + using var client = await acceptTask; + if (await Handle(client)) return true; + } } throw new NoResponseException(); @@ -84,64 +71,96 @@ public async Task Update(string remoteIp, int remotePort, EspOtaCommand co async Task Handle(TcpClient client) { - var stream = client.GetStream(); - - var ip = client.Client.RemoteEndPoint as IPEndPoint; - await _progress($"Connection from {ip?.Address}", 2); - var offset = 0; - var chunk = new byte[1460]; - var readCount = 0; - - while (_contentSize > offset) + using var s = client.GetStream(); + using var sr = new StreamReader(s, Encoding.UTF8); + var response = ""; + var reader = Task.Run(async () => { - var chunkSize = await _fs.ReadAsync(chunk, 0, 1460, ct); - offset += chunkSize; - if (client.Available > 0) + while (true) { - var r1 = Encoding.UTF8.GetString(chunk, 0, readCount); - Console.Write(r1); + var buf = new char[32]; + var r = await sr.ReadAsync(buf, 0, buf.Length); + if (r == 0) break; + var s = new string(buf, 0, r); + foreach (var c in s) + if (!char.IsDigit(c)) + response += c; + } + }, ct); + try + { + var ip = client.Client.RemoteEndPoint as IPEndPoint; + await _progress($"Connection from {ip?.Address}", 2); + var offset = 0; + var chunk = new byte[1460]; + while (_contentSize > offset) + { + var chunkSize = await _fs.ReadAsync(chunk, 0, 1460, ct); + offset += chunkSize; + await s.WriteAsync(chunk, 0, chunkSize, ct); + await _progress($"Written {offset} out of {_contentSize}", 2 + (int)(offset * 98.0f / _contentSize)); } - - await _progress($"Written {offset} out of {_contentSize}", 2 + (int)(offset * 98.0f / _contentSize)); - await stream.WriteAsync(chunk, 0, chunkSize, ct); } - - readCount = await stream.ReadAsync(chunk, 0, 1460, ct); - var resp = Encoding.UTF8.GetString(chunk, 0, readCount); - while (!resp.Contains("O")) + finally { - if (resp.Contains("E")) - return false; - - readCount = await stream.ReadAsync(chunk, 0, 1460, ct); - resp = Encoding.UTF8.GetString(chunk, 0, readCount); - Console.Write(resp); + await reader; } - - await _progress("All done!", 100); - client.Close(); - return true; + DateTime startTime = DateTime.UtcNow; + await _progress("Response: " + response, 100); + if (response.Contains('E')) return false; + if (response.Contains('O')) + { + await _progress("Successfully Updated!", 100); + return true; + } + return false; } - async Task Invite() + async Task Invite(CancellationToken ct) { - var message = $"{command:D} {_localPort} {_contentSize} {_fileMd5}\n"; - var messageBytes = Encoding.UTF8.GetBytes(message); - await _progress($"Sending invitation to {remoteIp}", 0); - - using var udp = new UdpClient(); - var ep = new IPEndPoint(IPAddress.Parse(remoteIp), remotePort); - await udp.SendAsync(messageBytes, messageBytes.Length, ep); - - var res = udp.ReceiveAsync(ct).AsTask(); - var index = await Task.WhenAny(res, Task.Delay(10000, ct)); - if (res != index) - throw new NoResponseException(); - - var resText = Encoding.UTF8.GetString((await res).Buffer); - await _progress($"Invitation: {resText}", 1); - - return resText == "OK" ? true : throw new Exception("AUTH required and not implemented"); + try + { + using var udp = new UdpClient(); + udp.Client.Bind(new IPEndPoint(IPAddress.Any, 0)); + var message = $"{command:D} {_localPort} {_contentSize} {_fileMd5}\n"; + var messageBytes = Encoding.UTF8.GetBytes(message); + CancellationTokenSource cts = new(); + var r = Task.Factory.StartNew(async a => + { + while (!cts.IsCancellationRequested) + { + var ep = new IPEndPoint(IPAddress.Parse(remoteIp), remotePort); + await udp.SendAsync(messageBytes, messageBytes.Length, ep); + await _progress($"Sent invitation to {remoteIp}", 0); + await Task.Delay(1000, cts.Token); + } + }, cts.Token, TaskCreationOptions.LongRunning); + + try + { + var res = udp.ReceiveAsync(ct).AsTask(); + var t = await Task.WhenAny(res, Task.Delay(30000, ct)); + if (res == t) + { + var resText = Encoding.UTF8.GetString((await res).Buffer); + await _progress($"Invitation: {resText}", 1); + if (resText == "OK") + return true; + throw new Exception("AUTH required and not implemented"); + } + throw new NoResponseException(); + } + finally + { + cts.Cancel(); + await r; + } + } + catch (Exception ex) + { + await _progress($"Error: {ex.Message}", 100); + throw; + } } } }