/// <summary> /// Wait on connection and received messages /// </summary> protected virtual void ReceiveAction() { try { using (NamedPipeServerStream serverStream = new NamedPipeServerStream( PipeName, PipeDirection.InOut, NumberOfThreads, PipeTransmissionMode.Byte, PipeOptions.Asynchronous)) { Task t = serverStream.WaitForConnectionAsync(CancellationToken.Token); t.GetAwaiter().GetResult(); Guid id = Guid.NewGuid(); IpcStream stream = new IpcStream(serverStream, KnownTypes); while (ProcessMessage(stream, id)) { } StatefulProxy.NotifyDisconnect(id); } // Todo: make sure there's a new listener, even when this isn't reached serverTask.Add(Task.Factory.StartNew(ReceiveAction)); } catch (OperationCanceledException) { } }
/// <summary> /// Establish the TCP connection asynchonously /// </summary> /// <param name="keepalive">Wether the connection should be kept alive after a first message</param> /// <returns>True for success, False otherwise</returns> public async Task <bool> ConnectAsync(bool keepalive = true) { try { // connect socket connection = new System.Net.Sockets.TcpClient(); connection.NoDelay = true; connection.ReceiveTimeout = 2000; await connection.ConnectAsync(EndPoint.Address, EndPoint.Port).ConfigureAwait(false); System.IO.Stream connectionStream = connection.GetStream(); Stream = new IpcStream(connectionStream, KnownTypes, Encryptor); } catch (SocketException e) { System.Diagnostics.Debug.WriteLine(e); return(false); } if (keepalive) { StartPing(); } return(true); }
/// <summary> /// Main connection loop, waits for and handles connections /// </summary> protected override void ReceiveAction() { try { var t = listener.AcceptTcpClientAsync(CancellationToken.Token); // wait for connection using (System.Net.Sockets.TcpClient client = t.GetAwaiter().GetResult()) { // Start new connection waiter before anything can go wrong here, // leaving us without a server serverTask.Add(Task.Factory.StartNew(ReceiveAction)); using (NetworkStream networkStream = client.GetStream()) { networkStream.ReadTimeout = Server.ReadTimeOut; Stream serverStream = networkStream; Guid id = Guid.NewGuid(); IpcStream stream = new IpcStream(serverStream, KnownTypes, Encryptor); // process incoming messages until disconnect while (ProcessMessage(stream, id)) { } StatefulProxy.NotifyDisconnect(id); serverStream.Close(); } } } catch (OperationCanceledException) { } }
/// <summary> /// Scans the service interface and registers custom proxy class /// </summary> /// <typeparam name="T">Service interface, must equal server-side</typeparam> /// <returns>Proxy class for remote calls</returns> public void RegisterServiceProxy <T>(Proxy <T> customProxy) { // check if service implements interface if (customProxy.GetType().GetInterface(typeof(T).Name) == null) { throw new InvalidOperationException("Custom Proxy class does not implement service interface"); } IpcStream.ScanInterfaceForTypes(typeof(T), KnownTypes); }
/// <summary> /// Register a service interface on the server /// This is a one instance for all clients service /// </summary> /// <typeparam name="T">The interface of the service</typeparam> /// <param name="instance">Instance of a class implementing the service</param> public void RegisterService <T>(T instance) { if (!typeof(T).IsInterface) { throw new InvalidOperationException("Service Type is not an interface"); } if (!(instance is T)) { throw new InvalidOperationException("Instance must implement service interface"); } services[typeof(T).Name] = instance; types[typeof(T).Name] = typeof(T); IpcStream.ScanInterfaceForTypes(typeof(T), KnownTypes); }
/// <summary> /// Establish the TCP connection /// </summary> /// <param name="keepalive">Wether the connection should be kept alive after a first message</param> /// <returns>True for success, False otherwise</returns> public override bool Connect(bool keepalive = true) { try { // connect socket connection = new System.Net.Sockets.TcpClient(); connection.ReceiveTimeout = 2000; connection.Connect(EndPoint); System.IO.Stream connectionStream = connection.GetStream(); Stream = new IpcStream(connectionStream, KnownTypes, Encryptor); } catch (SocketException e) { System.Diagnostics.Debug.WriteLine(e); return(false); } if (keepalive) { StartPing(); } return(true); }
/// <summary> /// Register a service interface on the server that keeps its state for a connection /// This a one instance for one client/connection service /// </summary> /// <typeparam name="T">The interface of the service</typeparam> /// <param name="t">Type of the class implementing the service, should have a default constructor /// which will be called on connection of a new client</param> public void RegisterStatefulService <T>(Type t) { if (!typeof(T).IsInterface) { throw new InvalidOperationException("Service Type is not an interface"); } // check if service implements interface if (t.GetInterface(typeof(T).Name) == null) { throw new InvalidOperationException("Instance must implement service interface"); } // check for default constructor if (t.GetConstructor(Type.EmptyTypes) == null || t.IsAbstract) { throw new InvalidOperationException("Stateful service requires default constructor"); } services[typeof(T).Name] = new StatefulProxy(t); types[typeof(T).Name] = typeof(T); IpcStream.ScanInterfaceForTypes(typeof(T), KnownTypes); }
/// <summary> /// Connect to server. This opens a persistent connection allowing multiple remote calls /// until <see cref="Disconnect(bool)"/> is called. /// </summary> /// <param name="keepalive">Whether to send pings over the connection to keep it alive</param> /// <returns>True if succeeded, false if not</returns> public virtual bool Connect(bool keepalive = true) { NamedPipeClientStream source = new NamedPipeClientStream( ".", PipeName, PipeDirection.InOut, PipeOptions.Asynchronous); try { source.Connect(500); } catch (TimeoutException) { return(false); } Stream = new IpcStream(source, KnownTypes); if (keepalive) { StartPing(); } return(true); }
/// <summary> /// Process a received message by calling the corresponding method on the service instance and /// returning the return value over the network. /// </summary> /// <param name="stream">The message stream</param> /// <param name="streamId">A GUID identifying this connection</param> public bool ProcessMessage(IpcStream stream, Guid streamId) { IpcMessage msg = stream.ReadMessage(); // this was a close-connection notification if (msg.StatusMsg == StatusMessage.CloseConnection) { return(false); } // or this is just a keepalive ping else if (msg.StatusMsg == StatusMessage.Ping) { return(true); } bool processedOk = false; string error = ""; object rv = null; IpcMessage returnMsg = new IpcMessage(); // find the service if (services.TryGetValue(msg.Service, out object instance) && instance != null) { // double check method existence against type-list for security // typelist will contain interfaces instead of instances if (types[msg.Service].GetMethod(msg.Method) != null) { // separate handling for stateful service if (instance is StatefulProxy) { try { // invoke method System.Reflection.MethodInfo method = (instance as StatefulProxy).Type.GetMethod(msg.Method); if (method == null) { throw new InvalidOperationException("Method not found in stateful proxy"); } rv = (instance as StatefulProxy).Invoke(streamId, method, msg.Parameters); processedOk = true; // check if encryption is required if (Attribute.IsDefined(method, typeof(EncryptIfTrueAttribute)) && (bool)rv == true) { returnMsg.StatusMsg = StatusMessage.Encrypt; } } catch (Exception e) { error = e.ToString(); } } else { // get the method System.Reflection.MethodInfo method = instance.GetType().GetMethod(msg.Method); if (method != null) { try { // invoke method rv = method.Invoke(instance, msg.Parameters); processedOk = true; // check if encryption is required if (Attribute.IsDefined(method, typeof(EncryptIfTrueAttribute)) && (bool)rv == true) { returnMsg.StatusMsg = StatusMessage.Encrypt; } } catch (Exception e) { error = e.ToString(); } } else { error = "Could not find method"; } } } else { error = "Could not find method in type"; } } else { error = "Could not find service"; } // return either return value or error message if (processedOk) { returnMsg.Return = rv; } else { returnMsg.Error = error; returnMsg.StatusMsg = StatusMessage.None; } stream.WriteMessage(returnMsg); // if there's more to come, keep reading a next message if (msg.StatusMsg == StatusMessage.KeepAlive) { return(true); } else // otherwise close the connection { return(false); } }
/// <summary> /// Scans the service interface and builds proxy class /// </summary> /// <typeparam name="T">Service interface, must equal server-side</typeparam> /// <returns>Proxy class for remote calls</returns> public T GetServiceProxy <T>() { IpcStream.ScanInterfaceForTypes(typeof(T), KnownTypes); return((T) new ProxyGenerator().CreateInterfaceProxyWithoutTarget(typeof(T), new Proxy <T>(this))); }