· Начало · Отвђтить · Статистика · Поиск · FAQ · Правила · Установки · Язык · Выход · WASM.RU · Noir.Ru ·

 WASM Phorum —› WASM.NETWORKS —› блокирующие сокеты....

Посл.отвђт Сообщенiе


Дата: Авг 20, 2003 20:04:08

Люди, я решил сервак написать на сокетах блокирующих...
Правильна ли моя идея (в краце):

Main:
1.Создаем серв. сокет блокирующийся.
2.Привязываем на FD_ACCEPT его к окну...
3.Открываем сокет, слухаем :-)
--------------------------------
Window:
1.На FD_ACCEPT - принимаем соединение...
2.Полученный сокет подвешиваем к окну на FD_READ и FD_CLOSE
--------------------------------
FD_READ:
1.Конструктим тред.
2.Принимаем с сокета в блокирующем режиме.
3.Обрабатываем\шлем ответ
4.Убиваем тред.
--------------------------------
А что собственно надо?
Немного ясности:

1.В моей идее сокеты после обработки не закрываются... :-) (так надо)... Через них рефрешь шлется... Статусы сокетов хранятся...
2.В этой всей лабуде интересуют меня следующие аспекты: recv и send тормознут ведь только тред в котором они есть?
3.Как это будет бегать на 9х? При нагрузке 5-20 чел...???

Пишу на C++ без MFC и Runtime :-)
Описал в краце... но енто главный вопрос.
Если не прав - поправьте.... плз!!!
Спасибо заранее.


Дата: Авг 21, 2003 06:12:16

Правильно!

В моей идее сокеты после обработки не закрываются
No problem. В некоторых случаях сокет закрывает клиент, в других - сервер. Всё зависит от конкретной реализации. В "Cinchy server" (лежит на сайте в разделе исходников), например, есть баг связанный с преждевременным закрытием сокета...

recv и send тормознут ведь только тред в котором они есть?
Верно.

Как это будет бегать на 9х? При нагрузке 5-20 чел...???
Количество чел. не есть мера траффика... Что за сервер? HTTP?


Дата: Авг 21, 2003 16:18:20

1. я бы тебе посоветовал использовать ассинхронные сокеты - блокирующие надо переодически опрашивать, что при большом числе клиентов (т.е. нитей), или интенсивном траффике будет жрать ресурсы.
А так нитка спит, пока ей winsock не пошлет мессагу.
2. на блокирующих сокетах хреново отлавливать момент дисконнекта клиента, на ассинхронных все намного проще.

подводные камни:
1. учти, на ассинхронных сокетах winproc при получении FD_READ вызывается в контексте нити самого winsock'а, поэтому надо быстренько прочитать буфер, просигналить обрабатывающей нити что есть данные, и отдать управление.
2. имей в виду, что можешь сделать два раза send по 100 байт, а recv получит один пакет в 200 байт, причем на блокирующих сокетах, как правило, именно так и происходит.
а может быть и наоборот...
короче говоря tcp - это stream-oriented сокеты, читай мануалы.


Дата: Авг 22, 2003 17:49:28

Я вас понял, но вот такой глюк....
1. Сервер не НТТР а ... для БД.
2. Юзеров будет не больше 25 чел...
3. Ведь на блок сокетах на отконнект от сервера клиентом сервер (при моем перехвате) вызовет FD_CLOSE ? и все станет ок? Или есть еще что то ?
4. При посылки нескольких цепочек как отмерять длину а?
Первым двордом слать длину блока чтоль?
5. Серверный сокет не получит с клиента никаких запросов, если он пыполняет ответ для сокета... Запросы клинета уместятся в одном пакете :-) наверное...
6. Тред констрактится только на чтение... (не знаю, как енто быстро :-) и после отправки убивается...
7. Какой размер буфера у сокета? Как узнать? Можно ли менять?


Дата: Авг 22, 2003 20:20:41

Юзеров будет не больше 25 чел...
на такие вещи лучше не рассчитывать и сразу писАть на века...
через день/месяц понадобиться прикрутить 50 чел., и че?

Ведь на блок сокетах на отконнект от сервера клиентом сервер (при моем перехвате) вызовет FD_CLOSE?
так это как раз на ассинхронных сокетах тебе будут идти нотификационные сообщения, а на блокирующих - хрен, ты должен его сам опрашивать - есть ли данные, нет ли дисконнекта и т.п.
по моему опыту, блокирующие сокеты на предмет дисконнекта работают крайне не устойчиво, т.е. клиент отвалился, а сокет продолжает выдавать что все тип-топ.
причем это не у меня руки кривые, я такие вещи не раз замечал на фирменных компонентах для делфи типа Indy.

Первым двордом слать длину блока чтоль?
ага, вводишь промежуточный буфер, который всасывает данные из сокета, а при должном наполнении маякует что данные пришли.
Тока учти насчет stream-oriented... напр., ты отправляешь 4 пакета по 100 байт, а получаешь два пакета 250 и 150 байт, т.е за один "физический" прием может получить сразу два "логических" пакета... а можешь и полтора... дальше думай сам...

Запросы клинета уместятся в одном пакете :-)
даже мануал говорит, что на это рассчитывать нельзя. Пакеты могут дефрагментироваться как угодно.

Тред констрактится только на чтение...
это не есть гуд, т.к. создание треда не есть быстро...
лучше один тред на клиента, и тред существует пока клиент не отвалится. Также советую почитать Рихтера "Programming server-side applications" насчет thread pool и вообще.

