Форум программистов, компьютерный форум, киберфорум
Наши страницы
C# .NET
Войти
Регистрация
Восстановить пароль
 
Рейтинг 4.50/4: Рейтинг темы: голосов - 4, средняя оценка - 4.50
Андрей8686
0 / 0 / 0
Регистрация: 05.08.2013
Сообщений: 5
1

Слабые места кода

05.08.2013, 11:41. Просмотров 684. Ответов 9
Метки нет (Все метки)

День добрый, уважаемые форумчане!
У меня есть код сервера, который я писал для одноплатного компьютера под CF3.5
Нужно обеспечить 100% надежность работы сервера, т.к. одноплатник потом может быть увезен черти куда и спрятан на каком-нибудь складе...
Я не прошу исправлять код, прошу только помощи в поиске его слабых мест, на которые стоит обратить внимание. Буду рад любой критике!

Особенности сервера:
Работает на ассинхронных сокетах. Максимум 5 клиентов одновременно. Подсоединяемые клиенты складываются в ArrayList и проверяются в другом потоке на длительное молчание раз в 10 секунд. Если молчание больше 30 сек - клиент удаляется.

Большое всем спасибо за помощь!


C#
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
namespace Server
{
    public partial class AsyncServer
    {
        //ServerReceivedMessage
        public class ServerReceivedMessageEventArgs
        {
            public ServerReceivedMessageEventArgs(Socket _socket, string _message)
            {
                socket = _socket;
                message = _message;
            }
            public Socket socket { get; private set; } // readonly
            public string message { get; private set; } // readonly
        }
        public delegate void ServerReceivedMessageDelegate(ServerReceivedMessageEventArgs ar);
        public event ServerReceivedMessageDelegate ServerReceivedMessage;
 
        #region Variables
        /// <summary>
        /// порт
        /// </summary>
        private int _Port;
        /// <summary>
        /// сокет
        /// </summary>
        private Socket _OwnSocket;
        /// <summary>
        /// клиенты
        /// </summary>
        private ArrayList _StateObjects;
        /// <summary>
        /// флаг работы сервера
        /// </summary>
        public volatile bool IsServerRunning = false;
        /// <summary>
        /// Поток проверки сокетов
        /// </summary>
        private Thread _CheckSocketsThread;
        /// <summary>
        /// флаг работы потока
        /// </summary>
        private bool _IsCheckingSockets = false;
        /// <summary>
        /// Свойство кол-во подключенный клиентов
        /// </summary>
        public int NumOfClients
        {
            get { return _StateObjects.Count; }
            private set { }
        }
        #endregion
 
        /// <summary>
        /// коснтанты
        /// </summary>
        private const int MAX_CONNECTIONS = 5;
        private const int TO_LONG_SILENCE_TIMEOUT = 30000; //milisec. Максимальное время ожидания запроса от клиента
        private const int CHECKING_SOCKETS_PERIOD = 10000; //milisec. Период проверки соединений с клиентами
        private const string PING_MSG = "PING\r\n";
        private const string HELLO_MSG = "HELLO!\r\n";
        private const string TO_LONG_SILENCE_MSG = "THERE WAS NO REQUEST FROM YOU FOR A LONG TIME! BYE!\r\n";
        private const string TO_MANY_CONNECTIONS_MSG = "SORRY! THERE ARE TO MANY CONNECTIONS TO THE SERVER! BYE!\r\n";
 
