tcp listner с производительностью потоковой передачи

Я создаю приложение GPS, в котором устройства GPS отправляют местоположение через порт TCP. Я создаю службу для чтения этих сообщений и сохранения их в базе данных.

    static void Main(string[] args)
    {
        TcpListener serverSocket = new TcpListener(8889);
        TcpClient clientSocket = default(TcpClient);
        int counter = 0;

        serverSocket.Start();
        //  Console.WriteLine(" >> " + "Server Started");

        counter = 0;
        while (true)
        {
            counter += 1;
            clientSocket = serverSocket.AcceptTcpClient();
            //  Console.WriteLine(" >> " + "Client No:" + Convert.ToString(counter) + " started!");
            handleClinet client = new handleClinet();
            client.startClient(clientSocket, Convert.ToString(counter));
        }

        clientSocket.Close();
        serverSocket.Stop();
        //   Console.WriteLine(" >> " + "exit");
        Console.ReadLine();
    }
}

//Class to handle each client request separatly
public class handleClinet
{

    static void WriteLog(string message, EventLogEntryType type)
    {

        using (EventLog eventLog = new EventLog("Application"))
        {
            eventLog.Source = "Application";
            eventLog.WriteEntry(message, type, 101, 1);
        }
    }
    static int InsideDangerArea(double Lat, double Lng)
    {

        string point = "POINT(" + Lng + "  " + Lat + ")";

        string ConnStr = "Data Source =.; Initial Catalog = GPS_Tracking;Integrated Security = True";
        using (SqlConnection conn = new SqlConnection(ConnStr))
        {
            conn.Open();
            using (SqlCommand comm = new SqlCommand("Select id from T_Geofncies", conn))
            {
                DataTable dt = new DataTable();
                dt.Load(comm.ExecuteReader());

                foreach (DataRow dr in dt.Rows)
                {
                    string Query = "  DECLARE @g geometry; DECLARE @h geometry; SET @g = (select(points) from T_Geofncies where id=" + dr["id"].ToString() + " );";
                    Query += " SET @h = geometry::STGeomFromText('" + point + "', 4326); SELECT @g.STContains(@h);";
                    comm.CommandText = Query;
                    int Val = Convert.ToInt32(comm.ExecuteScalar());
                    if (Val == 1)
                    {
                        conn.Close();
                        conn.Dispose();
                        return Convert.ToInt32(dr["id"]);
                    }

                }
            }
            conn.Close();
            conn.Dispose();
        }
        return 0;
    }

