C# listen in tcp network without busyTimes?

1k Views Asked by At

I am trying to catch all incoming events on a specific Port. It works fine but i think, my Socket is blocked for 5s while stream.Read() is performing. What could happen if a package enters while stream.Read() is busy? And how could i avoid possible dataloss?

    public static void Main(string[] args)
    {

        System.Console.WriteLine("Testprogramm Connection\n");
        //ThreadListener tl = new ThreadListener();
        DateTime startTime3 = DateTime.Now;
        DateTime startTime4 = DateTime.Now;

        TimeSpan duration3;
        TimeSpan duration4;

        while (true)
        {
            DataPackage dp = Connection.readEvent(4002, 3000);

            DateTime EndZeit = DateTime.Now;
            if (dp.Ip.Equals("192.168.101.3"))
            {
                duration3 = EndZeit - startTime3;
                System.Console.WriteLine("Duration to last receive: " + duration3);
                startTime3 = DateTime.Now;
            }
            if (dp.Ip.Equals("192.168.101.4"))
            {
                duration4 = EndZeit - startTime4;
                System.Console.WriteLine("Duration to last receive: " + duration4);
                startTime4 = DateTime.Now;
            }
            System.Console.WriteLine();
        }
    }

    ...Connection.cs
    public static DataPackage readEvent(int port, int size)
    {
        DataPackage dp = new DataPackage();
        byte[] bytes = new byte[size];
        String ip_client = "";
        IPHostEntry ipHostInfo = Dns.GetHostEntry(Dns.GetHostName());
        IPAddress ipAddress = ipHostInfo.AddressList[1];
        try
        {
            System.Console.Write("Wait...:");
            listener = new TcpListener(ipAddress, port);
            listener.Start();
            tcpClient = listener.AcceptTcpClient();
            tcpClient.NoDelay = true;
            tcpClient.Client.NoDelay = true;

            ip_client = tcpClient.Client.RemoteEndPoint.ToString();

            stream = tcpClient.GetStream();
            DateTime startTime = DateTime.Now;
            stream.Read(bytes, 0, bytes.Length);
            DateTime endTime = DateTime.Now;

            stream.Close();
            tcpClient.Close();
            listener.Stop();
            dp.Data = bytes;
            dp.setIP(ip_client);

            System.Console.Write("...received from " + ip_client + " busy:");
            TimeSpan duration = endTime - startTime;
            System.Console.WriteLine(duration.Seconds + "s");
        }
        catch (Exception e)
        {
            Console.WriteLine(e.Message);
            closeAllConnections();
            return null;
        }
        return dp;
    }

enter image description here

4

There are 4 best solutions below

5
On BEST ANSWER

I'm not surprised that you're seeing the inter-event delays. In your readEvent method, you're creating the sockets, then listening for an incoming connection, then performing your read() then closing all the sockets. It takes a little bit of time to set up your sockets. Its a better approach if you make the sockets members of your connection class, doing an initialization - create, listen, connect - and then in your readEvent method you would retrieve messages. A fancier approach might be to queue up all the events so that clients of your connection could just go and pick them up, but that involves a little asynchronous programming.

     public class Connection : IDisposable {
    private readonly Queue<DataPackage> messages = new Queue<DataPackage>();

    public object syncLock = new Object();
    public List<DataPackage> GetCurrentMessages() {
        Monitor.Enter(syncLock);
        var result = new List<DataPackage>();
        try {
            foreach (var item in messages) {
                result.Add(item);
            }
            messages.Clear();
        } finally {
            Monitor.Exit(syncLock);
        }
        return result;
    }

    private TcpListener listener;
    private TcpClient tcpClient;
    private int bufferSize;
    private bool running;
    public void Init(int port, int size) {
        // not real thread safe here
        if (running) return;
        running = true;
        bufferSize = size;
        IPHostEntry ipHostInfo = Dns.GetHostEntry(Dns.GetHostName());
        IPAddress ipAddress = ipHostInfo.AddressList[1];
        listener = new TcpListener(ipAddress, port);
        listener.Start();
        listener.BeginAcceptSocket(ar => { 
            tcpClient = listener.AcceptTcpClient();
            tcpClient.NoDelay = true;
            listener.EndAcceptTcpClient(ar);
            var t = new Thread(readEvent);
            t.Start();
        }, null);
    }

    private bool shouldRead = true;
    private void readEvent() {

        while (shouldRead) {
            byte[] bytes = new byte[bufferSize];

            try {
                Console.Write("Wait...:");

                var ip_client = tcpClient.Client.RemoteEndPoint.ToString();

                var stream = tcpClient.GetStream();
                DateTime startTime = DateTime.Now;
                stream.Read(bytes, 0, bytes.Length);
                DateTime endTime = DateTime.Now;
                var package = new DataPackage(); // add your stuff in here

                Monitor.Enter(syncLock);
                try {
                    messages.Enqueue(package);
                } finally {
                    Monitor.Exit(syncLock);
                }

                System.Console.Write("...received from " + ip_client + " busy:");
                TimeSpan duration = endTime - startTime;
                System.Console.WriteLine(duration.Seconds + "s");
            } catch (Exception e) {
                Console.WriteLine(e.Message);
            }
        }

    }

    public class DataPackage {}

    #region IDisposable Members
    private bool disposed;
    void Dispose(bool disposing) {
        if (!disposed) {
            if (disposing) {
                shouldRead = false;
                tcpClient.Close();
                listener.Stop();
            }
            //Clean up unmanaged resources
        }
        disposed = true;
    }
    public void Dispose() {
        Dispose(true);
    }
    #endregion
}
1
On

Use TcpClient.Available to see if enough bytes are available to read.

http://msdn.microsoft.com/en-us/library/system.net.sockets.tcpclient.available%28v=vs.110%29.aspx

0
On

I guess there are no any data loss. Read method in your case blocks thread until the first amount of data will come (even 1 byte). Theoretically you can wait infinite time, if client will keep "silence". If you want to limit wait time, use tcpClient.ReceiveTimeout, like this:

tcpClient.ReceiveTimeout = 3000;

So, there is everything quite right in your code, the problem is client keeps silence for 5 seconds.

PS: Why don't you use IPAddress.Any ? This Dns.GetHostEntry(Dns.GetHostName()) may take a very long time in some cases.

var listener = new TcpListener(IPAddress.Any, port);
3
On

Your TCP listener may be waiting for the buffer to fill.

  • Reduce the size of your buffer;
  • Send packets with the PUSH bit set; or
  • Your client is waiting to build up a significant packet size.

To set the buffer size, use the TcpClient.ReceiveBufferSize property.

To avoid delays while waiting for the buffer to fill up, set the TcpClient.NoDelay property to true.