GPS Socket communication (CONCOX)

Here is section 2 :

      public void ProcessMessages()
        {
            UInt16 sendCRC = 0;
            DateTime date;
            int year = 0;
            int month = 0;
            int day = 0;
            int hour = 0;
            int minute = 0;
            int second = 0;

            KeyValuePair<List<byte>, StateObject> byteState;
            KeyValuePair<UNPACK_STATUS, byte[]> status;
            byte[] receiveMessage = null;
            StateObject state = null;
            byte[] serialNumber = null;
            byte[] serverFlagBit = null;
            byte[] stringArray = null;
            string stringMessage = "";
            byte lengthOfCommand = 0;
            PROTOCOL_NUMBER protocolNumber = PROTOCOL_NUMBER.NONE;

            try
            {
                Boolean firstMessage = true;
                acceptDone.Set();
                //loop forever
                while (true)
                {
                    allDone.WaitOne();

                    //read fifo until empty
                    while (true)
                    {
                        //read one connection until buffer doesn't contain any more packets
                        byteState = ReadWrite(PROCESS_STATE.PROCESS, null, null, -1);

                        if (byteState.Value.fifoCount == -1) break;

                        state = byteState.Value;
                        while (true)
                        {
                            status = Unpack(byteState);
                            if (status.Key == UNPACK_STATUS.NOT_ENOUGH_BYTES)
                                break;

                            if (status.Key == UNPACK_STATUS.ERROR)
                            {
                                Console.WriteLine("Error : Bad Receive Message, Data");
                                break;
                            }

                            //message is 2 start bytes + 1 byte (message length) + 1 byte message length + 2 end bytes
                            receiveMessage = status.Value;

                            int messageLength = receiveMessage[2];
                            Console.WriteLine("Status : '{0}', Receive Message : '{1}'", status.Key == UNPACK_STATUS.GOOD_MESSAGE ? "Good" : "Bad", BytesToString(receiveMessage.Take(messageLength + 5).ToArray()));

                            if (status.Key != UNPACK_STATUS.GOOD_MESSAGE)
                            {
                                break;
                            }
                            else
                            {
                                if (firstMessage)
                                {
                                    if (receiveMessage[3] != 0x01)
                                    {
                                        Console.WriteLine("Error : Expected Login Message : '{0}'", BytesToString(receiveMessage));
                                        break;
                                    }
                                    firstMessage = false;
                                }

                                //skip start bytes, message length.  then go back 4 bytes (CRC and serial number)
                                serialNumber = receiveMessage.Skip(2 + 1 + messageLength - 4).Take(2).ToArray();

                                protocolNumber = (PROTOCOL_NUMBER)receiveMessage[3];
                                Console.WriteLine("Protocol Number : '{0}'",protocolNumber.ToString());
                                switch (protocolNumber)
                                {
                                    case PROTOCOL_NUMBER.LOGIN_MESSAGE:
                                        serialNumber.CopyTo(loginResponse, 4);

                                        sendCRC = crc_bytes(loginResponse.Skip(2).Take(loginResponse.Length - 6).ToArray());

                                        loginResponse[loginResponse.Length - 4] = (byte)((sendCRC >> 8) & 0xFF);
                                        loginResponse[loginResponse.Length - 3] = (byte)((sendCRC) & 0xFF);

                                        //
                                        string IMEI = Encoding.ASCII.GetString(receiveMessage.Skip(4).Take(messageLength - 5).ToArray());
                                        byteState.Value.IMEI = IMEI;

                                        Console.WriteLine("Received good login message from Serial Number : '{0}', Terminal ID = '{1}'", "0x" + serialNumber[0].ToString("X2") + serialNumber[1].ToString("X2"), IMEI);

                                        Console.WriteLine("Send Message : '{0}'", BytesToString(loginResponse));
                                        Send(state.workSocket, loginResponse);

                                        WriteDBMessageLogin loginMessage = new WriteDBMessageLogin() { message = DATABASE_MESSAGE_TYPE.LOGIN, IMEI = IMEI, date = DateTime.Now };

                                        WriteDBAsync.ReadWriteFifo(WriteDBAsync.Mode.WRITE, loginMessage);

                                        Console.WriteLine("Wrote to database");
                                        break;
                                    case PROTOCOL_NUMBER.LOCATION_DATA:
                                        year = receiveMessage[4];
                                        month = receiveMessage[5];
                                        day = receiveMessage[6];
                                        hour = receiveMessage[7];
                                        minute = receiveMessage[8];
                                        second = receiveMessage[9];

                                        date = new DateTime(2000 + year, month, day, hour, minute, second);

                                        WriteDBMessageLocation locationMessage = new WriteDBMessageLocation();
                                        locationMessage.message = DATABASE_MESSAGE_TYPE.LOCATION;

                                        locationMessage.trackTime = date;
                                        locationMessage.currTime = DateTime.Now;

                                        locationMessage.lattitude = new byte[4];
                                        Array.Copy(receiveMessage, 11, locationMessage.lattitude, 0, 4);

                                        locationMessage.longitude = new byte[4];
                                        Array.Copy(receiveMessage, 15, locationMessage.longitude, 0, 4);
                                        locationMessage.speed = receiveMessage[19];

                                        locationMessage.courseStatus = new byte[2];
                                        Array.Copy(receiveMessage, 20, locationMessage.courseStatus, 0, 2);

                                        locationMessage.IMEI = byteState.Value.IMEI;
                                        WriteDBAsync.ReadWriteFifo(WriteDBAsync.Mode.WRITE, locationMessage);


                                        Console.WriteLine("Received good location message from Serial Number '{0}', Time = '{1}'", "0x" + serialNumber[0].ToString("X2") + serialNumber[1].ToString("X2"), date.ToLongDateString());
                                        break;

                                    case PROTOCOL_NUMBER.ALARM_DATA:

                                        //first response
                                        int alarmPacketLen = alarmResponse.Length - 5;
                                        alarmResponse[2] = (byte)(alarmPacketLen & 0xFF);

                                        serialNumber.CopyTo(alarmResponse, alarmPacketLen - 1);

                                        sendCRC = crc_bytes(alarmResponse.Skip(2).Take(alarmPacketLen - 1).ToArray());

                                        alarmResponse[alarmPacketLen + 1] = (byte)((sendCRC >> 8) & 0xFF);
                                        alarmResponse[alarmPacketLen + 2] = (byte)((sendCRC) & 0xFF);

                                        Console.WriteLine("Send Alarm Response Message : '{0}'", BytesToString(alarmResponse));
                                        Send(state.workSocket, alarmResponse);


                                        //second response
                                        year = receiveMessage[4];
                                        month = receiveMessage[5];
                                        day = receiveMessage[6];
                                        hour = receiveMessage[7];
                                        minute = receiveMessage[8];
                                        second = receiveMessage[9];

                                        date = new DateTime(2000 + year, month, day, hour, minute, second);
                                        Console.WriteLine("Received good alarm message from Serial Number '{0}', Time = '{1}'", "0x" + serialNumber[0].ToString("X2") + serialNumber[1].ToString("X2"), date.ToLongDateString());
                                        int alarmDataAddressPacketLen = alarmDataAddressResponse.Length - 5;
                                        alarmDataAddressResponse[2] = (byte)(alarmDataAddressPacketLen & 0xFF);

                                        serialNumber.CopyTo(alarmDataAddressResponse, alarmDataAddressPacketLen - 1);

                                        sendCRC = crc_bytes(alarmDataAddressResponse.Skip(2).Take(alarmDataAddressPacketLen - 1).ToArray());

                                        alarmDataAddressResponse[alarmDataAddressPacketLen + 1] = (byte)((sendCRC >> 8) & 0xFF);
                                        alarmDataAddressResponse[alarmDataAddressPacketLen + 2] = (byte)((sendCRC) & 0xFF);

                                        Console.WriteLine("Send Alarm Data Address Message : '{0}'", BytesToString(alarmDataAddressResponse));
                                        Send(state.workSocket, alarmDataAddressResponse);

                                        break;

                                    case PROTOCOL_NUMBER.STATUS_INFO:
                                        serialNumber.CopyTo(heartbeatResponse, 4);

                                        byte info = receiveMessage[4];
                                        byte voltage = receiveMessage[5];
                                        byte GSMsignalStrength = receiveMessage[6];
                                        UInt16 alarmLanguage = (UInt16)((receiveMessage[7] << 8) | receiveMessage[8]);

                                        ALARM alarm = (ALARM)((info >> 3) & 0x07);

                                        sendCRC = crc_bytes(heartbeatResponse.Skip(2).Take(heartbeatResponse.Length - 6).ToArray());

                                        heartbeatResponse[heartbeatResponse.Length - 4] = (byte)((sendCRC >> 8) & 0xFF);
                                        heartbeatResponse[heartbeatResponse.Length - 3] = (byte)((sendCRC) & 0xFF);

                                        Console.WriteLine("Received good status message from Serial Number : '{0}', INFO : '0x{1}{2}{3}{4}'",
                                            "0x" + serialNumber[0].ToString("X2") + serialNumber[1].ToString("X2"),
                                            info.ToString("X2"), voltage.ToString("X2"), GSMsignalStrength.ToString("X2"),
                                            alarmLanguage.ToString("X4"));

                                        Console.WriteLine("Send Message : '{0}'", BytesToString(heartbeatResponse));
                                        Send(state.workSocket, heartbeatResponse);

                                        switch (alarm)
                                        {
                                            //reset cut off alarm
                                            case ALARM.POWER_CUT_ALARM:
                                                int connectOilAndElectricityPacketLen = connectOilAndEletricity.Length - 5;
                                                serialNumber.CopyTo(connectOilAndEletricity, connectOilAndElectricityPacketLen - 1);
                                                sendCRC = crc_bytes(connectOilAndEletricity.Skip(2).Take(connectOilAndEletricity.Length - 6).ToArray());
                                                connectOilAndEletricity[connectOilAndEletricity.Length - 4] = (byte)((sendCRC >> 8) & 0xFF);
                                                connectOilAndEletricity[connectOilAndEletricity.Length - 3] = (byte)((sendCRC) & 0xFF);

                                                serverFlagBit = new byte[4];
                                                Array.Copy(connectOilAndEletricity, 5, serverFlagBit, 0, 4);

                                                lengthOfCommand = connectOilAndEletricity[4];
                                                stringArray = new byte[lengthOfCommand - 4]; //do not include server flag bit
                                                Array.Copy(connectOilAndEletricity, 9, stringArray, 0, lengthOfCommand - 4);
                                                stringMessage = Encoding.ASCII.GetString(stringArray);

                                                Console.WriteLine("Reset Oil and Electricity, Server Flag Bit : '{0}{1}{2}{3}', Message : '{4}'",
                                                  serverFlagBit[0].ToString("X2"),
                                                  serverFlagBit[1].ToString("X2"),
                                                  serverFlagBit[2].ToString("X2"),
                                                  serverFlagBit[3].ToString("X2"),
                                                  stringMessage);
                                                Send(state.workSocket, connectOilAndEletricity);
                                                break;
                                         }

                                        break;

                                    case PROTOCOL_NUMBER.STRING_INFO :
                                        lengthOfCommand = receiveMessage[4];
                                        serverFlagBit = new byte[4];
                                        Array.Copy(receiveMessage, 5, serverFlagBit, 0, 4);
                                        stringArray = new byte[lengthOfCommand - 4]; //do not include server flag bit
                                        Array.Copy(receiveMessage, 9, stringArray, 0, lengthOfCommand - 4);
                                        stringMessage = Encoding.ASCII.GetString(stringArray);

                                        Console.WriteLine("String Message, Server Flag Bit : '{0}{1}{2}{3}', Message : '{4}'", 
                                            serverFlagBit[0].ToString("X2"),
                                            serverFlagBit[1].ToString("X2"),
                                            serverFlagBit[2].ToString("X2"),
                                            serverFlagBit[3].ToString("X2"),
                                            stringMessage);

                                        break;

                                } //end switch
                            }// End if
                        } //end while
                    }//end while fifo > 0
                    allDone.Reset();
                }//end while true
            }
            catch (Exception e)
            {

                Console.WriteLine(e.Message);
            }

        }

        static string BytesToString(byte[] bytes)
        {

            return string.Join("", bytes.Select(x => x.ToString("X2")));
        }
        static KeyValuePair<UNPACK_STATUS, byte[]> Unpack(KeyValuePair<List<byte>, StateObject> bitState)
        {
            List<byte> working_buffer = bitState.Key;
            //return null indicates an error
            if (working_buffer.Count() < 3) return new KeyValuePair<UNPACK_STATUS, byte[]>(UNPACK_STATUS.NOT_ENOUGH_BYTES, null);

            int len = working_buffer[2];

            if (working_buffer.Count < len + 5) return new KeyValuePair<UNPACK_STATUS, byte[]>(UNPACK_STATUS.NOT_ENOUGH_BYTES, null);
            // check start and end bytes
            // remove message fro workig buffer and dictionary 
            KeyValuePair<List<byte>, StateObject> byteState = ReadWrite(PROCESS_STATE.UNPACK, null, null, bitState.Value.connectionNumber);
            if (byteState.Key.Count == 0) return new KeyValuePair<UNPACK_STATUS, byte[]>(UNPACK_STATUS.ERROR, null);

            List<byte> packet = byteState.Key;

            //crc test
            byte[] crc = packet.Skip(len + 1).Take(2).ToArray();
            ushort crcShort = (ushort)((crc[0] << 8) | crc[1]);
            //skip start bytes, crc, and end bytes
            ushort CalculatedCRC = crc_bytes(packet.Skip(2).Take(len - 1).ToArray());

            if (CalculatedCRC != crcShort)
            {
                return new KeyValuePair<UNPACK_STATUS, byte[]>(UNPACK_STATUS.BAD_CRC, packet.ToArray());
            }

            return new KeyValuePair<UNPACK_STATUS, byte[]>(UNPACK_STATUS.GOOD_MESSAGE, packet.ToArray());
        }
        static public UInt16 crc_bytes(byte[] data)
        {
            ushort crc = 0xFFFF;

            for (int i = 0; i < data.Length; i++)
            {
                crc ^= (ushort)(Reflect(data[i], 8) << 8);
                for (int j = 0; j < 8; j++)
                {
                    if ((crc & 0x8000) > 0)
                        crc = (ushort)((crc << 1) ^ 0x1021);
                    else
                        crc <<= 1;
                }
            }
            crc = Reflect(crc, 16);
            crc = (ushort)~crc;
            return crc;
        }
        static public ushort Reflect(ushort data, int size)
        {
            ushort output = 0;
            for (int i = 0; i < size; i++)
            {
                int lsb = data & 0x01;
                output = (ushort)((output << 1) | lsb);
                data >>= 1;
            }
            return output;
        }

        static KeyValuePair<List<byte>, StateObject> ReadWrite(PROCESS_STATE ps, Socket handler, IAsyncResult ar, long unpackConnectionNumber)
        {
            KeyValuePair<List<byte>, StateObject> byteState = new KeyValuePair<List<byte>, StateObject>(); ;
            StateObject stateObject = null;
            int bytesRead = -1;
            int workingBufferLen = 0;
            List<byte> working_buffer = null;
            byte[] buffer = null;

            Object thisLock1 = new Object();

            lock (thisLock1)
            {
                switch (ps)
                {
                    case PROCESS_STATE.ACCEPT:

                        acceptDone.WaitOne();
                        acceptDone.Reset();
                        stateObject = new StateObject();
                        stateObject.buffer = new byte[BUFFER_SIZE];
                        connectionDict.Add(connectionNumber, new KeyValuePair<List<byte>, StateObject>(new List<byte>(), stateObject));
                        stateObject.connectionNumber = connectionNumber++;

                        stateObject.workSocket = handler;

                        byteState = new KeyValuePair<List<byte>, StateObject>(null, stateObject);
                        acceptDone.Set();
                        break;

                    case PROCESS_STATE.READ:
                        //catch when client disconnects

                        //wait if accept is being called
                        //acceptDone.WaitOne();
                        try
                        {
                            stateObject = ar.AsyncState as StateObject;
                            // Read data from the client socket. 
                            bytesRead = stateObject.workSocket.EndReceive(ar);

                            if (bytesRead > 0)
                            {
                                byteState = connectionDict[stateObject.connectionNumber];

                                buffer = new byte[bytesRead];
                                Array.Copy(byteState.Value.buffer, buffer, bytesRead);

                                byteState.Key.AddRange(buffer);
                            }
                            //only put one instance of connection number into fifo
                            if (!fifo.Contains(byteState.Value.connectionNumber)) {

                                fifo.Add(byteState.Value.connectionNumber);
                            }
                        }
                        catch (Exception ex)
                        {
                            //will get here if client disconnects
                            fifo.RemoveAll(x => x == byteState.Value.connectionNumber);
                            connectionDict.Remove(byteState.Value.connectionNumber);
                            byteState = new KeyValuePair<List<byte>, StateObject>(new List<byte>(), null);
                        }
                        break;

                    case PROCESS_STATE.PROCESS:
                        if (fifo.Count > 0)
                        {
                            //get message from working buffer
                            //unpack will later delete message
                            //remove connection number from fifo
                            // the list in the key in known as the working buffer
                            byteState = new KeyValuePair<List<byte>, StateObject>(connectionDict[fifo[0]].Key, connectionDict[fifo[0]].Value);
                            fifo.RemoveAt(0);
                            //put a valid value in fifoCount so -1 below can be detected.
                            byteState.Value.fifoCount = fifo.Count;
                        }
                        else
                        {
                            //getting here is normal when there is no more work to be performed
                            //set fifocount to zero so rest of code know fifo was empty so code waits for next receive message
                            byteState = new KeyValuePair<List<byte>, StateObject>(null, new StateObject() { fifoCount = -1 });
                        }
                        break;

                    case PROCESS_STATE.UNPACK:
                        try
                        {
                            working_buffer = connectionDict[unpackConnectionNumber].Key;
                            workingBufferLen = working_buffer[2];
                            if ((working_buffer[0] != 0x78) && (working_buffer[1] != 0x78) && (working_buffer[workingBufferLen + 3] != 0x0D) && (working_buffer[workingBufferLen + 4] != 0x0A))
                            {



                                working_buffer.Clear();
                                return new KeyValuePair<List<byte>, StateObject>(new List<byte>(), null);
                            }
                            List<byte> packet = working_buffer.GetRange(0, workingBufferLen + 5);
                            working_buffer.RemoveRange(0, workingBufferLen + 5);
                            byteState = new KeyValuePair<List<byte>, StateObject>(packet, null);
                        }
                        catch(Exception ex)
                        {

                            int testPoint = 0;
                        }

                        break;
                }// end switch
            }

            return byteState;
        }

        static void Send(Socket handler, String data)
        {
            // Convert the string data to byte data using ASCII encoding.
            byte[] byteData = Encoding.ASCII.GetBytes(data);

            // Begin sending the data to the remote device.
            handler.BeginSend(byteData, 0, byteData.Length, 0,
                new AsyncCallback(SendCallback), handler);
        }
        static void Send(Socket socket, byte[] data)
        {
            // Convert the string data to byte data using ASCII encoding.
            // byte[] byteData = Encoding.ASCII.GetBytes(data);

            // Begin sending the data to the remote device.
            socket.BeginSend(data, 0, data.Length, 0,
                new AsyncCallback(SendCallback), socket);
        }
        static void SendCallback(IAsyncResult ar)
        {
            try
            {
                // Retrieve the socket from the state object.
                Socket handler = ar.AsyncState as Socket;

                // Complete sending the data to the remote device.
                int bytesSent = handler.EndSend(ar);
                // Console.WriteLine("Sent {0} bytes to client.", bytesSent);

            }
            catch (Exception e)
            {
                // Console.WriteLine(e.ToString());
                int myerror = -1;
            }
        }

        public static void AcceptCallback(IAsyncResult ar)
        {
            try
            {

                // Get the socket that handles the client request.
                // Retrieve the state object and the handler socket
                // from the asynchronous state object.

                Socket listener = (Socket)ar.AsyncState;
                Socket handler = listener.EndAccept(ar);

                // Create the state object.
                StateObject state = ReadWrite(PROCESS_STATE.ACCEPT, handler, ar, - 1).Value;

                handler.BeginReceive(state.buffer, 0, BUFFER_SIZE, 0,
                    new AsyncCallback(ReadCallback), state);

            }
            catch (Exception ex)
            {
                int myerror = -1;
            }
        }

        public static void ReadCallback(IAsyncResult ar)
        {
            try
            {
                StateObject state = ar.AsyncState as StateObject;
                Socket handler = state.workSocket;

                // Read data from the client socket. 
                KeyValuePair<List<byte>, StateObject> byteState = ReadWrite(PROCESS_STATE.READ, handler, ar, -1);

                if (byteState.Value != null)
                {
                    allDone.Set();
                    handler.BeginReceive(state.buffer, 0, BUFFER_SIZE, 0,
                        new AsyncCallback(ReadCallback), state);
                }
                else
                {
                    int testPoint = 0;
                }
            }
            catch (Exception ex)
            {
                int myerror = -1;
            }

            // Signal the main thread to continue.  
            allDone.Set();
        }
    }
}

