private SimpleSocket CreateSocketContext() { var socketContext = new SimpleSocket(); socketContext.Connected = (clientSocketContext) => { Task.Run(async () => { try { // Register service server await socketContext.WriteStream.WriteInt16Async((short)RouterMessage.ServiceProvideServer); await socketContext.WriteStream.WriteStringAsync(serverUrl); await socketContext.WriteStream.FlushAsync(); while (true) { var routerMessage = (RouterMessage)await socketContext.ReadStream.ReadInt16Async(); switch (routerMessage) { case RouterMessage.ServiceRequestServer: { var requestedUrl = await clientSocketContext.ReadStream.ReadStringAsync(); var guid = await clientSocketContext.ReadStream.ReadGuidAsync(); // Spawn actual server var realServerSocketContext = new SimpleSocket(); realServerSocketContext.Connected = async (clientSocketContext2) => { // Write connection string await clientSocketContext2.WriteStream.WriteInt16Async((short)RouterMessage.ServerStarted); await clientSocketContext2.WriteStream.WriteGuidAsync(guid); // Delegate next steps to actual server HandleClient(clientSocketContext2, requestedUrl); }; // Start connection await realServerSocketContext.StartClient(address, port); break; } default: Console.WriteLine("Router: Unknown message: {0}", routerMessage); throw new ArgumentOutOfRangeException(); } } } catch (Exception e) { // TODO: Ideally, separate socket-related error messages (disconnection) from real errors // Unfortunately, it seems WinRT returns Exception, so it seems we can't filter with SocketException/IOException only? Log.Info("Client {0}:{1} disconnected with exception: {2}", clientSocketContext.RemoteAddress, clientSocketContext.RemotePort, e.Message); clientSocketContext.Dispose(); } }); }; return socketContext; }
/// <summary> /// Initiates a connection to the router. /// </summary> /// <returns></returns> private static Task<SimpleSocket> InitiateConnectionToRouter() { // Let's make sure this run in a different thread (in case some operation are blocking) return Task.Factory.StartNew(() => { var socketContextTCS = new TaskCompletionSource<SimpleSocket>(); var socketContext = new SimpleSocket(); socketContext.Connected = context => { socketContextTCS.TrySetResult(context); }; try { // If connecting as a client, try once, otherwise try to listen multiple time (in case port is shared) switch (ConnectionMode) { case RouterConnectionMode.Connect: socketContext.StartClient("127.0.0.1", DefaultPort).Wait(); break; case RouterConnectionMode.Listen: socketContext.StartServer(DefaultListenPort, true, 10).Wait(); break; case RouterConnectionMode.ConnectThenListen: bool clientException = false; try { socketContext.StartClient("127.0.0.1", DefaultPort).Wait(); } catch (Exception) // Ideally we should filter SocketException, but not available on some platforms (maybe it should be wrapped in a type available on all paltforms?) { clientException = true; } if (clientException) { socketContext.StartServer(DefaultListenPort, true, 10).Wait(); } break; default: throw new ArgumentOutOfRangeException(); } // Connection should happen within 5 seconds, otherwise consider there is no connection router trying to connect back to us if (!socketContextTCS.Task.Wait(TimeSpan.FromSeconds(5))) throw new InvalidOperationException("Connection router did not connect back to our listen socket"); return socketContextTCS.Task.Result; } catch (Exception e) { Log.Error("Could not connect to connection router using mode {0}: {1}", ConnectionMode, e.Message); throw; } }, CancellationToken.None, TaskCreationOptions.LongRunning, TaskScheduler.Default); }
private SimpleSocket CreateSocketContext() { var socketContext = new SimpleSocket(); socketContext.Connected = (clientSocketContext) => { Task.Run(async () => { try { // Routing var routerMessage = (RouterMessage)await clientSocketContext.ReadStream.ReadInt16Async(); Log.Info("Client {0}:{1} connected, with message {2}", clientSocketContext.RemoteAddress, clientSocketContext.RemotePort, routerMessage); switch (routerMessage) { case RouterMessage.TaskProvideServer: { await HandleMessageServiceProvideServer(clientSocketContext, true); break; } case RouterMessage.ServiceProvideServer: { await HandleMessageServiceProvideServer(clientSocketContext, false); break; } case RouterMessage.ServerStarted: { await HandleMessageServerStarted(clientSocketContext); break; } case RouterMessage.ClientRequestServer: { await HandleMessageClientRequestServer(clientSocketContext); break; } default: throw new ArgumentOutOfRangeException(string.Format("Router: Unknown message: {0}", routerMessage)); } } catch (Exception e) { // TODO: Ideally, separate socket-related error messages (disconnection) from real errors // Unfortunately, it seems WinRT returns Exception, so it seems we can't filter with SocketException/IOException only? Log.Info("Client {0}:{1} disconnected with exception: {2}", clientSocketContext.RemoteAddress, clientSocketContext.RemotePort, e.Message); clientSocketContext.Dispose(); } }); }; return socketContext; }
public async Task StartServer(int port, bool singleConnection, int retryCount = 1) { // Create TCP listener var listener = new TcpSocketListener(2048); listener.ConnectionReceived = async (sender, args) => { var clientSocketContext = new SimpleSocket(); try { // Stop listening if we accept only a single connection if (singleConnection) await listener.StopListeningAsync(); clientSocketContext.SetSocket((TcpSocketClient)args.SocketClient); // Do an ack with magic packet (necessary so that we know it's not a dead connection, // it sometimes happen when doing port forwarding because service don't refuse connection right away but only fails when sending data) await SendAndReceiveAck(clientSocketContext.socket, MagicAck, MagicAck); if (Connected != null) Connected(clientSocketContext); clientSocketContext.isConnected = true; } catch (Exception) { clientSocketContext.DisposeSocket(); } }; for (int i = 0; i < retryCount; ++i) { try { // Start listening await listener.StartListeningAsync(port); break; // Break if no exception, otherwise retry } catch (Exception) { // If there was an exception last try, propragate exception if (i == retryCount - 1) throw; } } }
private void RunTests() { AppDomain.CurrentDomain.UnhandledException += (a, e) => { var exception = e.ExceptionObject as Exception; if (exception != null) { var exceptionText = exception.ToString(); stringBuilder.Append($"Tests fatal failure: {exceptionText}"); Logger.Debug($"Unhandled fatal exception: {exception.ToString()}"); EndTesting(true); } }; var xenkoVersion = Intent.GetStringExtra(TestRunner.XenkoVersion); var buildNumber = Parse(Intent.GetStringExtra(TestRunner.XenkoBuildNumber) ?? "-1"); var branchName = Intent.GetStringExtra(TestRunner.XenkoBranchName) ?? ""; // Remove extra (if activity is recreated) Intent.RemoveExtra(TestRunner.XenkoVersion); Intent.RemoveExtra(TestRunner.XenkoBuildNumber); Intent.RemoveExtra(TestRunner.XenkoBranchName); Logger.Info(@"*******************************************************************************************************************************"); Logger.Info(@"date: " + DateTime.Now); Logger.Info(@"*******************************************************************************************************************************"); // Connect to server right away to let it know we're alive //var client = Connect(serverAddresses, serverPort); var url = "/task/SiliconStudio.Xenko.TestRunner.exe"; socketContext = RouterClient.RequestServer(url).Result; socketBinaryWriter = new BinaryWriter(socketContext.WriteStream); // Update build number (if available) ImageTester.ImageTestResultConnection.BuildNumber = buildNumber; ImageTester.ImageTestResultConnection.BranchName = branchName ?? ""; // Connect beforehand to image tester, so that first test timing is not affected by initial connection try { ImageTester.Connect(); } catch (Exception e) { Logger.Error("Error connecting to image tester server: {0}", e); } // Start unit test var cachePath = CacheDir.AbsolutePath; var timeNow = DateTime.Now; // Generate result file name resultFile = Path.Combine(cachePath, $"TestResult-{timeNow:yyyy-MM-dd_hh-mm-ss-tt}.xml"); Logger.Debug(@"Execute tests"); stringBuilder = new StringBuilder(); var stringWriter = new StringWriter(stringBuilder); try { new TextUI(stringWriter) { TestListener = this }.Execute(new[] { "-format:nunit2", $"-result:{resultFile}" }); } catch (Exception ex) { stringBuilder.Append($"Tests fatal failure: {ex}"); Logger.Error($"Tests fatal failure: {ex}"); } EndTesting(false); }
public Service(SimpleSocket socket) { Socket = socket; }
/// <summary> /// Initiates a connection to the router. /// </summary> /// <returns></returns> private static Task <SimpleSocket> InitiateConnectionToRouter() { // Let's make sure this run in a different thread (in case some operation are blocking) return(Task.Factory.StartNew(() => { var socketContextTCS = new TaskCompletionSource <SimpleSocket>(); var socketContext = new SimpleSocket(); socketContext.Connected = context => { socketContextTCS.TrySetResult(context); }; try { // If connecting as a client, try once, otherwise try to listen multiple time (in case port is shared) switch (ConnectionMode) { case RouterConnectionMode.Connect: socketContext.StartClient("127.0.0.1", DefaultPort).Wait(); break; case RouterConnectionMode.Listen: socketContext.StartServer(DefaultListenPort, true, 10).Wait(); break; case RouterConnectionMode.ConnectThenListen: bool clientException = false; try { socketContext.StartClient("127.0.0.1", DefaultPort).Wait(); } catch (Exception) // Ideally we should filter SocketException, but not available on some platforms (maybe it should be wrapped in a type available on all paltforms?) { clientException = true; } if (clientException) { socketContext.StartServer(DefaultListenPort, true, 10).Wait(); } break; default: throw new ArgumentOutOfRangeException(); } // Connection should happen within 10 seconds, otherwise consider there is no connection router trying to connect back to us if (!socketContextTCS.Task.Wait(TimeSpan.FromSeconds(10))) { throw new SimpleSocketException("Connection router did not connect back to our listen socket"); } return socketContextTCS.Task.Result; } catch (Exception e) { Log.Error("Could not connect to connection router using mode {0}: {1}", ConnectionMode, e.Message); throw; } }, CancellationToken.None, TaskCreationOptions.LongRunning, TaskScheduler.Default)); }
protected override async void HandleClient(SimpleSocket clientSocket, string url) { clientResultsEvent.Set(); await AcceptConnection(clientSocket); try { var binaryReader = new BinaryReader(clientSocket.ReadStream); //Read events TestRunnerMessageType messageType; do { messageType = (TestRunnerMessageType)binaryReader.ReadInt32(); switch (messageType) { case TestRunnerMessageType.TestStarted: { var testFullName = binaryReader.ReadString(); Console.WriteLine($"Test Started: {testFullName}"); clientResultsEvent.Set(); break; } case TestRunnerMessageType.TestFinished: { var testFullName = binaryReader.ReadString(); var status = binaryReader.ReadString(); Console.WriteLine($"Test {status}: {testFullName}"); clientResultsEvent.Set(); break; } case TestRunnerMessageType.TestOutput: { var outputType = binaryReader.ReadString(); var outputText = binaryReader.ReadString(); Console.WriteLine($" {outputType}: {outputText}"); clientResultsEvent.Set(); break; } case TestRunnerMessageType.SessionSuccess: testFailed = false; break; case TestRunnerMessageType.SessionFailure: testFailed = true; break; default: throw new ArgumentOutOfRangeException(); } } while (messageType != TestRunnerMessageType.SessionFailure && messageType != TestRunnerMessageType.SessionSuccess); // Mark test session as finished testFinished = true; //Read output var output = binaryReader.ReadString(); Console.WriteLine(output); // Read XML result var result = binaryReader.ReadString(); Console.WriteLine(result); // Write XML result to disk File.WriteAllText(resultFile, result); clientResultsEvent.Set(); } catch (Exception) { clientResultsEvent.Set(); Console.WriteLine(@"Client disconnected before sending results, a fatal crash might have occurred."); } }
/// <summary> /// Let router knows that we want to continue with that connection. /// </summary> /// <param name="clientSocket">The client socket.</param> /// <returns></returns> protected async Task AcceptConnection(SimpleSocket clientSocket) { await clientSocket.WriteStream.WriteInt32Async(0); // error code OK await clientSocket.WriteStream.FlushAsync(); }
/// <summary> /// Let router knows we refuse the connection, and why. /// </summary> /// <param name="clientSocket">The client socket.</param> /// <param name="errorCode">The error code.</param> /// <param name="errorMessage">The error message.</param> /// <returns></returns> protected async Task RefuseConnection(SimpleSocket clientSocket, int errorCode, string errorMessage) { await clientSocket.WriteStream.WriteInt32Async(errorCode); await clientSocket.WriteStream.WriteStringAsync(errorMessage); await clientSocket.WriteStream.FlushAsync(); }
protected override async void HandleClient(SimpleSocket clientSocket, string url) { await AcceptConnection(clientSocket); var socketMessageLayer = new SocketMessageLayer(clientSocket, true); socketMessageLayer.AddPacketHandler<TestRegistrationRequest>(request => { if (request.Tester) { switch (request.Platform) { case (int)PlatformType.Windows: { Process process = null; var debugInfo = ""; try { var workingDir = Path.GetDirectoryName(request.Cmd); if (workingDir != null) { var start = new ProcessStartInfo { WorkingDirectory = workingDir, FileName = request.Cmd }; start.EnvironmentVariables["SiliconStudioXenkoDir"] = Environment.GetEnvironmentVariable("SiliconStudioXenkoDir"); start.UseShellExecute = false; start.RedirectStandardError = true; start.RedirectStandardOutput = true; debugInfo = "Starting process " + start.FileName + " with path " + start.WorkingDirectory; socketMessageLayer.Send(new LogRequest { Message = debugInfo }).Wait(); process = Process.Start(start); } } catch (Exception ex) { socketMessageLayer.Send(new StatusMessageRequest { Error = true, Message = "Launch exception: " + ex.Message }).Wait(); } if (process == null) { socketMessageLayer.Send(new StatusMessageRequest { Error = true, Message = "Failed to start game process. " + debugInfo }).Wait(); } else { process.OutputDataReceived += (sender, args) => { try { socketMessageLayer.Send(new LogRequest { Message = $"STDIO: {args.Data}" }).Wait(); } catch { } }; process.ErrorDataReceived += (sender, args) => { try { socketMessageLayer.Send(new LogRequest { Message = $"STDERR: {args.Data}" }).Wait(); } catch { } }; process.BeginOutputReadLine(); process.BeginErrorReadLine(); var currenTestPair = new TestPair { TesterSocket = socketMessageLayer, GameName = request.GameAssembly, Process = process }; processes[request.GameAssembly] = currenTestPair; testerToGame[socketMessageLayer] = currenTestPair; socketMessageLayer.Send(new LogRequest { Message = "Process created, id: " + process.Id.ToString() }).Wait(); } break; } case (int)PlatformType.Android: { Process process = null; try { process = Process.Start("cmd.exe", $"/C adb shell monkey -p {request.GameAssembly}.{request.GameAssembly} -c android.intent.category.LAUNCHER 1"); } catch (Exception ex) { socketMessageLayer.Send(new StatusMessageRequest { Error = true, Message = "Launch exception: " + ex.Message }).Wait(); } if (process == null) { socketMessageLayer.Send(new StatusMessageRequest { Error = true, Message = "Failed to start game process." }).Wait(); } else { lock (loggerLock) { currentTester = socketMessageLayer; } var currenTestPair = new TestPair { TesterSocket = socketMessageLayer, GameName = request.GameAssembly, Process = process, TestEndAction = () => { // force stop - only works for Android 3.0 and above. Process.Start("cmd.exe", $"/C adb shell am force-stop {request.GameAssembly}.{request.GameAssembly}"); } }; processes[request.GameAssembly] = currenTestPair; testerToGame[socketMessageLayer] = currenTestPair; socketMessageLayer.Send(new LogRequest { Message = "Process created, id: " + process.Id.ToString() }).Wait(); } break; } case (int)PlatformType.iOS: { Process process = null; var debugInfo = ""; try { Thread.Sleep(5000); //ios processes might be slow to close, we must make sure that we start clean var start = new ProcessStartInfo { WorkingDirectory = $"{Environment.GetEnvironmentVariable("SiliconStudioXenkoDir")}\\Bin\\Windows-Direct3D11\\", FileName = $"{Environment.GetEnvironmentVariable("SiliconStudioXenkoDir")}\\Bin\\Windows-Direct3D11\\idevicedebug.exe", Arguments = $"run com.your-company.{request.GameAssembly}", UseShellExecute = false }; debugInfo = "Starting process " + start.FileName + " with path " + start.WorkingDirectory; process = Process.Start(start); } catch (Exception ex) { socketMessageLayer.Send(new StatusMessageRequest { Error = true, Message = $"Launch exception: {ex.Message} info: {debugInfo}" }).Wait(); } if (process == null) { socketMessageLayer.Send(new StatusMessageRequest { Error = true, Message = "Failed to start game process. " + debugInfo }).Wait(); } else { lock (loggerLock) { currentTester = socketMessageLayer; } var currenTestPair = new TestPair { TesterSocket = socketMessageLayer, GameName = request.GameAssembly, Process = process }; processes[request.GameAssembly] = currenTestPair; testerToGame[socketMessageLayer] = currenTestPair; socketMessageLayer.Send(new LogRequest { Message = "Process created, id: " + process.Id.ToString() }).Wait(); } break; } } } else //Game process { TestPair pair; if (!processes.TryGetValue(request.GameAssembly, out pair)) return; pair.GameSocket = socketMessageLayer; testerToGame[pair.TesterSocket] = pair; gameToTester[pair.GameSocket] = pair; pair.TesterSocket.Send(new StatusMessageRequest { Error = false, Message = "Start" }).Wait(); Console.WriteLine($"Starting test {request.GameAssembly}"); } }); socketMessageLayer.AddPacketHandler<KeySimulationRequest>(request => { var game = testerToGame[socketMessageLayer]; game.GameSocket.Send(request).Wait(); }); socketMessageLayer.AddPacketHandler<TapSimulationRequest>(request => { var game = testerToGame[socketMessageLayer]; game.GameSocket.Send(request).Wait(); }); socketMessageLayer.AddPacketHandler<ScreenshotRequest>(request => { var game = testerToGame[socketMessageLayer]; game.GameSocket.Send(request).Wait(); }); socketMessageLayer.AddPacketHandler<TestEndedRequest>(request => { var game = testerToGame[socketMessageLayer]; game.GameSocket.Send(request).Wait(); testerToGame.Remove(socketMessageLayer); gameToTester.Remove(game.GameSocket); processes.Remove(game.GameName); socketMessageLayer.Context.Dispose(); game.GameSocket.Context.Dispose(); game.Process.Kill(); game.Process.Dispose(); lock (loggerLock) { currentTester = null; } game.TestEndAction?.Invoke(); Console.WriteLine($"Finished test {game.GameName}"); }); socketMessageLayer.AddPacketHandler<TestAbortedRequest>(request => { var game = testerToGame[socketMessageLayer]; testerToGame.Remove(socketMessageLayer); processes.Remove(game.GameName); socketMessageLayer.Context.Dispose(); game.Process.Kill(); game.Process.Dispose(); lock (loggerLock) { currentTester = null; } game.TestEndAction?.Invoke(); Console.WriteLine($"Aborted test {game.GameName}"); }); socketMessageLayer.AddPacketHandler<ScreenShotPayload>(request => { var tester = gameToTester[socketMessageLayer]; var imageData = new TestResultImage(); var stream = new MemoryStream(request.Data); imageData.Read(new BinaryReader(stream)); stream.Dispose(); var resultFileStream = File.OpenWrite(request.FileName); imageData.Image.Save(resultFileStream, ImageFileType.Png); resultFileStream.Dispose(); tester.TesterSocket.Send(new ScreenshotStored()).Wait(); }); Task.Run(async () => { try { await socketMessageLayer.MessageLoop(); } catch { } }); }
public SocketMessageLayer(SimpleSocket context, bool isServer) { this.context = context; this.isServer = isServer; }
protected override async void HandleClient(SimpleSocket clientSocket, string url) { await AcceptConnection(clientSocket); var socketMessageLayer = new SocketMessageLayer(clientSocket, true); socketMessageLayer.AddPacketHandler<TestRegistrationRequest>(request => { if (request.Tester) { switch (request.Platform) { case (int)PlatformType.Windows: { Process process = null; var debugInfo = ""; try { var start = new ProcessStartInfo { WorkingDirectory = Path.GetDirectoryName(request.Cmd), FileName = request.Cmd, }; start.EnvironmentVariables["SiliconStudioXenkoDir"] = Environment.GetEnvironmentVariable("SiliconStudioXenkoDir"); start.UseShellExecute = false; debugInfo = "Starting process " + start.FileName + " with path " + start.WorkingDirectory; socketMessageLayer.Send(new LogRequest { Message = debugInfo }).Wait(); process = Process.Start(start); } catch (Exception ex) { socketMessageLayer.Send(new StatusMessageRequest { Error = true, Message = "Launch exception: " + ex.Message }).Wait(); } if (process == null) { socketMessageLayer.Send(new StatusMessageRequest { Error = true, Message = "Failed to start game process. " + debugInfo }).Wait(); } else { processes[request.GameAssembly] = new TestProcess { Process = process, TesterSocket = socketMessageLayer }; socketMessageLayer.Send(new LogRequest { Message = "Process created, id: " + process.Id.ToString() }).Wait(); } break; } case (int)PlatformType.Android: { Process process = null; var debugInfo = ""; try { process = Process.Start("cmd.exe", $"/C adb shell monkey -p {request.GameAssembly}.{request.GameAssembly} -c android.intent.category.LAUNCHER 1"); } catch (Exception ex) { socketMessageLayer.Send(new StatusMessageRequest { Error = true, Message = "Launch exception: " + ex.Message }).Wait(); } if (process == null) { socketMessageLayer.Send(new StatusMessageRequest { Error = true, Message = "Failed to start game process. " + debugInfo }).Wait(); } else { processes[request.GameAssembly] = new TestProcess { Process = process, TesterSocket = socketMessageLayer }; socketMessageLayer.Send(new LogRequest { Message = "Process created, id: " + process.Id.ToString() }).Wait(); } break; } } } else //Game process { TestProcess process; if (processes.TryGetValue(request.GameAssembly, out process)) { process.GameSocket = socketMessageLayer; testerToGame[process.TesterSocket] = process.GameSocket; gameToTester[process.GameSocket] = process.TesterSocket; process.TesterSocket.Send(new StatusMessageRequest { Error = false, Message = "Start" }).Wait(); } } }); socketMessageLayer.AddPacketHandler<KeySimulationRequest>(request => { var game = testerToGame[socketMessageLayer]; game.Send(request).Wait(); }); socketMessageLayer.AddPacketHandler<TapSimulationRequest>(request => { var game = testerToGame[socketMessageLayer]; game.Send(request).Wait(); }); socketMessageLayer.AddPacketHandler<ScreenshotRequest>(request => { var game = testerToGame[socketMessageLayer]; game.Send(request).Wait(); }); socketMessageLayer.AddPacketHandler<TestEndedRequest>(request => { var game = testerToGame[socketMessageLayer]; game.Send(request).Wait(); testerToGame.Remove(socketMessageLayer); }); socketMessageLayer.AddPacketHandler<ScreenShotPayload>(request => { var tester = gameToTester[socketMessageLayer]; var imageData = new TestResultImage(); var stream = new MemoryStream(request.Data); imageData.Read(new BinaryReader(stream)); stream.Dispose(); var resultFileStream = File.OpenWrite(request.FileName); imageData.Image.Save(resultFileStream, ImageFileType.Png); resultFileStream.Dispose(); tester.Send(new ScreenshotStored()).Wait(); }); Task.Run(() => socketMessageLayer.MessageLoop()); }
private async Task ForwardSocket(SimpleSocket source, SimpleSocket target) { var buffer = new byte[1024]; while (true) { var bufferLength = await source.ReadStream.ReadAsync(buffer, 0, buffer.Length); if (bufferLength == 0) throw new IOException("Socket closed"); await target.WriteStream.WriteAsync(buffer, 0, bufferLength); await target.WriteStream.FlushAsync(); } }
/// <summary> /// Handles ServiceProvideServer messages. It allows service to publicize what "server" they can instantiate. /// </summary> /// <param name="clientSocket">The client socket context.</param> /// <param name="task">If it's a task the service will overwrite old instances</param> /// <returns></returns> private async Task HandleMessageServiceProvideServer(SimpleSocket clientSocket, bool task) { var url = await clientSocket.ReadStream.ReadStringAsync(); lock (registeredServices) { TaskCompletionSource<Service> service; if (task) { if (registeredServices.TryGetValue(url, out service)) { var result = service.Task.Result; result.Socket.Dispose(); } service = new TaskCompletionSource<Service>(); registeredServices[url] = service; } else { if (!registeredServices.TryGetValue(url, out service)) { service = new TaskCompletionSource<Service>(); registeredServices.Add(url, service); } } service.TrySetResult(new Service(clientSocket)); } // TODO: Handle server disconnections //clientSocketContext.Disconnected += }
/// <summary> /// Handles ServerStarted messages. It happens when service opened a new "server" connection back to us. /// </summary> /// <param name="clientSocket">The client socket context.</param> /// <returns></returns> private async Task HandleMessageServerStarted(SimpleSocket clientSocket) { var guid = await clientSocket.ReadStream.ReadGuidAsync(); var errorCode = await clientSocket.ReadStream.ReadInt32Async(); var errorMessage = (errorCode != 0) ? await clientSocket.ReadStream.ReadStringAsync() : null; // Notify any waiter that a server with given GUID is available TaskCompletionSource<SimpleSocket> serverSocketTCS; lock (pendingServers) { if (!pendingServers.TryGetValue(guid, out serverSocketTCS)) { Log.Error("Could not find a matching server Guid"); clientSocket.Dispose(); return; } pendingServers.Remove(guid); } if (errorCode != 0) serverSocketTCS.TrySetException(new Exception(errorMessage)); else serverSocketTCS.TrySetResult(clientSocket); }
/// <summary> /// Handles ClientRequestServer messages. /// It will try to find a matching service (spawn it if not started yet), and ask it to establish a new "server" connection back to us. /// </summary> /// <param name="clientSocket">The client socket context.</param> /// <returns></returns> private async Task HandleMessageClientRequestServer(SimpleSocket clientSocket) { // Check for an existing server // TODO: Proper Url parsing (query string) var url = await clientSocket.ReadStream.ReadStringAsync(); Log.Info("Client {0}:{1} sent message ClientRequestServer with URL {2}", clientSocket.RemoteAddress, clientSocket.RemotePort, url); string[] urlSegments; string urlParameters; RouterHelper.ParseUrl(url, out urlSegments, out urlParameters); if (urlSegments.Length == 0) throw new InvalidOperationException("No URL Segments"); SimpleSocket serverSocket = null; ExceptionDispatchInfo serverSocketCapturedException = null; try { // For now, we handle only "service" URL switch (urlSegments[0]) { case "service": { // From the URL, start service (if not started yet) and ask it to provide a server serverSocket = await SpawnServerFromService(url, false); break; } case "task": { // From the URL, start service (if not started yet) and ask it to provide a server serverSocket = await SpawnServerFromService(url, true); } break; case "redirect": { // Redirect to a IP/port serverSocket = new SimpleSocket(); var host = urlSegments[1]; var port = int.Parse(urlSegments[2]); // Note: for security reasons, we currently use a whitelist if (host == "XenkoBuild.siliconstudio.co.jp" && port == 1832) await serverSocket.StartClient(host, port, false); else throw new InvalidOperationException("Trying to redirect to a non-whitelisted host/port"); break; } default: throw new InvalidOperationException("This type of URL is not supported"); } } catch (Exception e) { serverSocketCapturedException = ExceptionDispatchInfo.Capture(e); } if (serverSocketCapturedException != null) { try { // Notify client that there was an error await clientSocket.WriteStream.WriteInt16Async((short)RouterMessage.ClientServerStarted); await clientSocket.WriteStream.WriteInt32Async(1); // error code Failure await clientSocket.WriteStream.WriteStringAsync(serverSocketCapturedException.SourceException.Message); await clientSocket.WriteStream.FlushAsync(); } finally { serverSocketCapturedException.Throw(); } } try { // Notify client that we've found a server for it await clientSocket.WriteStream.WriteInt16Async((short)RouterMessage.ClientServerStarted); await clientSocket.WriteStream.WriteInt32Async(0); // error code OK await clientSocket.WriteStream.FlushAsync(); // Let's forward clientSocketContext and serverSocketContext await await Task.WhenAny( ForwardSocket(clientSocket, serverSocket), ForwardSocket(serverSocket, clientSocket)); } catch { serverSocket.Dispose(); throw; } }
/// <inheritdoc/> protected override async void HandleClient(SimpleSocket clientSocket, string url) { string[] urlSegments; string urlParameters; RouterHelper.ParseUrl(url, out urlSegments, out urlParameters); var parameters = RouterHelper.ParseQueryString(urlParameters); var mode = parameters["mode"]; // We accept everything await AcceptConnection(clientSocket); var socketMessageLayer = new SocketMessageLayer(clientSocket, true); Guid? packageId = null; { Guid packageIdParsed; if (Guid.TryParse(parameters["packageid"], out packageIdParsed)) packageId = packageIdParsed; } if (mode == "gamestudio") { Console.WriteLine(@"GameStudio mode started!"); if (!packageId.HasValue) return; lock (gameStudioPerPackageId) { gameStudioPerPackageId[packageId.Value] = socketMessageLayer; } } else { // Create an effect compiler per connection var effectCompiler = new EffectCompiler(); Console.WriteLine(@"Client connected"); // TODO: This should come from an "init" packet effectCompiler.SourceDirectories.Add(EffectCompilerBase.DefaultSourceShaderFolder); // Make a VFS that will access remotely the DatabaseFileProvider // TODO: Is that how we really want to do that in the future? var networkVFS = new NetworkVirtualFileProvider(socketMessageLayer, "/asset"); VirtualFileSystem.RegisterProvider(networkVFS); effectCompiler.FileProvider = networkVFS; socketMessageLayer.AddPacketHandler<RemoteEffectCompilerEffectRequest>(packet => ShaderCompilerRequestHandler(socketMessageLayer, effectCompiler, packet)); socketMessageLayer.AddPacketHandler<RemoteEffectCompilerEffectRequested>(packet => { if (!packageId.HasValue) return; SocketMessageLayer gameStudio; lock (gameStudioPerPackageId) { if (!gameStudioPerPackageId.TryGetValue(packageId.Value, out gameStudio)) return; } // Forward to game studio gameStudio.Send(packet); }); } Task.Run(() => socketMessageLayer.MessageLoop()); }
/// <summary> /// Called when a new client connection has been established. /// Before writing anything to the stream, HandleClient is responsible for either calling <see cref="AcceptConnection"/> or <see cref="RefuseConnection"/>. /// </summary> /// <param name="clientSocket">The client socket.</param> /// <param name="url">The requested URL.</param> protected abstract void HandleClient(SimpleSocket clientSocket, string url);
public static bool Connect(SimpleSocket simpleSocket) { ImageComparisonServer = simpleSocket.Socket; return OpenConnection(); }
/// <summary> /// Handles ClientRequestServer messages. /// It will try to find a matching service (spawn it if not started yet), and ask it to establish a new "server" connection back to us. /// </summary> /// <param name="clientSocket">The client socket context.</param> /// <returns></returns> private async Task HandleMessageClientRequestServer(SimpleSocket clientSocket) { // Check for an existing server // TODO: Proper Url parsing (query string) var url = await clientSocket.ReadStream.ReadStringAsync(); string[] urlSegments; string urlParameters; RouterHelper.ParseUrl(url, out urlSegments, out urlParameters); if (urlSegments.Length == 0) throw new InvalidOperationException("No URL Segments"); SimpleSocket serverSocket = null; ExceptionDispatchInfo serverSocketCapturedException = null; try { // For now, we handle only "service" URL switch (urlSegments[0]) { case "service": { // From the URL, start service (if not started yet) and ask it to provide a server serverSocket = await SpawnServerFromService(url); break; } default: throw new InvalidOperationException("This type of URL is not supported"); } } catch (Exception e) { serverSocketCapturedException = ExceptionDispatchInfo.Capture(e); } if (serverSocketCapturedException != null) { try { // Notify client that there was an error await clientSocket.WriteStream.WriteInt16Async((short)RouterMessage.ClientServerStarted); await clientSocket.WriteStream.WriteInt32Async(1); // error code Failure await clientSocket.WriteStream.WriteStringAsync(serverSocketCapturedException.SourceException.Message); await clientSocket.WriteStream.FlushAsync(); } finally { serverSocketCapturedException.Throw(); } } try { // Notify client that we've found a server for it await clientSocket.WriteStream.WriteInt16Async((short)RouterMessage.ClientServerStarted); await clientSocket.WriteStream.WriteInt32Async(0); // error code OK await clientSocket.WriteStream.FlushAsync(); // Let's forward clientSocketContext and serverSocketContext await await Task.WhenAny( ForwardSocket(clientSocket, serverSocket), ForwardSocket(serverSocket, clientSocket)); } catch { serverSocket.Dispose(); throw; } }