    static int OutsideSafeArea(double Lat, double Lng)
    {

        string point = "POINT(" + Lng + "  " + Lat + ")";

        string ConnStr = "Data Source =.; Initial Catalog = GPS_Tracking;Integrated Security = True";
        using (SqlConnection conn = new SqlConnection(ConnStr))
        {
            conn.Open();
            using (SqlCommand comm = new SqlCommand("Select id from T_SafeArea", conn))
            {
                DataTable dt = new DataTable();
                dt.Load(comm.ExecuteReader());

                foreach (DataRow dr in dt.Rows)
                {
                    string Query = "  DECLARE @g geometry; DECLARE @h geometry; SET @g = (select(points) from T_SafeArea where id=" + dr["id"].ToString() + " );";
                    Query += " SET @h = geometry::STGeomFromText('" + point + "', 4326); SELECT @g.STContains(@h);";
                    comm.CommandText = Query;
                    int Val = Convert.ToInt32(comm.ExecuteScalar());
                    if (Val == 1)
                    {
                        conn.Close();
                        conn.Dispose();
                        return Convert.ToInt32(dr["id"]);
                    }

                }
            }
            conn.Close();
            conn.Dispose();
        }
        return 0;
    }
    static SqlGeography GetGeographyFromText(String pText)
    {
        SqlString ss = new SqlString(pText);
        SqlChars sc = new SqlChars(ss);
        try
        {
            return SqlGeography.STPointFromText(sc, 4326);
        }
        catch (Exception ex)
        {
            throw ex;
        }
    }
    TcpClient clientSocket;
    string clNo;
    public void startClient(TcpClient inClientSocket, string clineNo)
    {
        this.clientSocket = inClientSocket;
        this.clNo = clineNo;
        Thread ctThread = new Thread(doChat);
        ctThread.Start();
    }
    private void doChat()
    {
        string ConnStr = "Data Source =.; Initial Catalog = GPS_Tracking;Integrated Security = True";

        int requestCount = 0;
        // byte[] bytesFrom = new byte[10025];
        string dataFromClient = null;
        Byte[] sendBytes = null;
        string serverResponse = null;
        string rCount = null;
        requestCount = 0;

        while ((true))
        {
            try
            {
                requestCount = requestCount + 1;
                NetworkStream networkStream = clientSocket.GetStream();
                int i;
                int size = (int)clientSocket.ReceiveBufferSize;
                // Loop to receive all the data sent by the client.
                Byte[] bytes = new Byte[size];
                string data = "";
                string IMEI;
                while ((i = networkStream.Read(bytes, 0, bytes.Length)) != 0)
                {

                    try
                    {
                        data = System.Text.Encoding.ASCII.GetString(bytes, 0, i);

                        string[] tokens = data.Split(new[] { "GPRMC" }, StringSplitOptions.None);
                        var longest = Regex.Matches(data, @"\d+").Cast<Match>().OrderByDescending(m => m.Length).First();


                        IMEI = longest.ToString();
                        if (IMEI.Length > 15)
                            IMEI = IMEI.Substring(1);



                        foreach (string item in tokens)
                        {
                            try
                            {
                                string[] Values = item.Split(',');                                   // Console.WriteLine("Received: {0}", data);
                                string time = Values[1];
                                // Console.WriteLine("Time= " + time);
                                string lat;
                                string lng;
                                string speed;
                                string date;

                                lat = Values[3];
                                lng = Values[5];
                                speed = Values[7];
                                date = Values[9];

                                string NewDString = date.Substring(2, 2) + date.Substring(0, 2) + date.Substring(4, 2);

                                //  Console.WriteLine("IMEI= " + IMEI);

                                // Alternate choice: If the string has been input by an end user, you might  
                                // want to format it according to the current culture: 
                                // IFormatProvider culture = System.Threading.Thread.CurrentThread.CurrentCulture;
                                string myDate = (NewDString + time).Insert(2, "-").Insert(5, "-").Insert(8, " ").Insert(11, ":").Insert(14, ":");


                                double latDeg = Convert.ToDouble(Convert.ToDouble(lat).ToString().Substring(0, 2));
                                double latMin = Convert.ToDouble(Convert.ToDouble(lat).ToString().Substring(2));

                                double lngDeg = Convert.ToDouble(Convert.ToDouble(lng).ToString().Substring(0, 2));
                                double lngmin = Convert.ToDouble(Convert.ToDouble(lng).ToString().Substring(2));

                                double latmap = latDeg + (latMin / 60);
                                //  OldLat=
                                double lngmap = lngDeg + (lngmin / 60);
                                //if ((Math.Round(latmap, 3) != Math.Round(OldLat, 3) && Math.Round(lngmap, 3) != Math.Round(OldLng, 3)) || idleRecord > 30)
                                //{
                                using (SqlConnection conn = new SqlConnection(ConnStr))
                                {
                                    conn.Open();
                                    using (SqlCommand cmd = conn.CreateCommand())
                                    {
                                        // DbCommand also implements IDisposable

                                        // create command with placeholders
                                        cmd.CommandText =
                                           "INSERT INTO T_Tracking " +
                                           "([IMEI], [TrackTime],  [Longitude], [Lattitude],  [speed],[MapPoint],[SafeAreaID],[GeoFenceID]) " +
                                           "VALUES(@IMEI, @TrackTime,  @Longitude, @Lattitude,  @speed,@MapPoint,@SafeAreaID,@GeoFenceID)";


                                        SqlParameter p_IMEI = new SqlParameter("@IMEI", IMEI);
                                        cmd.Parameters.Add(p_IMEI);

                                        SqlParameter p_TrackTime = new SqlParameter("@TrackTime", myDate);
                                        cmd.Parameters.Add(p_TrackTime);

                                        SqlParameter p_Longitude = new SqlParameter("@Longitude", lngmap);
                                        cmd.Parameters.Add(p_Longitude);

                                        SqlParameter p_Lattitude = new SqlParameter("@Lattitude", latmap);
                                        cmd.Parameters.Add(p_Lattitude);

                                        SqlParameter p_Speed = new SqlParameter("@speed", speed);
                                        cmd.Parameters.Add(p_Speed);
                                        SqlParameter p_Points = new SqlParameter("@MapPoint", System.Data.SqlDbType.Udt);
                                        p_Points.UdtTypeName = "geometry";
                                        p_Points.Value = GetGeographyFromText("Point(" + lngmap + "  " + latmap + ") ");
                                        cmd.Parameters.Add(p_Points);
                                        SqlParameter P_Safe = new SqlParameter("@SafeAreaID", OutsideSafeArea(latmap, lngmap));
                                        cmd.Parameters.Add(P_Safe);
                                        SqlParameter P_GeoFence = new SqlParameter("@GeoFenceID", InsideDangerArea(latmap, lngmap));
                                        cmd.Parameters.Add(P_GeoFence);
                                        // execute
                                        cmd.ExecuteNonQuery();

                                    }
                                    //}
                                    //else
                                    //    idleRecord = idleRecord + 1;

                                }
                            }
                            catch (Exception exp) { WriteLog(exp.ToString(), EventLogEntryType.Error); }
                        }


                    }
                    catch { }
                }

            }
            catch (Exception ex)
            {
                // Console.WriteLine(" >> " + ex.ToString());
            }
        }
    }
}

}

