Home > C#, Task, tcp, TPL > TCP Proxy in C# using Task Parallel Library

TCP Proxy in C# using Task Parallel Library

Every now and then I have the need to proxy TCP communications, handy for things like viewing network traffic or proxying Silverlight or Flash requests. C# makes this pretty easy, and the Task Parallel Library (add-on to .NET 3.5 & shipped with .NET 4) simplifies the code with a nice fluent interface.

Here’s a quick example that works for proxying a VNC connection. There is one task for reading from the client and sending data to the server and another task for reading server responses and sending them to the client.

static TcpListener listener = new TcpListener(IPAddress.Any, 4502);

const int BUFFER_SIZE = 4096;

static void Main(string[] args) {
    listener.Start();
    new Task(() => {
        // Accept clients.
        while (true) {
            var client = listener.AcceptTcpClient();
            new Task(() => {
                // Handle this client.
                var clientStream = client.GetStream();
                TcpClient server = new TcpClient("10.0.1.5", 5900);
                var serverStream = server.GetStream();
                new Task(() => {
                    byte[] message = new byte[BUFFER_SIZE];
                    int clientBytes;
                    while (true) {
                        try {
                            clientBytes = clientStream.Read(message, 0, BUFFER_SIZE);
                        }
                        catch {
                            // Socket error - exit loop.  Client will have to reconnect.
                            break;
                        }
                        if (clientBytes == 0) {
                            // Client disconnected.
                            break;
                        }
                        serverStream.Write(message, 0, clientBytes);
                    }
                    client.Close();
                }).Start();
                new Task(() => {
                    byte[] message = new byte[BUFFER_SIZE];
                    int serverBytes;
                    while (true) {
                        try {
                            serverBytes = serverStream.Read(message, 0, BUFFER_SIZE);
                            clientStream.Write(message, 0, serverBytes);
                        }
                        catch {
                            // Server socket error - exit loop.  Client will have to reconnect.
                            break;
                        }
                        if (serverBytes == 0) {
                            // server disconnected.
                            break;
                        }
                    }
                }).Start();
            }).Start();
        }
    }).Start();
    Debug.WriteLine("Server listening on port 4502.  Press enter to exit.");
    Debug.ReadLine();
    listener.Stop();
}

This is for illustrative purposes only. If you decide to use this in production, you’ll need to use TcpListener.BeginAcceptTcpClient() for async connections, you’ll need error handling and logging, and you’ll want some sort of pool to manage (and clean up) client socket connections. Have fun, and let me know if you have concerns or suggestions.

Advertisements
Categories: C#, Task, tcp, TPL Tags: , , ,
  1. annamalai
    August 29, 2011 at 7:39 am

    Thanks for sharing, works excellently

  2. siva
    August 16, 2012 at 12:38 pm

    Awesome. You saved me week of work. Great work and great use of multi cores.

  3. magfen
    December 14, 2012 at 4:42 am

    Good, you saved me too.
    Have you an idea how your code can work with securized proxy (with password)?
    Thanks

    • loosexaml
      December 15, 2012 at 11:49 am

      I’m not sure what you mean? Do you want this code to act as a securized proxy or do you want it to forward connections through another proxy?

  4. magfen
    December 16, 2012 at 5:50 am

    I want it to forward connections through another proxy but with password…

  5. Nelson
    October 20, 2015 at 3:00 pm

    Why do you recommend using BeginAcceptTcpClient over AcceptTcpClientAsync?

    Also, I did this experiment: I opened your program and Fiddler and forwarded all IE (https) traffic to your application and then routed it through Fiddler and I noticed that when IE is closed, the connections (tunnels) are kept open. If I send the https traffic directly to Fiddler as soon as IE is closed all tunnels are closed. I was expecting that the forwarder would detect when a client disconnects and would disconnect the server as well. Any ideas on how to achieve that? Thank you.

    • loosexaml
      October 20, 2015 at 3:23 pm

      I don’t recommend BeginAcceptTcpClient over the new Async version, but it did not exist when I wrote this post, and was only added in the .NET 4.5 framework to work with the C# 5 async / await.

      I’m not sure on your experiment. Maybe a code sample would help me see the issue.

  6. Nelson
    October 21, 2015 at 12:14 pm

    My mistake, your code works flawlessly. It was a modification I introduced.

    Thanks so much for your work, it was of great help!

    • loosexaml
      October 21, 2015 at 8:55 pm

      You’re very welcome. Thanks for the kind words and best of luck with your project.

  1. No trackbacks yet.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

%d bloggers like this: