context: using System;
  using System.Collections.Generic;
  using System.Text;
  using System.Threading;
  using System.Net;
  using System.Net.Sockets;
  using System.Threading.Tasks;
  
  namespace DotNetSiemensPLCToolBoxLibrary.Communication.Networking
  {
      public class TCPFunctionsAsync : IDisposable
      {
          private bool disposed = false;
          public void Dispose()
          {
              disposed = true;
  
              Stop();
          }
  
          public bool DoNotUseRecieveEvent { get; set; }
  
          public Socket CurrentSocket
          {
              get { return tcpClient.Client; }
          }
  
          public NetworkStream NetworkStream
          {
              get { return tcpClient.GetStream(); }
          }
  
          /// <summary>
          /// Status of the socket client
          /// </summary>
          public enum Status
          {
              /// <summary>client is not connected with the server</summary>
              DISCONNECTED = 0,
              /// <summary>the client try to connect the server</summary>
              LISTENING = 1,
              /// <summary>the client try to connect the server</summary>
              CONNECTING = 2,
              /// <summary>the client is connected</summary>
              CONNECTED = 3,
          }
  
          #region Properties & Events
  
          public Status State = Status.DISCONNECTED;
  
          public string StateString
          {
              get
              {
                  string retVal = State.ToString();
                  if (tcpClient != null)
                      retVal = "Local-Port: " + tcpClient.Client.LocalEndPoint + ": " + retVal;
                  return retVal;
              }
          }
  
          public string Name { get; set; }
  
          public string LastErrorMessage { get; set; }
  
          public string LastMessage { get; set; }
  
          private bool _autoReConnect = true;
          public bool AutoReConnect
          {
              get { return _autoReConnect; }
              set { _autoReConnect = value; }
          }
  
          /// <summary>
          /// On a Socket Server, allow multiple Clients 
          /// </summary>
          public bool AllowMultipleClients { get; set; }
  
  
          public event Action<TcpClient> ConnectionEstablished;
          public event Action<TcpClient> ConnectionClosed;
          public event Action<byte[], TcpClient> DataRecieved;
  
          public event ThreadExceptionEventHandler AsynchronousExceptionOccured;
  
          #endregion
  
          #region Private Fields
  
          private TcpClient tcpClient;
          private List<TcpClient> tcpClientsFromListener = new List<TcpClient>();
          private TcpListener tcpListener;
  
          private IPAddress local_ip;
          private int connection_port;
          private bool connection_active;
          private SynchronizationContext context;
  
          public bool UseKeepAlive { get; set; }
  
          public bool Started { get { return State != Status.DISCONNECTED; } }
          private int fixedLength = -1;
  
          private Dictionary<TcpClient, byte[]> readBytesPerCennection = new Dictionary<TcpClient, byte[]>();
          private Dictionary<TcpClient, int> readBytesCountPerCennection = new Dictionary<TcpClient, int>();
          //private Byte[] readBytes;
          //private int readPos = 0;
  
          private object lockObject = new object();
  
          #endregion
  
          #region Konstruktoren
  
          public TCPFunctionsAsync(SynchronizationContext context, IPAddress IP, int connection_port, bool connection_active)
          {
              this.context = context;
  
              this.connection_port = connection_port;
              this.connection_active = connection_active;
  
              local_ip = IP; // Dns.GetHostByName(Dns.GetHostName()).AddressList[0];
          }
  
          public TCPFunctionsAsync(SynchronizationContext context, IPAddress IP, int connection_port, bool connection_active, int FixedLength)
              : this(context, IP, connection_port, connection_active)
          {
              fixedLength = FixedLength;
          }
  
          public TCPFunctionsAsync(SynchronizationContext context, IPEndPoint endPoint, bool connection_active, int FixedLength)
              : this(context, endPoint.Address, endPoint.Port, connection_active, FixedLength)
          { }
  
          public TCPFunctionsAsync(SynchronizationContext context, IPEndPoint endPoint, bool connection_active)
              : this(context, endPoint.Address, endPoint.Port, connection_active)
          { }
  
          #endregion
  
          public void StartAsync()
          {
              if (!this.connection_active)
              {
                  this.State = Status.LISTENING;
  
                  //if (tcpListener != null)
                  //    tcpListener.Stop();
  
                  if (local_ip == null)
                      local_ip = IPAddress.Any;
  
                  if (tcpListener == null)
                  {
                      tcpListener = new TcpListener(local_ip, connection_port);
                      tcpListener.Start();
                  }
                  tcpListener.BeginAcceptTcpClient(new AsyncCallback(DoAcceptTcpClientCallback), tcpListener);
              }
              else
              {
                  lock (lockObject)
                  {
                      if (this.State != Status.CONNECTING)
                      {
                          if (tcpClient != null)
                          {
                              tcpClient.Close();
                              tcpClient = null;
                          }
  
                          this.State = Status.CONNECTING;
  
                          var tcpc = new TcpClient();
                          tcpc.BeginConnect(local_ip, connection_port, new AsyncCallback(DoBeginnConnectCallback), tcpc);
                      }
                  }
              }
          }
  
          public async void Start()
          {
             var t = new TaskCompletionSource<object>();
              
              Action<TcpClient> vCHandler = null;
              vCHandler = res =>
              {
                  ConnectionEstablished -= vCHandler;
                  ConnectionClosed -= vCHandler;
                  t.TrySetResult(res);
              };
              this.ConnectionEstablished += vCHandler;
              this.ConnectionClosed += vCHandler;
  
              StartAsync();
  
              await t.Task;
          }
  
          public void Stop()
          {
              if (tcpListener != null)
                  tcpListener.Stop();
  
              tcpListener = null;
              
              StopInternal();
          }
  
          private void StopInternal()
          {
              if (tcpClient != null)
                  tcpClient.Close();
  
  
              foreach (var client in tcpClientsFromListener)
              {
                  client.Close();
              }
              tcpClientsFromListener.Clear();
  
              tcpClient = null;
              
              this.State = Status.DISCONNECTED;
          }
  
          public void DoBeginnConnectCallback(IAsyncResult ar)
          {
              TcpClient tcpc = null;
              try
              {
                  tcpc = (TcpClient)ar.AsyncState;
                  tcpc.EndConnect(ar);
  
                  this.State = Status.CONNECTED;
  
                  if (tcpClient == null)
                      tcpClient = tcpc;
                  else
                  {
                      throw new Exception("Error: TCP Client already assigned");
                  }
  
                  if (UseKeepAlive)
                      tcpc.Client.SetKeepAlive(1000, 2000);
  
                  if (ConnectionEstablished != null)
                      if (context == null)
                          ConnectionEstablished(tcpc);
                      else
                          context.Post(delegate { ConnectionEstablished(tcpc); }, null);
  
                  beginRead(tcpc);
              }
              catch (Exception ex)
              {
                  string sMsg = DateTime.Now.ToString() + " - " + "TCPSocketClientAndServer.DoBeginConnectCallback(IAsyncResult) - error: " + ex.Message;
                  this.LastErrorMessage = sMsg;
  
                  if (ex is SocketException)// && (((SocketException)ex).ErrorCode == 10060 || ((SocketException)ex).ErrorCode == 10061 || ((SocketException)ex).ErrorCode == 10065))
                  {
                      if (tcpc != null)
                          tcpc.Close();
  
                      if (ConnectionClosed != null && this.State == Status.CONNECTED)
                          if (context == null)
                              ConnectionClosed(tcpClient);
                          else
  
gt:                             context.Post(delegate { ConnectionClosed(tcpClient); }, null);