using System; using System.Collections.Generic; using System.IO; using System.Net; using System.Net.Sockets; using System.Reflection; using System.Runtime.InteropServices.WindowsRuntime; using System.Threading; using System.Threading.Tasks; using Windows.Networking; using Windows.Networking.Sockets; namespace DCT_test { // Uses the production WinRT bridge callback and copy loops on loopback. // Radio discovery, WPS and a physical tablet are deliberately outside this test. internal static class VerifyWifiDirect { private sealed class Bridge : IDisposable { private readonly StreamSocketListener listener = new StreamSocketListener(); private readonly WifiDirectHost host; private readonly CancellationTokenSource cancellation = new CancellationTokenSource(); private int receivedConnections; public Bridge(IPEndPoint server) { host = new WifiDirectHost(server.Port); typeof(WifiDirectHost).GetField("_cts", BindingFlags.Instance | BindingFlags.NonPublic) .SetValue(host, cancellation); var callback = typeof(WifiDirectHost).GetMethod("SocketListener_ConnectionReceived", BindingFlags.Instance | BindingFlags.NonPublic); listener.Control.NoDelay = true; listener.ConnectionReceived += (sender, args) => { Interlocked.Increment(ref receivedConnections); callback.Invoke(host, new object[] { sender, args }); }; listener.BindEndpointAsync(new HostName("127.0.0.1"), "0") .AsTask().GetAwaiter().GetResult(); } public TcpClient Connect() { var client = new TcpClient { NoDelay = true }; client.Connect(IPAddress.Loopback, int.Parse(listener.Information.LocalPort)); if (!SpinWait.SpinUntil(() => Volatile.Read(ref receivedConnections) > 0, 2000)) { client.Dispose(); throw new Exception("WinRT listener did not accept the test client"); } return client; } public void Dispose() { host.Dispose(); listener.Dispose(); // The async bridge can still observe cancellation while disposing its I/O. } } private static int Main() { try { TransportRegression.RunCurrentAppCompatibility(); foreach (bool fragmented in new[] { false, true }) { var bridges = new List(); try { TransportRegression.RunCurrentAppCompatibility(endpoint => { var bridge = new Bridge(endpoint); bridges.Add(bridge); return bridge.Connect(); }, fragmented); Console.WriteLine("PASS: production Wi-Fi Direct WinRT bridge protocol parity; fragmented=" + fragmented); } finally { foreach (var bridge in bridges) bridge.Dispose(); } } VerifyBridgeBytes(); return 0; } catch (Exception ex) { Console.Error.WriteLine(ex); return 1; } } private static void VerifyBridgeBytes() { var server = new TcpListener(IPAddress.Loopback, 0); server.Start(); try { using (var bridge = new Bridge((IPEndPoint)server.LocalEndpoint)) using (var peer = bridge.Connect()) using (var target = server.AcceptTcpClient()) { var peerStream = peer.GetStream(); var targetStream = target.GetStream(); peerStream.ReadTimeout = targetStream.ReadTimeout = 5000; peerStream.WriteTimeout = targetStream.WriteTimeout = 5000; byte[] payload = new byte[2 * 1024 * 1024]; for (int i = 0; i < payload.Length; i++) payload[i] = (byte)(i * 37 + i / 251); var up = Task.Run(() => peerStream.Write(payload, 0, payload.Length)); CheckBytes(targetStream, payload); up.GetAwaiter().GetResult(); var down = Task.Run(() => targetStream.Write(payload, 0, payload.Length)); Thread.Sleep(1800); // A temporary paused receiver must keep byte order and the connection. CheckBytes(peerStream, payload); down.GetAwaiter().GetResult(); targetStream.WriteByte(0x85); if (peerStream.ReadByte() != 0x85) throw new Exception("Control byte after receiver pause"); target.Close(); try { if (peerStream.ReadByte() != -1) throw new Exception("Bridge must propagate server close"); } catch (IOException ex) { // Disposing a WinRT socket with its upstream read pending can // surface a TCP reset. Only accept that after our explicit close, // after every payload byte and the following control were checked. var socketError = ex.InnerException as SocketException; if (socketError == null || (socketError.SocketErrorCode != System.Net.Sockets.SocketError.ConnectionReset && socketError.SocketErrorCode != System.Net.Sockets.SocketError.ConnectionAborted)) throw; } Console.WriteLine("PASS: production WinRT bridge 2MiB each direction, exact bytes, 1.8s reader pause, control after pause, close propagation"); } } finally { server.Stop(); } } private static void CheckBytes(Stream source, byte[] expected) { byte[] buffer = new byte[1777]; int offset = 0; while (offset < expected.Length) { int count = source.Read(buffer, 0, Math.Min(buffer.Length, expected.Length - offset)); if (count == 0) throw new EndOfStreamException("Bridge closed before all bytes arrived"); for (int i = 0; i < count; i++) if (buffer[i] != expected[offset + i]) throw new Exception("Bridge changed byte " + (offset + i)); offset += count; } } } }