Here is the modified code Section 1 :

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Net;
using System.Net.Sockets;
using System.Threading;
using System.Data;
using System.Data.SqlClient;
using System.ComponentModel;
using System.Timers;

namespace ConcoxServer
{
    public class StateObject
    {
        public long connectionNumber = -1;
        public Socket workSocket { get; set; }
        public byte[] buffer { get; set; }
        public int fifoCount { get; set;}
        public string IMEI { get; set; }
    }

    public enum PROTOCOL_NUMBER
    {
        LOGIN_MESSAGE = 0x01,
        LOCATION_DATA = 0x12,
        STATUS_INFO = 0X13,
        STRING_INFO = 0X15,
        ALARM_DATA = 0X16,
        GPS_QUERY_ADDR_PHONE_NUM = 0X1A,
        COMMAND_INFO = 0X80,
        NONE
    }
    public enum UNPACK_STATUS
    {
        ERROR,
        NOT_ENOUGH_BYTES,
        BAD_CRC,
        GOOD_MESSAGE,
        DEFAULT
    }
    public enum PROCESS_STATE
    {
        ACCEPT,
        READ,
        PROCESS,
        UNPACK
    }
    public enum ALARM : Byte
    {
        NORMAL = 0,
        SHOCK_ALARM = 1,
        POWER_CUT_ALARM = 2,
        LOW_BATTERY = 3,
        SOS = 4
    }