        /// <summary>
        /// конструктор класса
        /// </summary>
        /// <param name="port"></param>
        public AsyncServer(int port)
        {
            _Port = port;
            //коллекция клиентов
            _StateObjects = new ArrayList();
            //поток проверки клиентов
            _CheckSocketsThread = new Thread(new ThreadStart(CheckSockets));
        }
        /// <summary>
        /// метод для запуска сервера
        /// </summary>
        /// <returns></returns>
        public ServerError Run()
        {
            try
            {
                _OwnSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
                IPHostEntry e = Dns.GetHostEntry(IPAddress.Any.ToString());
                IPAddress hostIP = e.AddressList[0]; //всегда будет только 1 сетевой интерфейс!
                IPEndPoint ep = new IPEndPoint(hostIP, _Port);
                _OwnSocket.Bind(ep);
                _OwnSocket.Listen(MAX_CONNECTIONS);
                _OwnSocket.BeginAccept(new AsyncCallback(AcceptConnection), _OwnSocket);
                _CheckSocketsThread.Start();  
            }
            catch (Exception) { return false;}
            return true;
        }
        /// <summary>
        /// метод для остановки сервера
        /// </summary>
        public void Close()
        {
            //прерываем поток проверки сокетов
            _IsCheckingSockets = false;
            _CheckSocketsThread.IsBackground = true;
            _CheckSocketsThread.Abort();
 
           //закрываем сокет сервера
            _OwnSocket.Close();
 
            //завершаем все соединения
            try{foreach (StateObject stateobj in _StateObjects) CloseSocket(stateobj.WorkSocket);}
            catch (InvalidOperationException) { }
            _StateObjects.Clear();             
        }
 
        /// <summary>
        /// Колбек для ассинхронного подключения клиентов
        /// </summary>
        /// <param name="ar"></param>
        private void AcceptConnection(IAsyncResult ar)
        {
            Socket ServerSocket = (Socket)ar.AsyncState;
            Socket handler = null;
 
            try 
            { 
                handler = ServerSocket.EndAccept(ar); 
            }
            //клиента уже нет
            catch (ObjectDisposedException)  { return; }
            finally
            {
                //продолжаем принимать подключения
                if (ServerSocket != null && ServerSocket.Connected) ServerSocket.BeginAccept(new AsyncCallback(AcceptConnection), ServerSocket);
            }
 
            //переполнение сервера
            if (_StateObjects.Count >= MAX_CONNECTIONS)
            {
                SendString(handler, TO_MANY_CONNECTIONS_MSG);
                CloseSocket(handler);
            }
            else
            {
                //новое подключение
                StateObject stateobj = new StateObject();
                stateobj.WorkSocket = handler;
                //ассинхронный прием данных
                handler.BeginReceive(stateobj.buffer, 0, StateObject.BufferSize, 0, new AsyncCallback(ReceiveData), stateobj);
                //добавляем в коллекцию
                _StateObjects.Add(stateobj);
            }
            
        }
        /// <summary>
        /// Колбек для ассинхронного приема данных
        /// </summary>
        /// <param name="ar"></param>
        private void ReceiveData(IAsyncResult ar)
        {
            int bytestoread = 0;
            bool error = false;
 
            StateObject stateobject = (StateObject)ar.AsyncState;
            Socket handler = stateobject.WorkSocket;
 
            try
            {
                //завершаем прием данных
                bytestoread = handler.EndReceive(ar);
            }
            catch (Exception) { error = true; }
 
            //получены данные
            if (bytestoread > 0 && !error)
            {
                //последняя активность
                stateobject.SetActivity();
                //данные из сокета
                stateobject.sb.Append(Encoding.ASCII.GetString(stateobject.buffer, 0, bytestoread));
                string content = stateobject.sb.ToString();
                if (content.IndexOf("\r\n") > -1)
                {
                    //сообщаем о приходе запроса
                    if (ServerReceivedMessage != null) ServerReceivedMessage(new ServerReceivedMessageEventArgs(stateobject.WorkSocket, content));
                    stateobject.sb.Remove(0, stateobject.sb.Length);
                }
                //продолжаем принимать данные из этого сокета
                handler.BeginReceive(stateobject.buffer, 0, StateObject.BufferSize, 0, new AsyncCallback(ReceiveData), stateobject);
            }
            else
            {
                //закрываем соединение
                CloseSocket(handler);
                if(_StateObjects.Contains(stateobject)) _StateObjects.Remove(stateobject);
            }
        }       
        /// <summary>
        /// ассинхронная отправка строки данных клиенту
        /// </summary>
        /// <param name="handler"></param>
        /// <param name="data"></param>
        public bool SendString(Socket handler, String data)
        {
            bool success=false;
            if (handler != null && data != string.Empty)
            {
                byte[] byteData = Encoding.ASCII.GetBytes(data);
                try
                {
                    handler.BeginSend(byteData, 0, byteData.Length, 0, new AsyncCallback(SendCallback), handler);
                    success = true;
                }
                catch (ObjectDisposedException) {}
            }
            return success;
        }
        /// <summary>
        /// ассинхронная отправка массива данных клиенту
        /// </summary>
        /// <param name="handler"></param>
        /// <param name="data"></param>
        public bool SendBytes(Socket handler, byte[] data)
        {
            bool success = false;
            if (handler != null && data != null)
            {
                try
                {
                    handler.BeginSend(data, 0, data.Length, 0, new AsyncCallback(SendCallback), handler);
                    success = true;
                }
                catch (ObjectDisposedException) { } 
            }
            return success;
        }
        /// <summary>
        /// Колбек для ассинхронной отправки данных клиенту
        /// </summary>
        /// <param name="ar"></param>
        private static void SendCallback(IAsyncResult ar)
        {
            Socket handler = (Socket)ar.AsyncState;
            try
            {
                int bytesSent = handler.EndSend(ar);
            }
            catch (Exception) { }   
        }
 
