using System; using System.Collections.Generic; using System.IO; using System.IO.Pipes; using System.Linq; using System.Net; using System.Net.Sockets; using System.Runtime.InteropServices; using System.Threading; namespace HeadlessServer { struct ThreadArgs { public NamedPipeClientStream pipe; public TcpClient port; public ManualResetEvent ev; public string pipeName; } struct AsyncParams { public ThreadArgs threadArgs; public byte[] buffer; public int hasStopped; public IAsyncResult currentOp; } struct Win32 { [DllImport("kernel32.dll", CallingConvention = CallingConvention.Winapi, EntryPoint = "WaitNamedPipeW", SetLastError = true)] public static extern int WaitNamedPipe([MarshalAs(UnmanagedType.LPWStr)] string pipe, int timeout); } static class Program { static void PipeReadComplete(IAsyncResult res) { AsyncParams param = (AsyncParams)res.AsyncState; ThreadArgs args = param.threadArgs; NamedPipeClientStream pipe = args.pipe; NetworkStream outStream = args.port.GetStream(); try { byte[] buffer = param.buffer; int asyncReadBytes = pipe.EndRead(res); if (asyncReadBytes > 0) { outStream.Write(buffer, 0, asyncReadBytes); outStream.Flush(); } param.currentOp = pipe.BeginRead(buffer, 0, buffer.Length, new AsyncCallback(PipeReadComplete), param); } catch (IOException) { Interlocked.Exchange(ref param.hasStopped, 1); } catch (InvalidOperationException) { Console.WriteLine("Pipe broken, closing down"); Interlocked.Exchange(ref param.hasStopped, 1); } } static void PipeListenThread(object obj) { ThreadArgs args = (ThreadArgs)obj; NamedPipeClientStream pipe = args.pipe; byte[] buffer = new byte[4096]; AsyncParams param = new AsyncParams(); param.threadArgs = args; param.buffer = buffer; string fullPipeName = @"\\.\pipe\" + args.pipeName; // pipe.Connect(timeout) pegs the cpu without any pauses if the pipe isn't available // so we implement it ourselves in a non-pegged way while (Win32.WaitNamedPipe(fullPipeName, 500) == 0) { if (args.ev.WaitOne(0)) { Console.WriteLine("Didn't connect to pipe before exit event was set"); goto exit; } Thread.Sleep(100); } pipe.Connect(); Console.WriteLine("Pipe Connected"); param.currentOp = pipe.BeginRead(buffer, 0, buffer.Length, new AsyncCallback(PipeReadComplete), param); while ((Interlocked.CompareExchange(ref param.hasStopped,0, 0) != 1) && (!args.ev.WaitOne(0))) { Thread.Sleep(250); } exit: pipe.Close(); } static void PortListenThread(object obj) { ThreadArgs args = (ThreadArgs)obj; TcpClient port = args.port; NetworkStream inStream = args.port.GetStream(); byte[] buffer = new byte[4096]; while (!args.ev.WaitOne(0)) { if (inStream.DataAvailable) { try { int read = 0; while ((read = inStream.Read(buffer, 0, 4096)) > 0) { args.pipe.Write(buffer, 0, read); } } // happens when timeout expires catch (IOException ex) { if (ex.InnerException.GetType() == typeof(SocketException)) { SocketException sEx = (SocketException)ex.InnerException; if (sEx.SocketErrorCode == SocketError.TimedOut) { // just a timeout continue; } } // not a timeout, something serious went down. Exit Console.WriteLine("Socket closed, closing down"); args.ev.Set(); } } else { Thread.Sleep(500); } } inStream.Close(); port.Close(); } static void DoListeningStuff(ThreadArgs args) { Thread pipe = new Thread(new ParameterizedThreadStart(PipeListenThread)); Thread port = new Thread(new ParameterizedThreadStart(PortListenThread)); pipe.Start(args); port.Start(args); } [STAThread] static void Main(string[] args) { short portNumber = 0; if ((args.Length < 2) || (!Int16.TryParse(args[1], out portNumber))) { Console.WriteLine("Usage: NamedPipeToPort pipeName portNumber"); return; } IPAddress localHost = IPAddress.Loopback; TcpListener server = new TcpListener(localHost, portNumber); server.ExclusiveAddressUse = true; Console.WriteLine("Listening for connections before connecting to pipe"); server.Start(); TcpClient client = server.AcceptTcpClient(); server.Stop(); Console.WriteLine("Client connected, creating pipe and kicking off threads"); string pipeName = args[0]; NamedPipeClientStream pipe = new NamedPipeClientStream(".", pipeName, PipeDirection.InOut, PipeOptions.Asynchronous); ThreadArgs threadArgs = new ThreadArgs(); threadArgs.ev = new ManualResetEvent(false); threadArgs.pipe = pipe; threadArgs.pipeName = pipeName; threadArgs.port = client; DoListeningStuff(threadArgs); Console.WriteLine("Enter to quit"); while (!Console.KeyAvailable) { Thread.Sleep(500); } threadArgs.ev.Set(); Thread.Sleep(2000); } } }