    class Program
    {
        const string IP = "127.0.0.1";
        const int PORT = 8841;
        const Boolean test = true;

        static void Main(string[] args)
        {

            GPS gps = new GPS(IP, PORT, test);

            Console.WriteLine("Connection Ended");

        }
    }
    public class GPS
    {
        const int BUFFER_SIZE = 1024;
        const string CONNECT_STRING = @"Data Source=.\SQLEXPRESS;Initial Catalog=GPSTracker;Integrated Security=SSPI;";
        const string LOGIN_INSERT_COMMMAND_TEXT = "use GPSTracker INSERT INTO Login (TerminalID,Date) VALUES(@TerminalID,@Date)";
        const string LOCATION_INSERT_COMMMAND_TEXT = "INSERT INTO T_Tracking (IMEI,TrackTime, CurrTime, Longitude, Lattitude,speed)" +
            "VALUES (@IMEI, @TrackTime, @CurrTime, @Longitude, @Lattitude, @speed)";
        static SqlConnection conn = null;
        static SqlCommand cmdLogin = null;
        static SqlCommand cmdLocation = null;

        static byte[] loginResponse = { 0x78, 0x78, 0x05, 0x01, 0x00, 0x00, 0x00, 0x00, 0x0D, 0x0A };
        static byte[] alarmResponse = { 0x78, 0x78, 0x05, 0x16, 0x00, 0x00, 0x00, 0x00, 0x0D, 0x0A };
        static byte[] alarmDataAddressResponse = { 0x78, 0x78, 0x00, 0x97, 0x7E,
                                          0x00, 0x00, 0x00, 0x01,
                                          0x41, 0x4C, 0x41, 0x52, 0x4D, 0x53, 0x4D, 0x53,   //ALARMS
                                          0x26, 0x26,                                       //&&
                                          0x80, 0x00, 0x72, 0x00, 0x79, 0x00, 0x78, 0x00,   // PHONE HOME
                                          0x69, 0x00, 0x32, 0x00, 0x72, 0x00, 0x79, 0x00,
                                          0x77, 0x00, 0x69,
                                          0x26, 0x26,                                       //&&
                                          0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,   // Phone Numbe
                                          0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
                                          0x00, 0x00, 0x00, 0x00, 0x00,
                                          0x23, 0x23,                                       //##
                                          0x00, 0x00,                                       //serial number
                                          0x00, 0x00,                                       //check bytes
                                          0x0D, 0x0A                                        //stop bytes
                                      };
        static byte[] heartbeatResponse = { 0x78, 0x78, 0x05, 0x13, 0x00, 0x00, 0x00, 0x00, 0x0D, 0x0A };
        static byte[] connectOilAndEletricity = {
                                          0x78, 0x78, 0x16, 0x80, 0x10, 0x12, 0x34, 0x56, 0x78, 0x48,  //server flag bit 0x12345678
                                          0x46, 0x59, 0x44, 0x2C, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30,
                                          0x23, 0x00, 0x00, 0x00, 0x00, 0x0D, 0x0A
                                     }; 

