public static SafePipeHandle CreateNamedPipeServer(string pipeName, string sddl) { return(NativeMethod.CreateNamedPipe( @"\\.\pipe\" + pipeName, // The unique pipe name. PipeOpenMode.PIPE_ACCESS_DUPLEX | PipeOpenMode.ASYNCHRONOUS, PipeMode.PIPE_TYPE_BYTE, 1, // Max server instances 1024 * 16, // Output buffer size 1024 * 16, // Input buffer size NMPWAIT_USE_DEFAULT_WAIT, // Time-out interval CreateNativePipeSecurity(sddl) // Pipe security attributes )); }
/// <summary> /// P/Invoke native APIs related to named pipe operations to create the named pipe. /// System.IO.Pipes in Unity is missing crucial types/methods for performing reliable and /// accurate transfers. Such as bytesSents/bytesReceived. /// </summary> public static void Run() { while (!KillServerRequested) { // ---------------------------------------------------------------------------------------------------------------- // Create the named pipe. using (SafePipeHandle hNamedPipe = NativeMethod.CreateNamedPipe( IPCServer.FullPipeName, // The unique pipe name. PipeOpenMode.PIPE_ACCESS_DUPLEX, // The pipe is duplex PipeMode.PIPE_TYPE_MESSAGE | // Message type pipe PipeMode.PIPE_READMODE_MESSAGE | // Message-read mode PipeMode.PIPE_WAIT, // Blocking mode is on 2, // Max server instances IPCServer.BufferSize, // Output buffer size IPCServer.BufferSize, // Input buffer size NMPWAIT_USE_DEFAULT_WAIT // Time-out interval )) { try { if (hNamedPipe.IsInvalid) { throw new Win32Exception(); } pipeHandle = hNamedPipe; Console.WriteLine("[IPC Server Waiting for Connection] - \"{0}\"", IPCServer.FullPipeName); // ---------------------------------------------------------------------------------------------------------------- // Wait for the connections. Runs on background thread. if (!NativeMethod.ConnectNamedPipe(hNamedPipe, IntPtr.Zero)) { if (Marshal.GetLastWin32Error() != ERROR_PIPE_CONNECTED) { throw new Win32Exception(); } } Console.WriteLine("[IPC Server Status] - Client Connected"); // ---------------------------------------------------------------------------------------------------------------- // Received a request from client. string message; bool finishRead = false; do { byte[] bRequest = new byte[IPCServer.BufferSize]; int cbRequest = bRequest.Length, cbRead; finishRead = NativeMethod.ReadFile( hNamedPipe, // Handle of the pipe bRequest, // Buffer to receive data cbRequest, // Size of buffer in bytes out cbRead, // Number of bytes read IntPtr.Zero // Not overlapped ); if (!finishRead && Marshal.GetLastWin32Error() != ERROR_MORE_DATA) { throw new Win32Exception(); } // UTF8-encode the received byte array and trim all the '\0' characters at the end. message = Encoding.UTF8.GetString(bRequest).Replace("\0", ""); Console.WriteLine("[IPC Server Received {0} bytes] Message: {1}\r\n", cbRead, message); }while (!finishRead); // Repeat loop if ERROR_MORE_DATA // If message is not KILL_SERVER, then process client request if (message != "KILL_SERVER") { //Get our message header and data string[] msgArray = message.Split(new string[] { "|:|" }, StringSplitOptions.None); string header = msgArray[0]; string data = msgArray[1]; // Process Client Requests Here based off request header Console.WriteLine(" Message Header: " + header); Order order = JsonConvert.DeserializeObject <Order>(data); Console.WriteLine(" Message Data: {0} ordered {1} {2}, delivery address: {3}\r\n", order.CustomerName, order.Quantity, order.ProductName, order.Address); } // ---------------------------------------------------------------------------------------------------------------- // Send a message received response to client. string rmessage = IPCServer.ResponseMessage; byte[] bResponse = Encoding.UTF8.GetBytes(rmessage); int cbResponse = bResponse.Length, cbWritten; if (!NativeMethod.WriteFile( hNamedPipe, // Handle of the pipe bResponse, // Message to be written cbResponse, // Number of bytes to write out cbWritten, // Number of bytes written IntPtr.Zero // Not overlapped )) { throw new Win32Exception(); } Console.WriteLine("[IPC Server Sent {0} bytes] Message: {1}", cbWritten, rmessage.Replace("\0", "")); if (message == "KILL_SERVER") { KillServerRequested = true; } // ---------------------------------------------------------------------------------------------------------------- // Flush the pipe to allow the client to read the pipe's contents before disconnecting. Then disconnect the client's connection. NativeMethod.FlushFileBuffers(hNamedPipe); NativeMethod.DisconnectNamedPipe(hNamedPipe); } catch (Exception ex) { if (ex.Message != "Thread was being aborted") { Console.WriteLine("[IPC Server ERROR] - {0}", ex.Message); } hNamedPipe.Close(); hNamedPipe.Dispose(); NativeMethod.DisconnectNamedPipe(hNamedPipe); } finally { if (hNamedPipe != null) { hNamedPipe.Close(); hNamedPipe.Dispose(); NativeMethod.DisconnectNamedPipe(hNamedPipe); } } } } if (KillServerRequested) { _ipcServer.StopServer(); } }
/// <summary> /// P/Invoke the native APIs related to named pipe operations to create /// the named pipe. /// </summary> public static void Run() { SafePipeHandle hNamedPipe = null; try { // Prepare the security attributes (the securityAttributes // parameter in CreateNamedPipe) for the pipe. This is optional. // If securityAttributes of CreateNamedPipe is null, the named // pipe gets a default security descriptor and the handle cannot // be inherited. The ACLs in the default security descriptor of a // pipe grant full control to the LocalSystem account, (elevated) // administrators, and the creator owner. They also give only // read access to members of the Everyone group and the anonymous // account. However, if you want to customize the security // permission of the pipe, (e.g. to allow Authenticated Users to // read from and write to the pipe), you need to create a // SECURITY_ATTRIBUTES object. SECURITY_ATTRIBUTES sa = null; sa = CreateNativePipeSecurity(); // Create the named pipe. hNamedPipe = NativeMethod.CreateNamedPipe( Program.FullPipeName, // The unique pipe name. PipeOpenMode.PIPE_ACCESS_DUPLEX, // The pipe is duplex PipeMode.PIPE_TYPE_MESSAGE | // Message type pipe PipeMode.PIPE_READMODE_MESSAGE | // Message-read mode PipeMode.PIPE_WAIT, // Blocking mode is on PIPE_UNLIMITED_INSTANCES, // Max server instances Program.BufferSize, // Output buffer size Program.BufferSize, // Input buffer size NMPWAIT_USE_DEFAULT_WAIT, // Time-out interval sa // Pipe security attributes ); if (hNamedPipe.IsInvalid) { throw new Win32Exception(); } Console.WriteLine("The named pipe ({0}) is created.", Program.FullPipeName); // Wait for the client to connect. Console.WriteLine("Waiting for the client's connection..."); if (!NativeMethod.ConnectNamedPipe(hNamedPipe, IntPtr.Zero)) { if (Marshal.GetLastWin32Error() != ERROR_PIPE_CONNECTED) { throw new Win32Exception(); } } Console.WriteLine("Client is connected."); // // Receive a request from client. // string message; bool finishRead = false; do { byte[] bRequest = new byte[Program.BufferSize]; int cbRequest = bRequest.Length, cbRead; finishRead = NativeMethod.ReadFile( hNamedPipe, // Handle of the pipe bRequest, // Buffer to receive data cbRequest, // Size of buffer in bytes out cbRead, // Number of bytes read IntPtr.Zero // Not overlapped ); if (!finishRead && Marshal.GetLastWin32Error() != ERROR_MORE_DATA) { throw new Win32Exception(); } // Unicode-encode the received byte array and trim all the // '\0' characters at the end. message = Encoding.Unicode.GetString(bRequest).TrimEnd('\0'); Console.WriteLine("Receive {0} bytes from client: \"{1}\"", cbRead, message); }while (!finishRead); // Repeat loop if ERROR_MORE_DATA // // Send a response from server to client. // message = Program.ResponseMessage; byte[] bResponse = Encoding.Unicode.GetBytes(message); int cbResponse = bResponse.Length, cbWritten; if (!NativeMethod.WriteFile( hNamedPipe, // Handle of the pipe bResponse, // Message to be written cbResponse, // Number of bytes to write out cbWritten, // Number of bytes written IntPtr.Zero // Not overlapped )) { throw new Win32Exception(); } Console.WriteLine("Send {0} bytes to client: \"{1}\"", cbWritten, message.TrimEnd('\0')); // Flush the pipe to allow the client to read the pipe's contents // before disconnecting. Then disconnect the client's connection. NativeMethod.FlushFileBuffers(hNamedPipe); NativeMethod.DisconnectNamedPipe(hNamedPipe); } catch (Exception ex) { Console.WriteLine("The server throws the error: {0}", ex.Message); } finally { if (hNamedPipe != null) { hNamedPipe.Close(); hNamedPipe = null; } } }