Question

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

Was it helpful?

Solution

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
}

OTHER TIPS

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);

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.

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

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top