        static long connectionNumber = 0;
        //mapping of connection number to StateObject
        static Dictionary<long, KeyValuePair<List<byte>, StateObject>> connectionDict = new Dictionary<long, KeyValuePair<List<byte>, StateObject>>();
        //fifo contains list of connections number wait with receive data
        public static List<long> fifo = new List<long>();

        public static AutoResetEvent allDone = new AutoResetEvent(false);
        public static AutoResetEvent acceptDone = new AutoResetEvent(false);

        public GPS(string IP, int port, Boolean test)
        {
            try
            {
                conn = new SqlConnection(CONNECT_STRING);
                conn.Open();

                cmdLogin = new SqlCommand(LOGIN_INSERT_COMMMAND_TEXT, conn);
                cmdLogin.Parameters.Add("@TerminalID", SqlDbType.NVarChar, 8);
                cmdLogin.Parameters.Add("@Date", SqlDbType.DateTime);

                cmdLocation = new SqlCommand(LOCATION_INSERT_COMMMAND_TEXT, conn);
                cmdLocation.Parameters.Add("@IMEI", SqlDbType.NVarChar, 50);
                cmdLocation.Parameters.Add("@TrackTime", SqlDbType.DateTime);
                cmdLocation.Parameters.Add("@currTime", SqlDbType.DateTime);
                cmdLocation.Parameters.Add("@Longitude", SqlDbType.NChar, 50);
                cmdLocation.Parameters.Add("@Lattitude", SqlDbType.NVarChar, 50);
                cmdLocation.Parameters.Add("@speed", SqlDbType.Float);

            }
            catch (Exception ex)
            {
                Console.WriteLine("Error : '{0}'", ex.Message);
                //Console.ReadLine();
                return;
            }

            try
            {
                //initialize the timer for writing to database.
                WriteDBAsync.WriteDatabase();
                StartListening(IP, port, test);

                // Open 2nd listener to simulate two devices, Only for testing
                //StartListening(IP, port + 1, test);

                ProcessMessages();
            }
            catch (Exception ex)
            {
                Console.WriteLine("Error : '{0}'", ex.Message);
                //Console.ReadLine();
                return;
            }
        }
        public enum DATABASE_MESSAGE_TYPE
        {
            LOGIN,
            LOCATION
        }
        public class WriteDBMessage
        {
            public DATABASE_MESSAGE_TYPE message { get; set; }
        }
        public class WriteDBMessageLogin : WriteDBMessage
        {
            public DateTime date { get; set; }
            public string IMEI { get; set; }
        }
        public class WriteDBMessageLocation : WriteDBMessage 
        {
            public string IMEI { get; set; }
            public DateTime trackTime { get; set; }
            public DateTime currTime { get; set; }
            public byte[] longitude { get; set; }
            public byte[] lattitude { get; set; }
            public float speed { get; set; }
            public byte[] courseStatus { get; set; }
        }