Какой размер буфера у сокета?
Смотря что понимать под буфером...
по сетки данные летают по 4K, у winsock'а есть свои внутренние буфера для отправки и принятия сообщений.
Размер - незнаю, может от версии меняется, может от загрузки машины, короче, это детали которые и не надо знать - будет лучше, если ты сразу приучишься воспринимать tcp-сокет как stream, а не как буфер некоторого размера.


Дата: Авг 22, 2003 22:24:42

4. При посылки нескольких цепочек как отмерять длину а?
Первым двордом слать длину блока чтоль?

Желательно использовать парсер, как в HTTP (читает "Content-Length" в header'е и качает всё пока не получит н-ое количество байт или сокет закроется). Я тут недавно постил пример.


Дата: Авг 23, 2003 11:45:28

1.Да, но вот тут я на Васме сампл видел на блокирующих сокетах... Там вроде простой вебсервер что-ли.... И там на FD_CLOSE привязано.... Это ошибка чтоли?
2.Както будучи еще на Дельфах (тфу тфу) ... я сделал какую-то фигню (не помню) и при большом количестве тредов (около 20) там затормоз винды начился такой мощный... Я понимаю, что все это было на 120 пентиуме, но не будит ли енто на нормальном компе (Celeron 600?) Если уж клиентов еще и больше 25 может стать :-)
3.Когда в сокете кончается буфер, а клиент скажем шлет - что получится?
----------------------------------------------
Не, слать еще какой-то мусор в сокеты с указанием длины... не интересно...
Лучше дворд послать 1 раз... и им отмерять... там следующий... по нему отмерять :-)


Дата: Авг 23, 2003 18:13:49

Да, но вот тут я на Васме сампл видел на блокирующих сокетах...
Ты сначала определись, что ты понимаешь под "блокирующими сокетами" :)
Есть несколько основных сообщений - FD_READ, FD_WRITE, FD_CLOSE, FD_ACCEPT. Отправлять системе сообщения тебе или нет - определяешь ты через WsaAsyncSelect.
Ты можешь указать FD_CLOSE, но не указать FD_READ, тогда при дисконнекте клиента тебе пришлют FD_CLOSE, а при получении данных тебе не пришлют нихрена - ты сам должен опрашивать сокет через ioctl(FIONREAD)... или не только так...
Под блокирующими обычно понимают сокеты, у которых кроме FD_ACCEPT ничего не указано. Иногда это удобно, иногда нет.
Напр., при вызове recv твоя нитка будет висеть, пока не придут данные, или не произойдет таймаут. При вызове send будет висеть, пока данные реально не уйдут в сеть.
Короче, читай MS Press Э. Джонс "Программирование в сетях Microsoft Windows". Она кажется была ftp://read:good_book@130.88.244.210 (Volodya, большой сэнкс за линк)

там затормоз винды начился такой мощный
Читай Рихтера thread pool, или делай сам нечто аналогичное

Когда в сокете кончается буфер, а клиент скажем шлет - что получится?
Это смотря какой буфер кончается...
На примере асинк сокетов:
Если ты отправляешь 1Мб через send, а у winsock'а есть буфер 10Кб, то он всосет именно 10Кб, о чем тебе и скажет, а когда данные уйдут в сеть и у него опять освободится буфер, он тебе пришлет FD_WRITE, типа давай следующую порцию...
Именно поэтому я тебе и писал про удобство промежуточных буферов, которые берут всю эту логику на себя.

Лучше дворд послать 1 раз... и им отмерять
Тока тут аккуратно надо, а то какой нибудь нехороший дядя тебе пришлет пакет с первым двордом = 2Гб, и начнет тебе в сокет данные лить, пока твой сервер не охренеет :)))

Вообщем, ты литературу почитай, да тестовые примерчики попиши, все сам поймешь :)
А когда на блюдечке все выкладываю, так ничему не научишься...


Дата: Авг 23, 2003 18:41:48

Ок, ребята... Все понял... Начинаю писать :-)
Хотя вряд ли злой дядя мне пришлет дворд а 2 ГБ :-) Я там ограничение сделаю на 1 ГБ :-)
Про FD_WRITE я знаю... тока реально оно у меня никогда не вызывалось :-)
Ладно. Спасибки что объяснили.


Дата: Авг 23, 2003 19:52:14

Gray_Wolf
Там вроде простой вебсервер что-ли.... И там на FD_CLOSE привязано.... Это ошибка чтоли?
Там действительно есть глюк, но не в FD_CLOSE :-) На то и годятся примеры, шоб в них глюки отлавливать.


Дата: Ноя 23, 2003 04:38:55 · Поправил: Безпощадный даос

Как это блокирующий если ты полученый сокет вешаешь на окно, чем ты это там его вешаешь? Я знаю только посредством WSAAsyncSelect а енто уже не блокируюший.

Вот сервак на блокирующих сокетах
#include <windows.h>

#define WS_VERSION_REQD 0x0101
#define SZ_MAX_HOST_NAME 256
#define SZ_MAX_BUFF 1024
#define SZ_MAX_NIK_NAME 15
#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[i].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[i].skt != INVALID_SOCKET) closesocket (users[i].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[i].skt == INVALID_SOCKET){

users[i].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[i].skt != INVALID_SOCKET){

FD_SET ( users[i].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[i].skt != INVALID_SOCKET)
if (FD_ISSET (users[i].skt, &readfds)){

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

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

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

count--;
}

return 1;
}


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

return 1;
}


Powered by miniBB 1.6 © 2001-2002
Время загрузки страницы (сек.): 0.626