Форум программистов «Весельчак У»
  *
Добро пожаловать, Гость. Пожалуйста, войдите или зарегистрируйтесь.
Вам не пришло письмо с кодом активации?

  • Рекомендуем проверить настройки временной зоны в вашем профиле (страница "Внешний вид форума", пункт "Часовой пояс:").
  • У нас больше нет рассылок. Если вам приходят письма от наших бывших рассылок mail.ru и subscribe.ru, то знайте, что это не мы рассылаем.
   Начало  
Наши сайты
Помощь Поиск Календарь Почта Войти Регистрация  
 
Страниц: [1]   Вниз
  Печать  
Автор Тема: "Многоразовый" ТСР сервер.  (Прочитано 22528 раз)
0 Пользователей и 1 Гость смотрят эту тему.
Diletant
Помогающий

de
Offline Offline

« : 18-07-2003 10:33 » 

Или как грамотно прервать блокирующий accept?
Постановка задачи:
Имеется ТСР сервер под Windows. Т.е. дежурный набор, неоднократно описанный в книгах и статьях в клубе:

SOCKET ListenSocket;
SOCKET AcceptSocket;
...
bind(ListenSocket,...);
listen(ListenSocket,..):

whille(1)
{
    AcceptSocket = accept(ListenSocket,...);

.....
}

Все это разбавлено обработками ошибок и т.д. Обычно все это прекрасно работает для сервера непрерывного действия. Но хочется иногда сервер остановить, что-нибудь модернизировать, и запустить заново.
Вопрос: Как грамотно выйти из бесконечного цикла с блокирующим accept внутри. Есть ли другое решение, кроме как closesocket( ListenSocket)  и обработки ошибки 10004?
Записан
RXL
Технический
Администратор

Offline Offline
Пол: Мужской

WWW
« Ответ #1 : 18-07-2003 12:02 » 

Как прервать в win - не знаю, а закрыть listen сокет - просто делай closesocket().
И кстати просто завершение приложения должно автоматически закрыть все файлы и соединения.
Записан

... мы преодолеваем эту трудность без синтеза распределенных прототипов. (с) Жуков М.С.
sss
Специалист

ru
Offline Offline

« Ответ #2 : 06-08-2003 08:57 » 

Делай блокирующий accept в отдельном потоке.
Записан

while (8==8)
Гром
Птычк. Тьфу, птычник... Вот!
Готовлюсь к пенсии

il
Offline Offline
Пол: Мужской
Бодрый птах


« Ответ #3 : 06-08-2003 09:29 » 

А зачем делать accept лучше использовать select
В этом случае можно открывать сокет на 127.0.0.1 который будет слушать на любом свободном порту, и открывая на него сокет срывать блокирующую опперацию.

Кстати в любом случае - блокирующий код ОБЯЗАТЕЛЬНО размещать в отдельном потоке.
Записан

А птичку нашу прошу не обижать!!!
mixa
Гость
« Ответ #4 : 01-11-2003 00:25 » 

На селекте делается шикарный однонитевой сервер !!!
Записан
RXL
Технический
Администратор

Offline Offline
Пол: Мужской

WWW
« Ответ #5 : 01-11-2003 00:30 » 

Цитата: mixa
На селекте делается шикарный однонитевой сервер !!!

А работает ли select() на win?
Записан

... мы преодолеваем эту трудность без синтеза распределенных прототипов. (с) Жуков М.С.
Гром
Птычк. Тьфу, птычник... Вот!
Готовлюсь к пенсии

il
Offline Offline
Пол: Мужской
Бодрый птах


« Ответ #6 : 01-11-2003 10:56 » 

Цитата

А работает ли select() на win?

Да...
Цитата

На селекте делается шикарный однонитевой сервер !!!


На select делается любой тип сервера - причем поток ожидания один - а потоки рабочие могут быть как в единственном так и в множественном числе - зависит от тебя Улыбаюсь
Записан

А птичку нашу прошу не обижать!!!
ixania
Гость
« Ответ #7 : 03-12-2003 10:27 » 

Вот реализация однопоточного сервака, и никто не блокирует и сервак остановить не проблема Ага

#include <windows.h>

#define WS_VERSION_REQD 0x0101
#define SZ_MAX_HOST_NAME 256
#define SZ_MAX_BUFF 1024
#define SOCKET_ID 0x3737



char lpsMyHostName [SZ_MAX_HOST_NAME];

// Имя евента
char* EvExitName = "ChatExitEvent";

SOCKET srvSocket = INVALID_SOCKET;

