This is the fifteenth part of the Availability Anywhere series. For your convenience you can find other parts in the table of contents in Part 1 – Connecting to SSH tunnel automatically in Windows
You can’t forward a named pipe between the host and the guest VM, but we can forward the named pipe as a serial port to the VM. So the host writes to the named pipe, and the VM reads from the serial port. Let’s see the server now:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 |
using System; using System.IO.Ports; using System.Linq; using System.Net; using System.Net.Sockets; using System.Threading; using System.IO; namespace PipeProxy { class PortServer { public static void Start(string[] args) { var destinationIp = args[1].Split(':')[0]; var destinationPort = int.Parse(args[1].Split(':')[1]); var portName = args[2]; Console.WriteLine($"Routing to {destinationIp}:{destinationPort} via {portName}"); Console.WriteLine("Connecting"); //In theory I can set baud rate to 268435456 //In practice the max is 115200 SerialPort port = new SerialPort(portName, 115200, Parity.None, 8, StopBits.One) { ReadBufferSize = 10000000, WriteBufferSize = 10000000 }; while (true) { try { while (true) { Thread.Sleep(1000); if (port.IsOpen) { continue; } else { port.Open(); Console.WriteLine("Connected"); } // I'm forcing IPv4 (IPv6 breaks with Cisco AnyConnect) IPEndPoint remoteEP = new IPEndPoint(Dns.GetHostEntry(destinationIp).AddressList.First(a => a.AddressFamily == AddressFamily.InterNetwork), destinationPort); Socket senderSocket = new Socket(remoteEP.AddressFamily, SocketType.Stream, ProtocolType.Tcp); senderSocket.Connect(remoteEP); Console.WriteLine("Socket connected to {0}", senderSocket.RemoteEndPoint); new Thread(() => { var socket = new PortSocket(senderSocket, port, r => { }, s => { }); socket.Start(); }).Start(); } } catch (Exception e) { Console.WriteLine("Exception " + e); } } } } public class PortSocket { private Socket clientSocket; private SerialPort port; private Action<int> received; private Action<int> sent; public PortSocket(Socket socket, SerialPort port, Action<int> received, Action<int> sent) { this.clientSocket = socket; this.port = port; this.received = received; this.sent = sent; } public void Start() { Thread clientThread = new Thread(() => { try { var buffer = new byte[100000]; while (true) { var read = clientSocket.Receive(buffer); if (read == 0) { port.Close(); return; } while (true) { try { port.Write(buffer, 0, read); sent(read); break; } catch (Exception e2) { Console.WriteLine("Exception when writing to pipe" + e2); } } } } catch (Exception e) { Console.WriteLine("Exception in client thread: " + e); } }); try { Thread senderThread = new Thread(KeepReading); senderThread.Start(); } catch (Exception e) { Console.WriteLine("Exception " + e); } clientThread.Start(); clientThread.Join(); } private void KeepReading() { int totalRead = 0; int toRead = 100024; byte[] bytes = new byte[toRead]; try { while (true) { int howMuchRead = port.Read(bytes, 0, toRead); if (howMuchRead == 0) break; totalRead += howMuchRead; received(howMuchRead); clientSocket.Send(bytes, 0, howMuchRead, SocketFlags.None); } } catch (Exception e) { Console.WriteLine("Exception while reading" + e); } } } } |
And here is the benchmark:
1 2 3 4 5 6 7 8 |
Pipe -> Serial (115200 baud rate) Mode: PipeProxyVirtualMachine 1 0.993 10 2.364 100 2.452 1000 9.119 10000 85.12 100000 844.847 |
It works, but slows down linearly with the size. Why? That’s because the serial port has a max bandwidth (that we call baud rate). Here it is 115200 (around 112 kBps). While this works much faster for small packets, this won’t give a decent performance for RDP with bigger bandwidth requirements.
Can we do better? One solution would be to emulate a COM port that has a bigger bandwidth. However, I don’t know a solution yet.