        public static class WriteDBAsync
        {
            public enum Mode
            {
                READ,
                WRITE
            }
            public static List<WriteDBMessage> fifo = new List<WriteDBMessage>();
            public static System.Timers.Timer timer = null;

            public static void WriteDatabase()
            {
                timer = new System.Timers.Timer(1000);
                timer.Elapsed += Timer_Elapsed;
                timer.Start();
            }
            public static WriteDBMessage ReadWriteFifo(Mode mode, WriteDBMessage message)
            {
                Object thisLock2 = new Object();

                lock (thisLock2)
                {
                    switch (mode)
                    {
                        case Mode.READ:
                            if(fifo.Count > 0)
                            {
                                message = fifo[0];
                                fifo.RemoveAt(0);
                            }
                            break;
                        case Mode.WRITE:
                            fifo.Add(message);
                            break;
                    }

                }
                return message;
            }
            static void Timer_Elapsed(object sender, ElapsedEventArgs e)
            {
                timer.Enabled = false;
                WriteDBMessage row = null;
                int rowsAdded = 0;
                uint number = 0;
                double lat = 0;
                string latStr = "";
                double lon = 0;
                string longStr = "";
                int degrees = 0;
                double minutes = 0;
                try
                {

                    while((row = ReadWriteFifo(Mode.READ, null)) != null)
                    {
                        switch(row.message)
                        {
                            case DATABASE_MESSAGE_TYPE.LOGIN:
                                cmdLogin.Parameters["@TerminalID"].Value = ((WriteDBMessageLogin)row).IMEI;
                                cmdLogin.Parameters["@Date"].Value = ((WriteDBMessageLogin)row).date;
                                rowsAdded = cmdLogin.ExecuteNonQuery();
                                break;
                            case DATABASE_MESSAGE_TYPE.LOCATION:
                                cmdLocation.Parameters["@IMEI"].Value = ((WriteDBMessageLocation)row).IMEI;
                                cmdLocation.Parameters["@TrackTime"].Value = ((WriteDBMessageLocation)row).trackTime;
                                cmdLocation.Parameters["@currTime"].Value = ((WriteDBMessageLocation)row).currTime;

                                number = BitConverter.ToUInt32(((WriteDBMessageLocation)row).longitude.Reverse().ToArray(), 0);
                                lon = (180 * number) / 324000000.0;

                                degrees = (int)lon;
                                minutes = 60 * (lon - degrees);
                                longStr = string.Format("{0}º{1}{2}", degrees, minutes, (((WriteDBMessageLocation)row).courseStatus[0] & 0x08) == 0 ? "E" : "W");

                                cmdLocation.Parameters["@Longitude"].Value = longStr;

                                number = BitConverter.ToUInt32(((WriteDBMessageLocation)row).lattitude.Reverse().ToArray(), 0);
                                lat = (90 * number) / 162000000.0;

                                degrees = (int)lat;
                                minutes = 60 * (lat - degrees);
                                latStr = string.Format("{0}º{1}{2}", degrees, minutes, (((WriteDBMessageLocation)row).courseStatus[0] & 0x04) == 0 ? "S" : "N");

                                cmdLocation.Parameters["@Lattitude"].Value = latStr;
                                cmdLocation.Parameters["@speed"].Value = ((WriteDBMessageLocation)row).speed;
                                rowsAdded = cmdLocation.ExecuteNonQuery();
                                break;
                        }
                    }
                }
                catch (Exception ex)
                {
                    //Console.WriteLine("Error : '{0}'", ex.Message);
                }
                timer.Enabled = true;
            }
        }

