You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
148 lines
6.7 KiB
148 lines
6.7 KiB
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<Bridge>();
|
|
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;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|