0

I am building a tcp port forwarding application. The client connects to the server on a particular port and internally the request is routed in the server to a remote port and response is passed back to the client. Below is my code.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Net.Sockets;
using System.Threading;
namespace BBGRouter
{
    class Router
    {
        #region log4net
        private static readonly log4net.ILog log = log4net.LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType);
        #endregion //log4net
        bool isShutdown = false;
        private Socket router = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);

        public event EventHandler OnRestartNeeded;
        //only one time

        Thread thRouterTh;
        public Router()
        {

            thRouterTh = new Thread(new ParameterizedThreadStart(RouterProc));
        }
        IPEndPoint local, remote;
        public void Start(IPEndPoint local, IPEndPoint remote)
        {
            if (log.IsInfoEnabled) { log.Info("Listening on " + local); }
            this.local = local;
            this.remote = remote;
            router.Bind(local);
            router.Listen(10);
            thRouterTh.Name = "router thread";
            thRouterTh.Start();
        }

        void RouterProc(object obj)
        {
            while (!isShutdown)
            {
                try
                {
                    var source = router.Accept();
                    if (log.IsInfoEnabled) { log.Info("Creating new session...."); }
                    var destination = new Router();
                    var state = new State(source, destination.router);
                    destination.Connect(remote, source);
                    source.BeginReceive(state.Buffer, 0, state.Buffer.Length, 0, OnDataReceive, state);
                }
                catch (Exception ex)
                {
                    if (log.IsErrorEnabled) { log.Error("Exception in Router thread", ex); }
                    if (isShutdown) { if (log.IsInfoEnabled) { log.Info("Shutting down..."); } }
                }
            }
        }

        public void Join()
        {
            if (thRouterTh != null)
                thRouterTh.Join();
        }

        private void StopAndFireRestart(EventArgs e)
        {
            Stop();

           log.Info("Stopped router");

            EventHandler handler = OnRestartNeeded;
            if (handler != null)
            {
                log.Info("Firing the restart event now");
                handler(this, e);
            }
        }

        public void Stop()
        {
            try
            {
                isShutdown = true;
                if (log.IsInfoEnabled) { log.Info("Stopping router thread"); }
                router.Shutdown(SocketShutdown.Both);
                //router.Shutdown(SocketShutdown.Receive);
            }
            catch (Exception ex)
            {
                if (log.IsErrorEnabled) { log.Error("Exception while stopping", ex); }
            }
            finally
            {
                thRouterTh.Interrupt();
                router.Dispose();
            }
        }

        private void Connect(EndPoint remoteEndpoint, Socket destination)
        {

            if (log.IsInfoEnabled) { log.InfoFormat("connecting session at {0}", remoteEndpoint.ToString()); }
            var state = new State(router, destination);

            try
            {
                router.Connect(remoteEndpoint);
                router.BeginReceive(state.Buffer, 0, state.Buffer.Length, SocketFlags.None, OnDataReceive, state);
            }
            catch (SocketException e)
            {
                if (log.IsErrorEnabled) { log.Error(string.Format("SocketException while connect: Exception: {0}, ErrorCode: {1}", e, e.ErrorCode)); }

                // Stop the service
                StopAndFireRestart(new EventArgs());
            }
            catch (Exception ex)
            {
                if (log.IsErrorEnabled) { log.Error("exception while connect {0}", ex); }
            }
        }

        private void OnDataReceive(IAsyncResult result)
        {
            var state = (State)result.AsyncState;
            try
            {
                var bytesRead = state.SourceSocket.EndReceive(result);
                if (bytesRead > 0)
                {
                    log.Info(string.Format("Bytes read: {0}", bytesRead));
                    state.DestinationSocket.Send(state.Buffer, bytesRead, SocketFlags.None);
                    state.SourceSocket.BeginReceive(state.Buffer, 0, state.Buffer.Length, 0, OnDataReceive, state);
                }
            }
            catch (Exception e)
            {
                if (log.IsErrorEnabled) { log.Error("Exception receiving the data, Closing source/destination sockets", e); }
                //state.DestinationSocket.Close();

                //state.SourceSocket.Close();
                //StopAndFireRestart(new EventArgs());
            }
        }

        private class State
        {
            public Socket SourceSocket { get; private set; }
            public Socket DestinationSocket { get; private set; }
            public byte[] Buffer { get; private set; }

            public State(Socket source, Socket destination)
            {
                SourceSocket = source;
                DestinationSocket = destination;
                Buffer = new byte[8192];
            }
        }
    }
}

The problem happens when suddenly the client gets disconnected from the remote port randomly after 4-5 hrs. How can I reconnect the client gracefully so that there is no change in the client code ?

  • [As soon as you type new Thread(), it’s over; your project already has legacy code.](http://shop.oreilly.com/product/0636920030171.do) –  Apr 21 '15 at 18:43
  • also note, that you should synchronize your `isShutdown`-field for the communication. Why? Read [this explanation: *Do We Really Need Locks and Barriers?*](http://www.albahari.com/threading/part4.aspx). [Instead of using `volatile` you can use `lock`.](http://stackoverflow.com/a/17530556/57508) –  Apr 21 '15 at 18:48
  • @AndreasNiedermair -- Yes sir certainly this is legacy code which I am trying to patch. Is there a sample SSH program somewhere that I can use for this. All I want to do is port forwarding for requests and response. – user4816199 Apr 21 '15 at 18:52
  • sorry, my mind played tricks on me ... it is not SSH, it is netsh ... –  Apr 21 '15 at 19:08
  • @AndreasNiedermair -- But would they handle multiple clients and connect clients on disconnects? Pardon my ignorance, I am not too knowledgeable about it. – user4816199 Apr 21 '15 at 19:15
  • no worries. actually it should work as needed - why don't you give it a try? –  Apr 21 '15 at 19:27

1 Answers1

0

Why don't you solve this issue with netsh?

netsh interface portproxy add v4tov4 listenport=LOCALPORT listenaddress=localhost connectport=REMOTEPORT connectaddress=REMOTEADDRESS

or

netsh interface portproxy add v4tov4 listenport=LOCALPORT connectport=REMOTEPORT connectaddress=REMOTEADDRESS

More information on netsh available on MSDN.