// тайм аут для select чтоб он не блокировал
timeval tout = {0,500};

// буфер для приема
char buffer[SZ_MAX_BUFF];

// структура описывающая подключеный узел
struct USERS {

SOCKET skt;

}users [FD_SETSIZE];

int InitChat ();
int InitUndoChat ();
int DoChat ();
int DoAccept ();
int DoReceive ();
int DoSend (int to_send);

WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow)
{
HANDLE EvExit;

// попробуем сначала открыть евент
EvExit = OpenEvent (EVENT_MODIFY_STATE, false , EvExitName);

if (!lstrcmpi (lpCmdLine, "exit")){

// отсигналить выход сервака
if (EvExit){ // если сервер запущен
SetEvent (EvExit); // выставить евент
CloseHandle (EvExit);
}

return 0;
}

// если евент открылся, значит мы имеем уже запущенный сервак
if (EvExit){CloseHandle (EvExit); return 0;} // так что выходим

// создаем евент
EvExit = CreateEvent(NULL, true, false, EvExitName);

if (EvExit){ // если создался

if (InitChat ()) // инициализация чата

while (WaitForSingleObject (EvExit, 10) != WAIT_OBJECT_0) // пока не выставлен евент
if (!DoChat ()) break; // чатимси

InitUndoChat (); // деинициализировать сервак

CloseHandle (EvExit); // закрыть евент
}

return 0;
}
//---------------------------------------------------------------------------

// иниуиализация сервака
int InitChat()
{
WSADATA stWSAData;
struct hostent*lpHostInfo;

// инициализация масива сокетов
for (register i = 0; i < FD_SETSIZE; i++) users.skt = INVALID_SOCKET;

if (WSAStartup(WS_VERSION_REQD, &stWSAData) == 0) // запрашиваем winsock2.dll

if ( gethostname(lpsMyHostName, SZ_MAX_HOST_NAME) != SOCKET_ERROR ) // имя нашего хоста

if ((lpHostInfo = gethostbyname(lpsMyHostName)) != NULL) // запрос информации о нашем хосте

// открываем слушающий сокет
if ((srvSocket = socket(PF_INET, SOCK_STREAM, 0)) != INVALID_SOCKET){

SOCKADDR_IN sin;

sin.sin_family = AF_INET;
sin.sin_port = htons(SOCKET_ID);
sin.sin_addr.s_addr = *(long*)lpHostInfo->h_addr_list[0];

// биндим его т.е. привязываем к номеру порта
if (bind (srvSocket, (LPSOCKADDR)&sin, sizeof(sin)) != SOCKET_ERROR)
// выставляем на прослушку
if (listen (srvSocket, 5) != SOCKET_ERROR) return 1;

}


return 0;
}

// деинициализация сервака
int InitUndoChat ()
{

// закрываем все соединения
for (register i = 0; i < FD_SETSIZE; i++)
if (users.skt != INVALID_SOCKET) closesocket (users.skt);

// закроем слушающий сокет
if (srvSocket == INVALID_SOCKET) closesocket (srvSocket);

WSACleanup (); // освобождаем winsock2.dll библиотеку

return 1;
}

// основная процедура чата
int DoChat ()
{
DoAccept (); // проверить запросы на конект
DoReceive (); // прием и и отсылка всем узлам сообщений

return 1;
}

// проверить запросы на конект
int DoAccept ()
{
int i;

FD_SET readfds;

FD_ZERO ( &readfds ); // обнулить структуру
FD_SET ( srvSocket, &readfds ); // занести слушающий сокет в структуру

// если есть запрос на конект
if (select (0, &readfds, NULL, NULL, &tout) > 0){


SOCKET skt = accept (srvSocket, NULL, NULL );

for (i = 0; i < FD_SETSIZE; i++)// ищем свободную структуру
if (users.skt == INVALID_SOCKET){

users.skt = skt;
break;
}

// если нкт свободных, закрываем соединение
if (i == FD_SETSIZE) closesocket (skt);
}

return 1;
}

// прием данных из сети
int DoReceive ()
{
int count = 0; // количество подключенных узлов

FD_SET readfds;

FD_ZERO ( &readfds );

for (register i = 0; i < FD_SETSIZE; i++)
if (users.skt != INVALID_SOCKET){

FD_SET ( users.skt, &readfds );
count++;

}


if (count) // если есть подключенные узлы
// проверить на готовность к чтению
if ((count = select (0, &readfds, NULL, NULL, &tout)) > 0)
for (register i = 0; i < FD_SETSIZE && count; i++)
if (users.skt != INVALID_SOCKET)
if (FD_ISSET (users.skt, &readfds)){

// прием данных
int err = recv (users.skt, buffer, SZ_MAX_BUFF, 0);

if (err > 0) DoSend (err); // если чтото приняли, отослать всем

// в противном случае, ошибка или узел отключился
// закрываем сокет
else{ closesocket (users.skt); users.skt = INVALID_SOCKET;}

count--;
}

return 1;
}


