Member 4099447 Ответов: 2

Использование процессора увеличивается с течением времени TCP/IP


Привет.
Я создал сервер telnet на основе этого кода из
[с/C++] Как закодировать мульти-клиентский сервер на C\++ с помощью потоков - учебники - rohitab.com - форумы[^]


#include <windows.h>
#include  <stdlib.h>
#include <stdio.h>
#include <winsock.h>
#include "mingw.thread.h"

// our thread for recving commands
DWORD WINAPI receive_cmds(LPVOID lpParam) 
{
// Here I changed for our needs ! its a warehouse management system.  
  printf("thread created\r\n");
  
  // set our socket to the socket passed in as a parameter   
  SOCKET current_client = (SOCKET)lpParam; 
  
  // buffer to hold our recived data
  char buf[100];
  // buffer to hold our sent data
  char sendData[100];
  // for error checking 
  int res;
  
  // our recv loop
  while(true) 
  {
	 res = recv(current_client,buf,sizeof(buf),0); // recv cmds
	 
	 Sleep(10);
	 
	 if(res == 0)
	 {
	  MessageBox(0,"error","error",MB_OK);
	  closesocket(current_client);
	  ExitThread(0);
	 }
	 
	 if(strstr(buf,"hello"))
	 { // greet this user
	   printf("\nrecived hello cmd");
	   
	   strcpy(sendData,"hello, greetz from KOrUPt\n");
	   Sleep(10);
	   send(current_client,sendData,sizeof(sendData),0); 
	 }								
	 else if(strstr(buf,"bye"))   
	 { // dissconnected this user
	   printf("\nrecived bye cmd\n");
	   
	   strcpy(sendData,"cya\n");
	   Sleep(10);
	   send(current_client,sendData,sizeof(sendData),0);
 
	  // close the socket associted with this client and end this thread
	   closesocket(current_client);
	   ExitThread(0);
	 }
	 else
	 {
	   strcpy(sendData,"Invalid cmd\n");
	   Sleep(10);
	   send(current_client,sendData,sizeof(sendData),0);
	 }
	 
	 // clear buffers
	   strcpy(sendData,"");
	   strcpy(buf,"");
   }
}   

int main()
{
 printf("Starting up multi-threaded TCP server by KOrUPt\r\n");
 
 // our masterSocket(socket that listens for connections)
 SOCKET sock;
 
 // for our thread
 DWORD thread; 
 
 WSADATA wsaData;
 sockaddr_in server;
 
 // start winsock
 int ret = WSAStartup(0x101,&wsaData); // use highest version of winsock avalible
 
 if(ret != 0)
 {
	return 0;
 }
  
 // fill in winsock struct ... 
 server.sin_family=AF_INET; 
 server.sin_addr.s_addr=INADDR_ANY; 
 server.sin_port=htons(123); // listen on telnet port 23
 
 // create our socket
 sock=socket(AF_INET,SOCK_STREAM,0); 
 
 if(sock == INVALID_SOCKET)
 {
	return 0;
 }
  
 // bind our socket to a port(port 123) 
 if( bind(sock,(sockaddr*)&server,sizeof(server)) !=0 )
 {
	return 0;
 }
  
 // listen for a connection  
 if(listen(sock,5) != 0)
 {
	return 0;
 }
 
 // socket that we snedzrecv data on
 SOCKET client;
 
 sockaddr_in from;
 int fromlen = sizeof(from); 
  
 // loop forever 

 while ((client = accept(sock, (struct sockaddr*)&from,&fromlen))!= INVALID_SOCKET) 
{
  // accept connections
  client = accept(sock,(struct sockaddr*)&from,&fromlen);
  printf("Client connected\r\n");
  
  // create our recv_cmds thread and parse client socket as a parameter
  // CreateThread(NULL, 0,receive_cmds,(LPVOID)client, 0, &thread);   
// Reaplaced CreateThread with
std::thread th_client(receive_cmds,(LPVOID)client);
 }
 
 // shutdown winsock
 closesocket(sock); 
 WSACleanup(); 
 
 // exit
 return 0;
}


Что я уже пробовал:

Загрузка процессора увеличивается с течением времени.
Не из-за утечки памяти.
Пожалуйста, помогите мне

Shao Voon Wong

Вы звоните принять дважды! Первый вызов находится в состоянии while, а второй - в цикле while. Уберите одну из них.

Member 4099447

Я забыл прокомментировать второе accept.in это мой код.
Количество потоков не увеличивается из-за того, что у нас есть только 7 ручных устройств, которые общаются с сервером

Stefan_Lang

Хорошо, тогда скажи это своему коду.

Member 4099447

Мне не разрешается публиковать полный код.

Stefan_Lang

Я не сказал почтовый индекс, я имел в виду, что ваш код, похоже, не знает о ваших ручных устройствах и доступном номере! Вы должны поместить такую информацию в свой код, чтобы он не создавал ненужные новые потоки.

Member 4099447

Спасибо.
Я так и сделаю

2 Ответов

Рейтинг:
1

Greg Utas

Может быть, я что-то упускаю, но вы принимаете связи. По мере увеличения числа открытых соединений их обслуживание занимает больше процессорного времени, предполагая, что сообщения регулярно поступают по ним. Действительно, серверы должны иметь политику управления перегрузкой, которая применяется, как только процессор становится полностью загруженным.


Рейтинг:
0

Stefan_Lang

Этот последний цикл в main() продолжает создавать новые потоки как можно быстрее. Новые потоки тогда получают едва ли достаточно времени, чтобы среагировать, а тем более выйти. В результате вы тонете во все увеличивающемся море потоков, и процессор тратит все свое время только на то, чтобы управлять всеми этими потоками, переключая контекст.