он работает нормально, но проблема в производительности, это тестовая система с 5 устройствами, она потребляет 95% памяти на сервере за 10 минут, что можно сделать для оптимизации этого кода.

Благодарю вас


person Maher Khalil    schedule 09.11.2016    source источник
comment
Одна проблема, с которой вы скоро столкнетесь, это половинные пакеты/двойные пакеты. Потому что TCP — это поток, и вы предполагаете, что читаете одно полное сообщение за вызов Receive.   -  person Jeroen van Langen    schedule 09.11.2016
comment
Моя проблема сейчас не в том, что я могу игнорировать неполные или поврежденные данные, моя проблема заключается в производительности отправки/получения   -  person Maher Khalil    schedule 09.11.2016
comment
Вот почему это комментарий, а не ответ. ;-)   -  person Jeroen van Langen    schedule 09.11.2016


Ответы (1)


Проблема здесь в том, что каждый раз, когда приходит новое соединение сокета, вы создаете новый поток, который никогда не заканчивается, даже когда базовое соединение закрыто. Процедура потока обернута обработчиком всех исключений внутри цикла while (true), поэтому, когда соединение сокета закрывается и выдается исключение SocketException, оно перехватывается и игнорируется, и процедура потока снова зацикливается. Это также предотвратит сборку мусора объектов сокета и, вероятно, также сильно нагружает ЦП. Кроме того, код очистки в конце main никогда не будет вызываться из-за наличия там while(true) и действительно закроет только последнее клиентское соединение.

Ваша процедура потока должна правильно обрабатывать исключения и останавливать зацикливание, когда базовое соединение сокета закрывается.

person Ben Jackson    schedule 09.11.2016
comment
я добавил поймать его, чтобы получить сообщение об ошибке, не могу получить доступ к удаленному объекту system.net.sockets.socket, затем я добавил networkStream.close и clientsocket.close, но все равно ЦП зависает на 92% после 20 минут, сделал то, что я добавил, правильно или есть что-то еще нужно сделать, должен ли я использовать асинхронный серверный сокет, если он работает?? Ждем ответа, спасибо msdn.microsoft.com/en-us/library/fx6588te .aspx - person Maher Khalil; 14.11.2016