        public void StartListening(string IP, int port, Boolean test)
        {
            try
            {
                // Establish the local endpoint for the socket.
                // The DNS name of the computer
                // running the listener is "host.contoso.com".
                IPHostEntry ipHostInfo = Dns.GetHostEntry(IP);  //Dns.Resolve(Dns.GetHostName());
                IPAddress ipAddress = ipHostInfo.AddressList[0];
                //IPAddress local = IPAddress.Parse(IP);

                IPEndPoint localEndPoint = null;
                if (test)
                {
                    localEndPoint = new IPEndPoint(IPAddress.Any, port);
                }
                else
                {
                    localEndPoint = new IPEndPoint(ipAddress, port);
                }

                // Create a TCP/IP socket.
                Socket listener = new Socket(AddressFamily.InterNetwork,
                    SocketType.Stream, ProtocolType.Tcp);
                // Bind the socket to the local endpoint and listen for incoming connections.


                allDone.Reset();
                acceptDone.Reset();
                listener.Bind(localEndPoint);
                listener.Listen(100);

                //login code, wait for 1st message
                Console.WriteLine("Wait 5 seconds for login message");

                listener.BeginAccept(new AsyncCallback(AcceptCallback), listener);
            }
            catch (Exception e)
            {
                Console.WriteLine(e.Message);
            }
        }