int DoSend (int to_send)
{
for (register i = 0; i < FD_SETSIZE; i++)
if (users.skt != INVALID_SOCKET){
send (users.skt, buffer, to_send, 0);
}

return 1;
}
Записан
sss
Специалист

ru
Offline Offline

« Ответ #8 : 05-12-2003 08:12 » 

Гром, какой такой select без access ?
select - просто неблокирующая технология проверки от Berkley.
Причем ужасная !

Лучше использовать QueueUserAPC, который выводит поток из ожидания сигнала с ошибкой WAIT_IO_COMPLETION.
Причем, также вызывается в контексте потока указанная в методе функция.
Короче метод супер (спасибо J. Richter)
Записан

while (8==8)
Diletant
Помогающий

de
Offline Offline

« Ответ #9 : 05-12-2003 09:59 » 

2 ixania

Зачем было приводить столь длинный код (подробности смотреть лень, но коряво это как-то работать должно, похоже сначала для Юникса писалось), чтобы повторить, что accept можно прервать, закрыв слушающий сокет?
Записан
ixania
Гость
« Ответ #10 : 07-12-2003 18:44 » 

Согласен, коряво выглядит для форточек, для юникса в жижни и строчки не написал  Вот такой я вот , но написал я это для того чтоб показать БСД "стиль" програмирования сокетов, вот и выложил, у ребят возеикали вопросы: как избежать блокировки и работает ли селект в форточках.
Записан
Olej
Гость
« Ответ #11 : 26-12-2003 15:27 » 

Цитата: sss
Гром, какой такой select без access ?
select - просто неблокирующая технология проверки от Berkley.
Причем ужасная !


А кроме того, обратите внимание, что POSIX отмечает select: not thread safe - не так многие удостаиваются! Ага
При использование pthread_* - select - это гроб с музыкой. Проверено!
Записан
ixania
Гость
« Ответ #12 : 30-12-2003 01:39 » 

Конечно select под форточки не очень приветствуется, но для того чтоб прервать блокирующий вызов предусмотрена функция WSACancelBlockingCall().
Записан
sss
Специалист

ru
Offline Offline

« Ответ #13 : 30-12-2003 03:50 » 

ixania,  WSACancelBlockingCall() устарела. Не рекомендуется использовать в winsock 2 и выше .
Записан

while (8==8)
ixania
Гость
« Ответ #14 : 30-12-2003 04:13 » 

Ага  Жжешь  как и select  8)
Записан
sss
Специалист

ru
Offline Offline

« Ответ #15 : 30-12-2003 09:51 » 

ну
Записан

while (8==8)
RXL
Технический
Администратор

Offline Offline
Пол: Мужской

WWW
« Ответ #16 : 30-12-2003 09:58 » 

Виндовс с версии 1.0 то же не рекомендуется  Ага
Записан

... мы преодолеваем эту трудность без синтеза распределенных прототипов. (с) Жуков М.С.
ixania
Гость
« Ответ #17 : 31-12-2003 01:27 » 

Я ващета сизу на:

WSACreateEvent() --- создать новое событие;
WSACloseEvent() --- удалить событие;
WSAEventSelect() --- связать событие с сокетом и его внутренним событием (пришли новые данные, готов к передаче и т.д.);
WSAWaitForMultipleEvents() --- ожидание событий.

Самое удачное для серваков с подержкой не больше 100 подключений.
Записан
um
Гость
« Ответ #18 : 10-03-2004 23:38 » 

а нельзя ли перед accept разблокировать сокет?
например,
DWORD arg = 1; ioctlsocket(SOCKET(listen_socket),FIONBIO,&arg);
а потом проверять результат, который вернул accept.
Записан
ixania
Гость
« Ответ #19 : 13-03-2004 00:23 » new

В худшем случае воспользуйся select - ом, как в пимере что приводил. А как ты говоришь, работать будет но вот не рекомендуется.
Записан
Страниц: [1]   Вверх
  Печать  
 

Powered by SMF 1.1.21 | SMF © 2015, Simple Machines