Вы должны проверить количество активных потоков и не продолжать создавать новые, если только количество активных потоков не упадет ниже некоторого разумного предела.


Greg Utas

Я пропустил это! Я просто увидел accept() и не стал смотреть дальше. Боже мой, любой учебник, который создает поток на TCP-сокет, ничего не знает о создании масштабируемого сервера.

Stefan_Lang

Ну, вы *действительно* сказали, что "может быть, я что-то упускаю"; - p

Безусловное создание потоков в цикле никогда не может быть хорошей вещью - для этого вам не нужно ничего знать о сокетах или серверах.

*Я* ничего об этом не знаю, но я не думаю, что должно быть неограниченное количество сокетов, и ни один из них не должен создаваться без причины (то есть какой-то клиент, использующий этот сокет). Я ожидал бы что-то вроде главного потока, который получает запросы на новые соединения, а затем создает их. Будет ли это новая нить или нет, я не знаю и не волнуюсь.

Greg Utas

Если это сервер, он создаст сокет для каждого клиента. Это нормально, пока он не войдет в перегрузку, и в этот момент он должен начать отклонять TCP-соединения. Но *один* поток может обслуживать *все* сокеты IP-порта, прослушивая запросы на подключение и опрашивая (WSAPoll в Windows) клиентские сокеты для входящих данных.

Member 4099447

Количество потоков не увеличивается из-за того, что у нас есть только 7 ручных устройств, которые общаются с сервером.
Когда я говорю "во времени", я имею в виду часы и даже дни.
Может быть, потому, что это приложение работает на виртуальном сервере s.У меня есть дополнительный поток, который каждые 10 минут проверяет процессор, если более 85 он перезапускает приложение.
Моя компания не позволит публиковать мой код.

Stefan_Lang

Код, который вы опубликовали, не останавливается на 7, он продолжает создавать новые потоки.

Member 4099447

Неправда
Приложение начинается с 2-х потоков собственного и одного msvcrt.dll
Когда я подключаю 3 клиента он растет с 3

https://docs.google.com/spreadsheets/d/e/2PACX-1vT4ovUEMwOehRZmbx5bLrKmh3wCNzP2XX_aas3MMDSTazTH4lLumxa4VlLXqEqmyw/pubhtml

И когда я отключаю один из них, он уменьшается на 1, как и ожидалось.
Но я поставлю предел на всякий случай.

Stefan_Lang

Я думаю, что неправильно истолковал ваш код: дело не в том, что он продолжает создавать слишком много потоков, но он создает слишком много сокетов, а затем никогда не закрывает их.

Сравните с примером кода, опубликованным здесь: https://docs.microsoft.com/en-us/windows/win32/api/winsock2/nf-winsock2-accept

Там вы можете видеть, что сокет действительно закрывается, даже если он недействителен. Вы не делаете этого в своем коде.

Кроме того, вы создаете каждый сокет дважды: сначала в состоянии while, а затем сразу же после этого в блоке while. Он перезаписывает первый сокет, не выпуская его должным образом.

До сих пор я не понимаю, почему это создаст заметную нагрузку на процессор, но я полагаю, что это не помешает исправить это и посмотреть, поможет ли это.

Member 4099447

Вот Бегущий код основной
часть потока receive_cmds(LPVOID lpParam) слишком длинная,..

`каждый сокет дважды: "это была моя ошибка при отправке кода.

Извините, что я побоялся опубликовать свой настоящий код .Я спросил свой n+ 1, и все в порядке


int main()
{
    printf("Starting up multi-threaded TCP server \r\n");
    FILE *fileStream_SQL; char fileText_SQL [240];
    fileStream_SQL = fopen ("PATH_sql.txt", "r");
    fgets (fileText_SQL, 240, fileStream_SQL);
    CONN_STR = fileText_SQL;
    CONN_STR_READ_ONLY =ReplaceString(CONN_STR,"Uid=sa;","Uid=alex_ro;");
    fileText_SQL[0] ='\0';
    fclose(fileStream_SQL);

 // our masterSocket(socket that listens for connections)
 SOCKET sock;

 // for our thread
 //DWORD thread;

 WSADATA wsaData;
 sockaddr_in server;

 // start winsock
 int ret = WSAStartup(0x101,&wsaData); // use highest version of winsock avalible

 if(ret != 0)
 {
	return 0;
 }

 // fill in winsock struct ...
 server.sin_family=AF_INET;
 server.sin_addr.s_addr=INADDR_ANY;
 server.sin_port=htons(23); // listen on telnet port 23

 // create our socket
 sock=socket(AF_INET,SOCK_STREAM,0);

 if(sock == INVALID_SOCKET)
 {
	return 0;
 }

 // bind our socket to a port(port 23)
 if( bind(sock,(sockaddr*)&server,sizeof(server)) !=0 )
 {
	return 0;
 }

 // listen for a connection
 if(listen(sock,5) != 0)
 {
	return 0;
 }

 printf("TCP server sin_port 23 \r\n");

 // socket that we snedzrecv data on
 SOCKET client;

 sockaddr_in from;
 int fromlen = sizeof(from);


 std::thread th_overCPU(overCPU);
th_overCPU.detach();

 // loop forever
  while ((client = accept(sock, (struct sockaddr*)&from,&fromlen))!= INVALID_SOCKET)
    {
    Sleep(250);
    std::thread th_client(receive_cmds,(LPVOID)client);
    th_client.detach();

 }
 // shutdown winsock
 closesocket(sock);
 WSACleanup();

 // exit
 return 0;
}