public async Task <EchoResult> WriteAsync(HostName address, string service, ProtocolType protocolType, string data) { switch (protocolType) { case ProtocolType.Tcp: return(await WriteTcpAsync(address, service, data)); case ProtocolType.Udp: return(await WriteUdpAsync(address, service, data)); } return(EchoResult.MakeFailed(SocketErrorStatus.SocketTypeNotSupported, 0.0)); }
/// <summary> /// Ensures that the socket is made. Will return a failure EchoResult on failure /// and will return NULL for a success. /// </summary> /// <param name="address"></param> /// <param name="service"></param> /// <returns></returns> private async Task <EchoResult> EnsureUdpSocket(HostName address, string service) { bool mustConnect = false; lock (this) { if (udpSocket == null) { udpSocket = new DatagramSocket(); udpSocket.MessageReceived += UdpSocket_MessageReceived; mustConnect = true; } } if (mustConnect) { try { // Can't do the await inside a lock; it's not allowed in C#. await udpSocket.ConnectAsync(address, service); udpDw = new DataWriter(udpSocket.OutputStream); } catch (Exception ex) { udpSocket = null; Stats.NExceptions++; Log($"ERROR: Client: Creating UDP socket to {address} exception {ex.Message}"); var delta = DateTime.UtcNow.Subtract(SocketStartTime).TotalSeconds; return(EchoResult.MakeFailed(ex, delta)); } } // must be in a race condition to make the socket and get the udpDw //TODO: remove magic number and verify and explain this code. while (udpSocket != null && udpDw == null) { await Task.Delay(10); } // We made a socket and then making the datawriter somehow failed. // Just give up; there's no path that will get us into a better place. if (udpSocket == null || udpDw == null) { return(EchoResult.MakeFailed(SocketErrorStatus.CannotAssignRequestedAddress, 0)); } return(null); }
private EchoResult ReadUdp(DataReader dr) { uint count = dr.UnconsumedBufferLength; if (count > 0) { byte[] buffer = new byte[dr.UnconsumedBufferLength]; dr.ReadBytes(buffer); var stringresult = BufferToString.ToString(buffer); LogEchoBuffer(buffer); var delta = DateTime.UtcNow.Subtract(SocketStartTime).TotalSeconds; return(EchoResult.MakeSucceeded(stringresult, delta)); } else // socket is done { var delta = DateTime.UtcNow.Subtract(SocketStartTime).TotalSeconds; return(EchoResult.MakeFailed(SocketErrorStatus.HttpInvalidServerResponse, delta)); } }
private void UdpSocket_MessageReceived(DatagramSocket sender, DatagramSocketMessageReceivedEventArgs args) { try { var dr = args.GetDataReader(); dr.InputStreamOptions = InputStreamOptions.Partial; // | InputStreamOptions.ReadAhead; var udpResult = ReadUdp(dr); UdpResults.Add(sender.Information.LocalPort, udpResult); } catch (Exception ex) { // This can happen when we send a packet to a correct host (like localhost) but with an // incorrect service. The packet will "bounce", resuluting in a MessageReceived event // but with an args with no real data. Stats.NExceptions++; var delta = DateTime.UtcNow.Subtract(SocketStartTime).TotalSeconds; var udpResult = EchoResult.MakeFailed(ex, delta); UdpResults.Add(sender.Information.LocalPort, udpResult); } }
private async Task <EchoResult> WriteTcpAsync(HostName address, string service, string data) { var startTime = DateTime.UtcNow; try { if (tcpSocket == null) { tcpSocket = new StreamSocket(); await tcpSocket.ConnectAsync(address, service); tcpDw = new DataWriter(tcpSocket.OutputStream); SocketStartTime = startTime; // Now read everything TcpReadEchoResult = EchoResult.MakeInProgress(0); // Taken no time at all so far :-) var dr = new DataReader(tcpSocket.InputStream); dr.InputStreamOptions = InputStreamOptions.Partial; TcpReadTask = ReadTcpAsync(dr); } tcpDw.WriteString(data); await tcpDw.StoreAsync(); Stats.NWrites++; var delta = DateTime.UtcNow.Subtract(SocketStartTime).TotalSeconds; return(EchoResult.MakeInProgress(delta)); } catch (Exception ex) { Stats.NExceptions++; Log($"ERROR: Client: Writing {data} to {address} exception {ex.Message}"); var delta = DateTime.UtcNow.Subtract(SocketStartTime).TotalSeconds; return(EchoResult.MakeFailed(ex, delta)); } }
private async Task <EchoResult> WriteUdpAsync(HostName address, string service, string data) { SocketStartTime = DateTime.UtcNow; try { var result = await EnsureUdpSocket(address, service); if (result != null) { // EnsureUdpSocket only returns non-null result after a failure. return(result); } if (string.IsNullOrEmpty(data)) { // A blank string, when written to a data writer, won't actually result in a // UDP packet being sent. For the special case of not sending any data, // use the WriteAsync on the socket's OutputStream directly. var b = new Windows.Storage.Streams.Buffer(0); await udpSocket.OutputStream.WriteAsync(b); Stats.NWrites++; } else { udpDw.WriteString(data); await udpDw.StoreAsync(); Stats.NWrites++; } Log(ClientOptions.Verbosity.Verbose, $"Client: UDP: Sent request on local port {udpSocket.Information.LocalPort} request {data}"); // // Wait for an answer // const int START_DELAY_MS = 10; int currTotalDelay = 0; int currDelay = START_DELAY_MS; EchoResult udpResult = null; while (!UdpResults.TryRemove(udpSocket.Information.LocalPort, out udpResult)) { await Task.Delay(currDelay); currTotalDelay += currDelay; currDelay = Math.Min(currDelay * 2, Options.MaxPollLoopInMilliseconds); // Do an exponential backup up to max (10 seconds) if (currTotalDelay >= Options.MaxWaitInMilliseconds) { Log($"ERROR: Client: reply from {address} took too long (outgoing data={data})"); var delta = DateTime.UtcNow.Subtract(SocketStartTime).TotalSeconds; udpResult = EchoResult.MakeFailed(SocketErrorStatus.ConnectionTimedOut, delta); break; } } return(udpResult); } catch (Exception ex) { Stats.NExceptions++; Log($"ERROR: Client: Writing {data} to {address} exception {ex.Message}"); var delta = DateTime.UtcNow.Subtract(SocketStartTime).TotalSeconds; return(EchoResult.MakeFailed(ex, delta)); } }