2015-03-11

Http time consumption

I did this as a refresher for raw sockets, and the TCP/IP stack. Written in C#, the following class can be used to tally the amount of time spent waiting for a site. It measures the time between the request, an HTTP GET, and the end of the response, the TCP FIN.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Net;           // for IPEndPoint
using System.Net.Sockets;   // for socket
using System.IO;

namespace User.Network
{
    class SocketMonitor
    {
        // members
        private bool _bIsRunning;
        private DateTime _dtLastReset;
        private DateTime _dtTimeStart;
        private DateTime _dtTimeEnd;
        private TimeSpan _tsCumulative;
        private TimeSpan _tsMax;
        private TimeSpan _tsMin;
        private TimeSpan _tsLast;

        // properties
        public IPAddress ipaTarget { get; private set; }
        public int iPort { get; private set; }

        public DateTime dtLastReset
        {
            get
            {
                return _dtLastReset;
            }
            private set
            {
                _dtLastReset = value;
            }
        }

        public TimeSpan tsCumulative
        {
            get
            {
                return _tsCumulative;
            }
            private set
            {
                _tsCumulative = value;
            }
        }

        public TimeSpan tsMax
        {
            get
            {
                return _tsMax;
            }
            private set
            {
                _tsMax = value;
            }
        }

        public TimeSpan tsMin
        {
            get
            {
                return _tsMin;
            }
            private set
            {
                _tsMin = value;
            }
        }

        public TimeSpan tsLast
        {
            get
            {
                return _tsLast;
            }
            private set
            {
                _tsLast = value;
            }
        }

        // constructor
        public SocketMonitor(IPAddress target, int port)
        {
            ipaTarget = target;
            iPort = port;
            _bIsRunning = false;
            _dtTimeStart = DateTime.MinValue;
            _dtTimeEnd = DateTime.MinValue;
            Reset();
        }

        // member methods
        // timer control
        public void Reset()
        {
            tsCumulative = TimeSpan.Zero;
            tsMax = TimeSpan.Zero;
            tsMin = TimeSpan.MaxValue;
            tsLast = TimeSpan.Zero;
            dtLastReset = DateTime.Now;
        }// of method Reset()

        private void Start()
        {
            if (!_bIsRunning)
            {
                _bIsRunning = true;
                _dtTimeStart = DateTime.Now;
            }
        }// of method Start()

        private void Stop()
        {
            if(_bIsRunning)
            {
                _bIsRunning = false;
                _dtTimeEnd = DateTime.Now;
                TimeSpan duration = _dtTimeEnd - _dtTimeStart;
                if (duration > TimeSpan.Zero)
                {
                    tsLast = duration;
                    tsCumulative += duration;
                    if (duration > tsMax) tsMax = duration;
                    if (duration < tsMin) tsMin = duration;
                    Console.WriteLine("{0} Total: {1}, Max: {2}, Min: {3}, Last: {4}",
                        DateTime.Now.ToShortTimeString(), tsCumulative, tsMax, tsMin, tsLast);
                }
            }
        }// of method Stop()

        // executable
        // much of this taken from here: https://social.msdn.microsoft.com/Forums/en-US/6d19f326-0d8f-4672-90f9-29d3497fc803/raw-sockets-promiscuous-mode-only-sees-datagrams-tofrom-single-machine?forum=ncl
        public void Run()
        {
            int len_receive_buf = 4096;
            byte[] receive_buf = new byte[len_receive_buf];
            int cout_receive_bytes;
            Socket socket = new Socket(AddressFamily.InterNetwork, SocketType.Raw, ProtocolType.IP);
            socket.Blocking = false;
            socket.SetSocketOption(SocketOptionLevel.IP, SocketOptionName.HeaderIncluded, true);
            socket.ReceiveBufferSize = 65535;
            // the following linq frag was found here: http://stackoverflow.com/questions/1069103/how-to-get-my-own-ip-address-in-c
            IPAddress thisMachine = Dns.GetHostEntry(Dns.GetHostName()).AddressList.
                Where(o => o.AddressFamily == System.Net.Sockets.AddressFamily.InterNetwork).First();
            IPEndPoint ipEP = new IPEndPoint(thisMachine, 0); //(IPAddress.Any, 0);
            socket.Bind(ipEP);
            // found this here: http://www.progamercity.net/code-tut/5370-c-creating-simple-network-sniffer.html
            byte[] byTrue = new byte[4] { 1, 0, 0, 0 };
            byte[] byOut = new byte[4];
            socket.IOControl(IOControlCode.ReceiveAll, byTrue, byOut);
            while (true)
            {
                IAsyncResult ar =
                    socket.BeginReceive(receive_buf, 0, len_receive_buf, SocketFlags.None, null, this);
                cout_receive_bytes = socket.EndReceive(ar);
                Receive(receive_buf, cout_receive_bytes);
            }
        }// of method Run()

        private void Receive(byte[] buf, int len)
        {
            if ((len >= 20) && ((buf[0] >> 4) == 4))
            {
                //check IPv4 only; header info
                int IHL = buf[0] & 0xf; // number of words in header
                int byIHL = IHL * 4; // number of bytes in header
                IPAddress ipSender = new IPAddress((long)BitConverter.ToUInt32(buf, 12));
                IPAddress ipReceiver = new IPAddress((long)BitConverter.ToUInt32(buf, 16));
                // here are the protocol numbers: http://www.iana.org/assignments/protocol-numbers/protocol-numbers.xhtml
                if (buf[9] == 6)
                {
                    // tcp = 6; header info
                    int sourcePort = buf[byIHL] << 8 | buf[byIHL + 1];
                    int destPort = buf[byIHL + 2] << 8 | buf[byIHL + 3];
                    int dataOffset = buf[byIHL + 12] >> 4; // number of words in TCP header
                    int flags = (buf[byIHL + 12] & 1) << 8 | buf[byIHL + 13];
                    int byDataOffset = (dataOffset * 4) + byIHL; // offset past the IP & tcp headers
                    if ((ipReceiver.ToString() == ipaTarget.ToString()) && (destPort == iPort))
                    {
                        // from us -- looking for the GET in the payload
                        if (len > byDataOffset)
                        {
                            // a payload exists
                            byte[] payload = new byte[len - byDataOffset];
                            for (int index = 0; index < len - byDataOffset; index++)
                            {
                                payload[index] = buf[index + byDataOffset];
                            }
                            string outline1 = Encoding.ASCII.GetString(payload);

                            if (outline1.TrimStart().ToUpper().StartsWith("GET"))
                            {
                                // found the get... start the timer
                                Start();
                            }
                        }
                    }

                    if ((ipSender.ToString() == ipaTarget.ToString()) && (sourcePort == iPort))
                    {
                        // to us -- looking for a FIN flag (flags, bit 0)
                        if ((flags & 1) == 1)
                        {
                            // found the fin... stop the timer & save
                            Stop();
                        }
                    }
                }
            }
        }// of method Receive(byte[], int)

    }
}
Have fun!
Steve



No comments:

Post a Comment