        /// <summary>
        /// проверяет состояние сокетов
        /// </summary> 
        private void CheckSockets()
        {
            _IsCheckingSockets = true;
            while (_IsCheckingSockets)
            {
                //проверка сокетов
                for (int i = 0; i < _StateObjects.Count; i++)
                {
                    StateObject stateobject = (StateObject)_StateObjects[i];
                    Socket socket = stateobject.WorkSocket;
                    //посылаем PING каждые CHECKING_SOCKETS_PERIOD милисек молчания - это сделано просто для совместимости с прошлой версией сервера
                    if (stateobject.SilenceTime > CHECKING_SOCKETS_PERIOD) SendString(socket, PING_MSG);
                    //отключаем клиента через TO_LONG_SILENCE_TIMEOUT милисек молчания
                    if (stateobject.SilenceTime > TO_LONG_SILENCE_TIMEOUT)
                    {
                        SendString(socket, TO_LONG_SILENCE_MSG);
                        CloseSocket(socket);
                        _StateObjects.Remove(stateobject);
                    }
                }
                //спим
                Thread.Sleep(CHECKING_SOCKETS_PERIOD);
            }
            
        }
        /// <summary>
        /// Завершает соединение с клиентом
        /// </summary>
        /// 
        public void DisconnectClient(Socket socket)
        {
            CloseSocket(socket);
            StateObject stateobject = GetStateObject(socket);
            if (stateobject != null) _StateObjects.Remove(stateobject);          
        }       
        /// <summary>
        /// метод поиска stateobject по связанному с ним сокету
        /// </summary>
        /// <param name="socket"></param>
        /// <returns></returns>
        /// 
        private StateObject GetStateObject(Socket socket)
        {
            if (_StateObjects == null || socket == null) return null;
            foreach (StateObject stateobj in _StateObjects)
            {
                if (stateobj.WorkSocket.Equals(socket)) return stateobj;
            }
            return null;
        }
        /// <summary>
        /// закрывает сокет
        /// </summary>
        /// <param name="socket"></param>
        private static void CloseSocket(Socket socket)
        {
            if (socket != null)
            {
                try
                {
                    socket.Shutdown(SocketShutdown.Both);
                    socket.Close();
                }
                catch (Exception) { }
            }
        }
 
 
        // State object for reading client data asynchronously
        private class StateObject
        {
            // Client  socket.
            public Socket WorkSocket = null;
            // Size of receive buffer.
            public const int BufferSize = 1024;
            // Receive buffer.
            public byte[] buffer = new byte[BufferSize];
            // Received data string
            public StringBuilder sb = new StringBuilder();         
            //SilenceTime
            public double SilenceTime
            {
                get
                {
                    return ((TimeSpan)(DateTime.Now - LastActivity)).TotalSeconds*1000;
                }
            }
            //LastActivityTime
            private DateTime LastActivity = DateTime.Now;
            //устанавливает текущее время последней активности
            public void SetActivity()
            {
                LastActivity = DateTime.Now;
            }
        }
    }
}
0
Надоела реклама? Зарегистрируйтесь и она исчезнет полностью.
Similar
Эксперт
41792 / 34177 / 6122
Регистрация: 12.04.2006
Сообщений: 57,940
05.08.2013, 11:41
Ответы с готовыми решениями:

Компиляция/добавление кода без видимости самого кода(сложно объяснить)
Есть код по типу такого: static void Main() { Hm.Ex checker = new...

Реализовать 2 статических анализатора исходного кода, которые будут проверять правила оформления кода
Доброго времени суток. Уже читал подобную пост здесь &quot;Парсер С++&quot;, но немного...

Формирование низкоуровневого кода (компиляция кода на С из текстового файла)
Доброго времени суток, форумчане. Необходима информация о трансляции Си (без...

Как вытащить часть кода из кода страницы?
Знаю, что можно через json как-то. Вот например есть код страницы, на нем есть...

Менеджер свободного места
Суть такая: есть файл в котором пишутся, изменяются и удаляются данные. Как...

9
Anklav
442 / 301 / 46
Регистрация: 23.01.2013
Сообщений: 640
Завершенные тесты: 2
05.08.2013, 12:46 2
А что если подключатся 5 клиентов, и будут беспрерывно посылать мусор, без идентификатора /r/n?
1
m0nax
1203 / 908 / 109
Регистрация: 12.01.2010
Сообщений: 1,891
Завершенные тесты: 3
05.08.2013, 17:30 3
а если пришлют не ASCII строки?
тем более вижу метод
C#
1
SendBytes(Socket handler, byte[] data)
т.е отправить могут любой массив байт, значит там вообще не строка может быть
1
Андрей8686
0 / 0 / 0
Регистрация: 05.08.2013
Сообщений: 5
05.08.2013, 20:41  [ТС] 4
Цитата Сообщение от Anklav Посмотреть сообщение
А что если подключатся 5 клиентов, и будут беспрерывно посылать мусор, без идентификатора /r/n?
Цитата Сообщение от m0nax Посмотреть сообщение
а если пришлют не ASCII строки?
тем более вижу метод
C#
1
SendBytes(Socket handler, byte[] data)
т.е отправить могут любой массив байт, значит там вообще не строка может быть
Да, действительно. Здесь можно было бы потерять прилично памяти. На одноплатнике оперативки мало, так что вопрос утечек памяти стоит очень остро. Исправил код - теперь если больше 30 байт пришло, то все удаляется. Клиент может слать запросы только такого вида: "GET_VALUE_XXX\r\n". Спасибо за наводку!

Помимо утечек памяти меня так же очень интересуют нестандартные ситуации типа: клиент подключился и моментально отключился и тд. и тп, что может вызвать SocketException и остановку программы...
0
Андрей8686
0 / 0 / 0
Регистрация: 05.08.2013
Сообщений: 5
22.08.2013, 09:56  [ТС] 5
Помогите разобраться, пожалуйста...
Заметил, что если к этому серверу в цикле подключаются 5 клиентов, отправляют один запрос, получают ответ и отключаются, то память съедается очень быстро. В итоге устрйоство зависает от нехватки RAM. Но если принятие подключений делать в отдельном потоке:
C#
1
2
3
4
5
6
7
8
9
10
 while (_IsCheckingSockets)
            {
                // сбросить мьютекс
                _connectionMutex.Reset();
 
                _OwnSocket.BeginAccept(new AsyncCallback(AcceptCallback), _OwnSocket);
 
                // ожидание следующего подключения
                _connectionMutex.WaitOne();
            }
то с памятью все нормально.
В чем дело?
0
MegaSinner
98 / 94 / 9
Регистрация: 09.04.2010
Сообщений: 746
22.08.2013, 16:32 6
Цитата Сообщение от Андрей8686 Посмотреть сообщение
если принятие подключений делать в отдельном потоке
без разницы, оперативку оно будет хавать одинаково..
0
m0nax
1203 / 908 / 109
Регистрация: 12.01.2010
Сообщений: 1,891
Завершенные тесты: 3
22.08.2013, 16:59 7
Андрей8686, о каких объемах речь? килобайт, гигабайт..
0
Андрей8686
0 / 0 / 0
Регистрация: 05.08.2013
Сообщений: 5
23.08.2013, 10:27  [ТС] 8
MegaSinner, До этого _OwnSocket.BeginAccept(new AsyncCallback(AcceptConnection), _OwnSocket) вызывался в его колбэке AcceptConnection. И почему то это отъедало всю память устройства. Сейчас сделал _OwnSocket.BeginAccept в цикле с мьютексом - с памятью все ок

m0nax, в устройстве всего-то 32 мб RAM, вот она полностью и заканчивалась..

Добавлено через 1 час 58 минут
Прошу прощения, похоже, ввел вас в заблуждение...

Видимо, память отъедалась вот в этом месте

C#
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
 
 
private void AcceptCallback(IAsyncResult ar)
        {
            //разрешем принять новое подключение
            _acceptMutex.Set();
 
            Socket ServerSocket = (Socket)ar.AsyncState;
            Socket handler = null;
            try 
            { 
                //заканчиваем прием подключения
                handler = ServerSocket.EndAccept(ar); 
            }
            //сокет закрыт
            catch (ObjectDisposedException) { return; }
 
 
            //переполнение сервера
            if (_ClientStateObjects.Count >= MAX_CONNECTIONS) 
            {
                //закрываем сокет и очищаем ресурсы
                CloseSocket(handler);   //<======================вот здесь
            }
            else
            {
Я так полагаю, что сокеты закрываются немгновенно, поэтому память и расходуется сильно (подключения к серверу происходят очень часто)

Единственным решением вижу не принимать новые подключения, пока кол-во уже созданных подключений равно 5
0
m0nax
1203 / 908 / 109
Регистрация: 12.01.2010
Сообщений: 1,891
Завершенные тесты: 3
23.08.2013, 16:59 9
32 мб это серьезно для net, лучше вообще завязывать с любыми необязательными фишками
асинхронность я бы выпилил, для 5 соединений оно не нужно, а лишние потоки память кушают

Добавлено через 12 минут
и кстати на счет надежности - при доступе к коллекции сокетов(add remove) из разных потоков нужно делать синхронизацию, lock например
0
Андрей8686
0 / 0 / 0
Регистрация: 05.08.2013
Сообщений: 5
26.08.2013, 09:20  [ТС] 10
Цитата Сообщение от m0nax Посмотреть сообщение
асинхронность я бы выпилил, для 5 соединений оно не нужно, а лишние потоки память кушают
А если делать на синхронных сокетах, ведь все-равно минимум 2 потока будет: поток для accept'ов и поток для отправки данных клиентам и проверки их на наличие коннекта и тд. В пятницу сделал на ассинхронных с отключение accept'ов при заполнении сервера 5-ю клиентами. Оставил на выхи - вроде память теперь ведет себя предсказуемо.

Цитата Сообщение от m0nax Посмотреть сообщение
и кстати на счет надежности - при доступе к коллекции сокетов(add remove) из разных потоков нужно делать синхронизацию, lock например
Да, с этим тоже разобрался, спасибо! Сперва стояли локи, теперь сделал remove клиентов только в одном потоке, остальные потоки редактируют поле NeedClose у каждого клиента.

Понаблюдаю, как будет себя вести...
0
26.08.2013, 09:20
MoreAnswers
Эксперт
37091 / 29110 / 5898
Регистрация: 17.06.2006
Сообщений: 43,301
26.08.2013, 09:20

Запуск неуправляемого кода внутри кода c#
часто требуется при выполнении программы запускать исполняемые файлы (exe) с...

Трабла с определением места нахождения программы
Для нахождения директории программы пользуюсь Environment.CurrentDirectory, но...

Проверка пин кода и в соответствии с ним заполнить поля после ввода пин-кода на другой форме
Здраствуйте!Люди проблема в следующем, есть задача где мне надо сделать...


Искать еще темы с ответами

Или воспользуйтесь поиском по форуму:
10
Ответ Создать тему
Опции темы

КиберФорум - форум программистов, компьютерный форум, программирование
Powered by vBulletin® Version 3.8.9
Copyright ©2000 - 2018, vBulletin Solutions, Inc.
Рейтинг@Mail.ru