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); }
/// <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 SILICONSTUDIO_PLATFORM_UWP var serverAddress = "127.0.0.1"; #else var serverAddress = Environment.GetEnvironmentVariable("XenkoConnectionRouterRemoteIP") ?? "127.0.0.1"; #endif // 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(serverAddress, DefaultPort).Wait(); break; case RouterConnectionMode.Listen: socketContext.StartServer(DefaultListenPort, true, 10).Wait(); break; case RouterConnectionMode.ConnectThenListen: bool clientException = false; try { socketContext.StartClient(serverAddress, 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 {ConnectionMode}", e); throw; } }, CancellationToken.None, TaskCreationOptions.LongRunning, TaskScheduler.Default)); }
/// <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; } }