Имеется устройство с этой ОС... сделана система автоматики по ModbusRTU, где ведущим это устройство. Всё нормально... но это устройство ещё и в сети. Некая программа на другом ПК подключается к этому серверу для чтения данных и проверки работоспособности как по написанному коду так и по NetCat для проверки работоспособности сервера. Кроме того другая программа-скрипт по telnet обращается к этому устройству для реализации его перезапуска. Ну и т.п. И вот проблема: Код (Text): \> netstat -nlta TCP TABLE Loc Addr Loc Port Rem Addr Rem Port State 0.0.0.0 21 0.0.0.0 139 LISTEN 0.0.0.0 23 0.0.0.0 57449 LISTEN 0.0.0.0 80 0.0.0.0 20725 LISTEN 0.0.0.0 135 0.0.0.0 4326 LISTEN 0.0.0.0 139 0.0.0.0 24784 LISTEN 0.0.0.0 443 0.0.0.0 24612 LISTEN 0.0.0.0 987 0.0.0.0 33016 LISTEN 0.0.0.0 2394 0.0.0.0 24806 LISTEN 0.0.0.0 2395 0.0.0.0 28683 LISTEN 0.0.0.0 5120 0.0.0.0 8263 LISTEN 0.0.0.0 9891 0.0.0.0 24641 LISTEN 127.0.0.1 3001 0.0.0.0 37081 LISTEN 192.168.1.210 23 192.168.1.110 51446 ESTAB 192.168.1.210 23 192.168.1.110 51448 ESTAB 192.168.1.210 23 192.168.1.110 51706 ESTAB 192.168.1.210 445 0.0.0.0 8408 LISTEN 192.168.1.210 3008 192.168.1.100 102 ESTAB 192.168.1.210 9891 192.168.1.110 40446 CLOSE_WAIT 192.168.1.210 9891 192.168.1.110 40450 CLOSE_WAIT 192.168.1.210 9891 192.168.1.110 40454 CLOSE_WAIT 192.168.1.210 9891 192.168.1.110 40458 CLOSE_WAIT 192.168.1.210 9891 192.168.1.110 40462 CLOSE_WAIT UDP TABLE Loc Addr Loc Port 0.0.0.0 137 0.0.0.0 137 0.0.0.0 138 0.0.0.0 161 192.168.1.210 5048 \> Это консоль устройства, а CLOSE_WAIT могут висеть и неделю... и может и дольше. Это после подключения и отключения клиента, а так же после обращений по NetCat по порту 9891. Сокет висит пожизненно. Вот поток обслуживания клиента (в VisualSudio 2005 для Windows CE): Код (C++): void __stdcall ServerProcess(SERV *dat) { SERV s_dat; int res; int cntlive = _time_live; U8 InBuf[_srsizebuf]; U8 OutBuf[_srsizebuf]; U8 flagSend; memcpy((void*)(&s_dat),(void*)(dat),sizeof(SERV)); memset((U8*)(&OutBuf[0]),0,_srsizebuf); //setsockopt(s_dat.Socket[s_dat.CurrentNumClient],0,11,11,11); while(1) { //===== ожидание мьютекса ====== while(WaitForSingleObject( hMutex, _TimeMutex) == WAIT_TIMEOUT) { Sleep(_SSLEEP); }; //printf("==server== %i\r\n", s_dat.socket); memset((U8*)(&InBuf[0]),0,_srsizebuf); memset((U8*)(&OutBuf[0]),0,_srsizebuf); res = ReadSocket(s_dat.Socket[s_dat.CurrentNumClient], &InBuf[0]); flagSend = 0; switch(res) { case -2: goto err; //выход при ошибке чтения default: //анализ типа пакета switch((U8)(InBuf[_ad_pkt])) { case _PKT_NULL: (*(U16*)(&OutBuf[_ad_size])) = _size_null; OutBuf[_ad_pkt] = _PKT_NULL; flagSend = 0xFF; break; case _PKT_REAL: //пакет реальных данных (*(U16*)(&OutBuf[_ad_size])) = _size_null + sizeof(SDAT);//(RdReal(&OutBuf[3])); memcpy(&OutBuf[3], &sdat, sizeof(SDAT)); OutBuf[_ad_pkt] = _PKT_REAL; flagSend = 0xFF; break; default: //неизвесный пекет break; } } // if(flagSend > 0) { res = WriteSocket(s_dat.Socket[s_dat.CurrentNumClient],&OutBuf[0],(*(U16*)(&OutBuf[_ad_size]))); switch(res) { case -2: goto err; //выход при ошибке записи case -1: break; default: cntlive = _time_live; break; } } if(cntlive < 0) goto err; //время жизни вышло //====== Освобождаем мьютекс ====== ReleaseMutex(hMutex); // Sleep(_serv_pr_ssleep); cntlive -= _serv_pr_ssleep; } err: printf("==stop server=\r\n"); shutdown(s_dat.Socket[s_dat.CurrentNumClient], 2); while( recv(s_dat.Socket[s_dat.CurrentNumClient], &InBuf[0], _srsizebuf, 0)!= -1); closesocket(s_dat.Socket[s_dat.CurrentNumClient]); dat->Socket[s_dat.CurrentNumClient] = 0; dat->CntClient--; //====== Освобождаем мьютекс ====== ReleaseMutex(hMutex); // } Не ругайте за код... он изменяется и сейчас... сию минуту. Уже недели 2 с этим ковыряюсь... С Linux всё проще и работает. Никого не хочу обидеть, но от Microsoft ничего хорошего уже давно не ожидаю. А "заплатки" невозможно прикрутить и нет их... система в прошивке устройства. С глубоким уважением!
...что-то полазил по поисковикам... и там тысячи таких вопросов на эту тему. Конечно там обычный windows, который можно обновлять. И пока не знаю как быть... делать на сторонней машине с Linux жесточайший контроль за Windows(она сейчас занимается сбором данных, и на неё навешано много ещё других задач) ... скриптами по доступа к устройству или можно по нормальному с закрытием сокета. Если по нормальному, то переделать только одну программу с набором dll, а если контроль то надо переделать много уже готового. Было бы проще это предоставить другой машине, если бы эта была бы локальная сеть предприятия, а не другая сеть к оборудованию, в которой нет иных машин. Может по нормальному Microsoft и решил уже эту проблему через несколько лет... но вот я пока только в настоящем времени и немного помню прошлое...
Это как? Вот стартует сервер: Код (C++): /******************************* СТАРТ СЕРВЕРА ********************************/ int StartServer(void) { int timeout; //===== ожидание мьютекса ====== while(WaitForSingleObject( hMutex, _TimeMutex) == WAIT_TIMEOUT) { Sleep(_SSLEEP); } //============================== //======== Initialize Winsock ======= WSAStartup(MAKEWORD(2,2), &wsaData); //======== Create a SOCKET for connecting to server ========= ListenSocket = socket(AF_INET,SOCK_STREAM, IPPROTO_TCP); //======== Setup the TCP listening socket =================== // int maxbuff = SO_MAX_MSG_SIZE; // setsockopt(SL1_ListenSocket,SOL_SOCKET,SO_RCVBUF, (char*) &maxbuff, sizeof(maxbuff)); // setsockopt(SL1_ListenSocket,SOL_SOCKET,SO_SNDBUF, (char*) &maxbuff, sizeof(maxbuff)); //=========================================================== timeout = _SOCKET_TIMEOUT; setsockopt(ListenSocket,SOL_SOCKET,SO_SNDTIMEO,(char*) &timeout,sizeof(timeout)); setsockopt(ListenSocket,SOL_SOCKET,SO_RCVTIMEO,(char*) &timeout, sizeof(timeout)); //=========================================== ServerAddr.sin_family = AF_INET; ServerAddr.sin_addr.s_addr = INADDR_ANY; ServerAddr.sin_port = htons(LPort); memset(&(ServerAddr.sin_zero), '\0', 8); //select(ListenSocket,NULL,NULL,NULL,&tv); SL1_err = bind(ListenSocket,(struct sockaddr *) &ServerAddr, sizeof(ServerAddr)); if(SL1_err == INVALID_SOCKET) { closesocket(ListenSocket); WSACleanup(); //====== Освобождаем мьютекс ====== ReleaseMutex(hMutex); // //================================= return -1; } SL1_err = listen(ListenSocket,_MaxSock); if (SL1_err == SOCKET_ERROR) { closesocket(ListenSocket); WSACleanup(); //====== Освобождаем мьютекс ====== ReleaseMutex(hMutex); // //================================= return -1; } /************************************************************ Запуск потоков сервера *************************************************************/ //===== запуск потока прослушивания ====== if(serv.ListenWork == FALSE) { serv.ListenWork = TRUE; ListenHnd = CreateThread(NULL,NULL,(LPTHREAD_START_ROUTINE)ThreadListenProcess, NULL,NULL,&ListenID); SetThreadPriority(ListenHnd,THREAD_PRIORITY_NORMAL); //====== Освобождаем мьютекс ====== ReleaseMutex(hMutex); // //================================= Sleep(_SSLEEP); //===== ожидание мьютекса ====== while(WaitForSingleObject( hMutex, _TimeMutex) == WAIT_TIMEOUT) { Sleep(_SSLEEP); } //============================== } //====== Освобождаем мьютекс ====== ReleaseMutex(hMutex); // //================================= return 0; } тут и создаётся поток прослушивания... вот этот: Код (C++): /**************************************** ПОТОК ПРОСЛУШИВАНИЯ СОКЕТА *****************************************/ void __stdcall ThreadListenProcess(void)//(void * arg) { //HWND hwnd; struct sockaddr_in addr; int addr_len = sizeof(addr); DWORD a = 0; WORD count; BOOL res1; fd_set fds; //TimeTick = 0; //timeval tv; //===== ожидание мьютекса ====== while(WaitForSingleObject( hMutex, _TimeMutex) == WAIT_TIMEOUT) { Sleep(_SSLEEP); }; //============================== do { //===========реализация выхода по таймауту ============= tv.tv_sec = 0; tv.tv_usec = _SOCKET_TIMEOUT; FD_ZERO(&fds); FD_SET(ListenSocket,&fds); //SL1_err = select(SL1_ListenSocket,&fds,NULL,NULL,&tv); //SL1_err = WSAAsyncSelect(SL1_ListenSocket,hwnd,WM_SOCKET,FD_ACCEPT | FD_READ); SL1_err = select(ListenSocket,&fds,&fds,&fds,&tv); //SL1_err = select(SL1_ListenSocket,NULL,NULL,NULL,&tv); if(SL1_err == 0) { //====== Освобождаем мьютекс ====== ReleaseMutex(hMutex); // //================================= Sleep(_SSLEEP); //===== ожидание мьютекса ====== while(WaitForSingleObject( hMutex, _TimeMutex) == WAIT_TIMEOUT) { Sleep(_SSLEEP); }; //============================== continue; } //===================================================== ClientSocket = accept(ListenSocket,(struct sockaddr*) &addr, &addr_len); //a = addr.sin_addr.S_un.S_addr; //=========== поиск записей по адресу в списке ========= count = 0; res1 = FALSE; while(count < _MaxSock) // { if(serv.Socket[count] == 0) { serv.CurrentNumClient = count; serv.Socket[count] = ClientSocket; serv.cHnd[count] = CreateThread(NULL,NULL,(LPTHREAD_START_ROUTINE)ServerProcess, &serv, NULL,&serv.cID[count]); serv.CntClient++; break; }; count++; } //if(!(res1)) //{ // closesocket(ClientSocket); //} //====== Освобождаем мьютекс ====== ReleaseMutex(hMutex); // //================================= Sleep(_SSLEEP); //===== ожидание мьютекса ====== while(WaitForSingleObject( hMutex, _TimeMutex) == WAIT_TIMEOUT) { Sleep(_SSLEEP); }; //============================== }while(serv.ListenWork); closesocket(ListenSocket); WSACleanup(); //====== Освобождаем мьютекс ====== ReleaseMutex(hMutex); // //================================= } Он принимает входящие соединения и формирует поток для этого соединения - обслуживание клиента. Вот: Код (C++): void __stdcall ServerProcess(SERV *dat) { SERV s_dat; DWORD optval; int res; int cntlive = _time_live; U8 InBuf[_srsizebuf]; U8 OutBuf[_srsizebuf]; U8 flagSend; memcpy((void*)(&s_dat),(void*)(dat),sizeof(SERV)); while(1) { //===== ожидание мьютекса ====== while(WaitForSingleObject( hMutex, _TimeMutex) == WAIT_TIMEOUT) { Sleep(_SSLEEP); }; //printf("==server== %i\r\n", s_dat.socket); memset((U8*)(&InBuf[0]),0,_srsizebuf); memset((U8*)(&OutBuf[0]),0,_srsizebuf); res = ReadSocket(s_dat.Socket[s_dat.CurrentNumClient], &InBuf[0]); flagSend = 0; switch(res) { case -2: goto err; //выход при ошибке чтения default: //анализ типа пакета switch((U8)(InBuf[_ad_pkt])) { case _PKT_NULL: (*(U16*)(&OutBuf[_ad_size])) = _size_null; OutBuf[_ad_pkt] = _PKT_NULL; flagSend = 0xFF; break; case _PKT_REAL: //пакет реальных данных (*(U16*)(&OutBuf[_ad_size])) = _size_null + sizeof(SDAT);//(RdReal(&OutBuf[3])); memcpy(&OutBuf[3], &sdat, sizeof(SDAT)); OutBuf[_ad_pkt] = _PKT_REAL; flagSend = 0xFF; break; default: //неизвесный пекет break; } } // if(flagSend > 0) { res = WriteSocket(s_dat.Socket[s_dat.CurrentNumClient],&OutBuf[0],(*(U16*)(&OutBuf[_ad_size]))); switch(res) { case -2: goto err; //выход при ошибке записи case -1: break; default: cntlive = _time_live; break; } } if(cntlive < 0) goto err; //время жизни вышло //====== Освобождаем мьютекс ====== ReleaseMutex(hMutex); // Sleep(_serv_pr_ssleep); cntlive -= _serv_pr_ssleep; } err: printf("==stop server=\r\n"); optval = 1; setsockopt(s_dat.Socket[s_dat.CurrentNumClient], SOL_SOCKET, SO_LINGER, &optval, sizeof(optval)); setsockopt(s_dat.Socket[s_dat.CurrentNumClient], SOL_SOCKET, SO_LINGER, &optval, sizeof(optval)); shutdown(s_dat.Socket[s_dat.CurrentNumClient], 2); closesocket(s_dat.Socket[s_dat.CurrentNumClient]); /*shutdown(s_dat.Socket[s_dat.CurrentNumClient], 2); while( recv(s_dat.Socket[s_dat.CurrentNumClient], &InBuf[0], _srsizebuf, 0)!= -1); closesocket(s_dat.Socket[s_dat.CurrentNumClient]);*/ dat->Socket[s_dat.CurrentNumClient] = 0; dat->CntClient--; //====== Освобождаем мьютекс ====== ReleaseMutex(hMutex); // } Он работает с клиентом... принимает запросы и отдаёт ответ... структуры данных, с которыми работает основной поток... ну допустим main. А при ошибке связи, разрыве соединения или клиент забыл про обмен... ну завис и т.п. этот поток должен закрыть соединение и умереть. Одновременно может быть создано _MaxSock = 20 потоков т.е. в одно время может быть подключено к серверу 20 клиентов. В их число входят программы сбора данных, скрипт контроля наличия порта(то что сервер в работе) и т.п. Кроме того программа (часть этого сервера тут не показана) является и клиентом, который подключается к контроллеру AC800M для контроля за техпроцессом и ещё по 2-м Modbus RTU контролировать работу насосов охлаждения и управлять ими.
Да. Вот часть окончания потока: Код (C++): void __stdcall ServerProcess(SERV *dat) { SERV s_dat; DWORD optval; int res; int cntlive = _time_live; U8 InBuf[_srsizebuf]; U8 OutBuf[_srsizebuf]; U8 flagSend; memcpy((void*)(&s_dat),(void*)(dat),sizeof(SERV)); while(1) { ...... ...... ...... } err: printf("==stop server=\r\n"); optval = 1; setsockopt(s_dat.Socket[s_dat.CurrentNumClient], SOL_SOCKET, SO_LINGER, &optval, sizeof(optval)); setsockopt(s_dat.Socket[s_dat.CurrentNumClient], SOL_SOCKET, SO_LINGER, &optval, sizeof(optval)); shutdown(s_dat.Socket[s_dat.CurrentNumClient], 2); closesocket(s_dat.Socket[s_dat.CurrentNumClient]); /*shutdown(s_dat.Socket[s_dat.CurrentNumClient], 2); while( recv(s_dat.Socket[s_dat.CurrentNumClient], &InBuf[0], _srsizebuf, 0)!= -1); closesocket(s_dat.Socket[s_dat.CurrentNumClient]);*/ dat->Socket[s_dat.CurrentNumClient] = 0; dat->CntClient--; //====== Освобождаем мьютекс ====== ReleaseMutex(hMutex); // } Как видно мы прыгаем на метку err, потому как мютекс отпускаем в конце. Да и данные для контроля состояния только локальные. Конечно ничего не понял завершения собственно потока(не соединения) именно в windows, когда в Linux всё понятно и однозначно: Код (C++): pthread_exit(0); //как пример
Простите меня за беспокойство!!!!! Уж надоело уже и по поисковикам и тестировать новый код по схеме: VirtualBox(windows, VisualSudio, Проект)<-внутренняя сеть->PC Debian <-локальная сеть->PC_сервер Debian<-сеть оборудования цеха->UNO-1019. Проще переделать скрипты автоконфигурации, настройки и контроля на PC_сервер Debian и не морочить голову. А Windows нужен только как рабочая станция... и я дурак полный, что выбрал контроллер с Windows в 2007 году. Сейчас поставили задачу про надёжность, пытался решить, переделать. А что толку? Проще контроль ужесточить. UNO-1019 получает дату и время от рядом стоящего AC800M... а PC_сервер Debian и программа-клиент на нём получает это время от UNO-1019. Так пусть он PC_сервер Debian и перезапускает этого UNO-1019 по telnet. А виндовс умеет работать если только кнопка сброса рядом. Извините!