с++ Как сейчас принято писать win приложение с асинхронными сокетами?

Phoenix

Задача такая.
1. Подключается некоторое кол-во(не очень большое) клиентов. Новые клиенты могут появляться, но это очень редкая операция.
2.все сообщения нужно конвертировать в некоторый внутренний формат и отправить через закрытую dll. Ответы разконвертировать и оправить клиентам
DLL предоставляет такие функции.
AsyncSend(Message msg)
Recv(int timeout)
В самом простом варианте, вижу так:
while(1)
{
select(0, &clientlist, NULL, NULL, 1);
// обрабатываем сообщения от клиентов
Recv(1)
// обрабатываем сообщения от внутренний dll-ки.
}
1. Каким образом имеет смысл писать подобное? (операционка win2003) Может есть какие-то opensource приложения, которые очень чувствительны к задержкам?
2. Будут ли тормозить треды? Имеет ли смысл их использовать? 16мс не переключения - это в общем-то прилично для этой задачи.
3. Вместо select быстрее использовать WSACreateEvent + WSAEventSelect + WSAWaitForMultipleEvents? Или select это просто обёртка, чтобы было привычнее тем, кто под bsd-socket'ы писать привык?

ava3443

очень чувствительны к задержкам
в смысле, требуется low latency?
тогда 0MQ

ava3443

3. Вместо select быстрее использовать WSACreateEvent + WSAEventSelect + WSAWaitForMultipleEvents? Или select это просто обёртка, чтобы было привычнее тем, кто под bsd-socket'ы писать привык?
с такими замашками лучше сразу ботать IOCP :)
а сколько клиентов-то планируется ?

Phoenix

спасибо!

ava3443

про IOCP я пошутил, если что :)
при таком числе клиентов, где пора использовать IOCP, имхо уже надо с винды перелезать на что-то более сетевое.

Phoenix

да тут большого числа клиентов нет. Это больше для своих нужд. Просто хочется, чтобы приложения крурились на freebsd(хотя бы потому, что уже там всё написано а подключение только через dll. Вот и приходится изобретать шлюзы всякие.
IOCP - как раз про это читал только что. Так и не понял, что это. Это дозавершение операций на уровне операционки что ли?
сейчас используется шлюз на C# c SocketAsyncEventArgs. Там тоже какие- обратные вызовы. Вроде они даже иногда параллельно обрабатываются в разных тредах. Это и есть IOCP ?

kokoc88

да тут большого числа клиентов нет

Тогда лучше пиши синхронный код.
сейчас используется шлюз на C# c SocketAsyncEventArgs. Там тоже какие- обратные вызовы. Вроде они даже иногда параллельно обрабатываются в разных тредах. Это и есть IOCP ?
Да, там используется IOCP.

Phoenix

Тогда лучше пиши синхронный код.

Синхронный точно не подходит. Если какой-то затык и send будет ждать пока клиет буфер разгребёт, все остальные клиенты будут ждать.

Dasar

Синхронный точно не подходит. Если какой-то затык и send будет ждать пока клиет буфер разгребёт, все остальные клиенты будут ждать.
а если синхронный, но по отдельному треду на каждого клиента?

Phoenix

Так тоже хотел. Но даже send на предыдущие recv не должен тормозить обработку следующего recv.
можно конечно как-нибудь выкрутиться. А синхронная отсылка быстрее получается? или из каких соображений её советуете?

Dasar

код меньше и проще

kokoc88

Так тоже хотел. Но даже send на предыдущие recv не должен тормозить обработку следующего recv.
Так используй N потоков, а не один, тебе же написали.

Phoenix

пока send будешь делать, может следующее сообщение прийти. Так что в каждом треде всё равно что-то асинхронное придётся городить.
да и жирно это как-то. Вот 20 клиентов - 20 тредов. память-то не резиновая.

Devid

Вот 20 клиентов - 20 тредов. память-то не резиновая.
У меня вот сейчас >700 тредов работает и ничего. Обычная винда 7. Предел по идее больше 20000.

Phoenix

убедил. аргумент снимается.

slonishka

пока send будешь делать, может следующее сообщение прийти
какая-то странная претензия, ни разу такого не видел.
No indication of failure to deliver is implicit in a send. Locally detected errors are indicated by a return value of -1.
When the message does not fit into the send buffer of the socket, send normally blocks, unless the socket has been placed in nonblocking I/O mode. In nonblocking mode it would fail with the error EAGAIN or EWOULDBLOCK in this case. The select(2) call may be used to determine when it is possible to send more data.
вроде send возвращается сразу, как только положил данные в ядерный буфер. блочится только если в буфере места не хватило. но можно же send делать из треда-обработчика по идее. ну или как там эта проблема решается в модели "по треду на запрос". короче, никогда не слышал, чтобы кто-то отказывался от "по треду на запрос" из-за того, что "пока send будешь делать, может следующее сообщение прийти". жаль, что не пользуюсь этой моделью, а то бы сказал точно, что там.

Phoenix

в буфере места не хватило.

да, именно тогда.
Т.е. recv будет синхронным из треда-для-клиента.
а в некотором другом треде, одном на всех будет крутиться DLL.Recv(int timeout). Вот она получила ответ. и делает ..... что?
1. синхронный send ? - тогд встанет обработка вообще всей проги, если он сразу не отработает
2. асинхронный send - тогда принимаем синхронно, отправляем асинхронно. Так можно вообще?
3. отправляет данные в тред-клиент (как я подумал изначально).
Может что и пропустил.

elenangel

у меня до 3к тредов бывает в приложении, предел зависит от размера стека не тред, настраивается ключем компиляции например. если сделать скажем 128к размер стека треда, то получим до 8192 тредов, потом исчерпается 2Гб адресного пространства, это под Win32. если произойдет стек оверфлоу в треде, винда накрутит автоматом кусочек стека, однако накручивает слишком щедро - сразу метр выдает.

slonishka

а в некотором другом треде, одном на всех будет крутиться DLL.Recv(int timeout). Вот она получила ответ. и делает ..... что?
1. синхронный send ? - тогд встанет обработка вообще всей проги, если он сразу не отработает
2. асинхронный send - тогда принимаем синхронно, отправляем асинхронно. Так можно вообще?
3. отправляет данные в тред-клиент (как я подумал изначально).
я видел два варианта, которые работают:
1) все треды одинаковые, кто-то произвольный из них акцептит внутри мутекса, как заакцептил - начинает обработку и отдает мутекс другому треду, который работает абсолютно так же (лочится и ждет, пока кого-нибудь заакцептит) - это вариант nginx (только в нем не треды, а процессы).
2) один тред крутится в event loop-е с неблокирующими сокетами, он же делает все send-ы и recv-ы в нужные моменты, а обработкой запросов занимаются другие треды - это вариант http://github.com/Begun/lizard
я вообще однопоточно пишу, делая все асинхронным, т.к. это самый быстро работающий способ, но так сложней всего писать, говорят.

slonishka

ну а в твоем случае надо просто, например, акцептить в одном треде, а потом все остальное с заакцепченным сокетом делать в созданном после акцепта треде, например! синхронный сенд заблокирует только этот созданный тред, акцептящий тред не заблокируется => новые клиенты придут и будут обработаны. можно треды не создавать а брать из пула, но это уже детали.
собственно, странно, что я сразу не написал этого, ведь проблемы с тем,
что send заблокирует новые акцепты не существует в реальности!
типа:

void request_handler(void *arg)
{
char buf [4096];
int nb, sd = (int) arg;

nb = recv(sd, buf, 4096);
nb = send(sd, buf, nb);
close(sd);

return NULL;
}

...

for (;;)
{
struct sockaddr_in addr;
pthread_t tid;

int sd = accept(listen_fd, (struct sockaddr *) &addr, sizeof(addr;
if (0 > sd) continue;

if (0 != pthread_create(&tid, NULL, request_handler, sd
{
close(sd);
continue;
}
}

ну и там джойнить где-то надо треды
или детачить их, чтоб самоубивались при выходе
или в пуле сохранять...

Phoenix

можно треды не создавать а брать из пула

это как раз похоже на SocketAsyncEventArgs. Так сейчас.
что send заблокирует новые акцепты не существует в реальности!

для простоты считаем, что новых акцептов нет.
В общем, можно сделать так, если держать несколько дополнительных тредов.
создаём N + M тредов. Они извлекают из общей кучи N сокетов. Оставшиеся M ждут.
Как только что-то пришло один из N (пусть первый) начинает обработку, один из M ( пусть N+1 -ый) занимает сокет и ждёт оттуда новых сообщений.
Меня интересует, насколько эта модель проигрывает( и вообще проигрывает ли?) "всё в одном цикле"
я вообще однопоточно пишу, делая все асинхронным, т.к. это самый быстро работающий способ, но так сложней всего писать, говорят.

точно самый быстроработающий?
Пусть ограничение 60 клиентов стоит. select или WSAWaitForMultipleEvents могут сразу всех слушать.

slonishka

а, у тебя вообще не http-like.
а зачем делать второй recv во время обработки данных, полученных из первого?
у тебя возможна ситуация, когда ответ на первый вопрос посылается позже, чем ответ на второй
и при этом есть вопросы, на которые нужно отвечать в пределах нескольких миллисекунд?
если такая ситуация невозможна, то нужно просто в любом из N тредов делать:
recv(request_buf);
обработка(request_buf, response_buf);
send(response_buf);

потому что send заблокируется только если в ответ на предыдущие recv-ы никак не удается отправить ничего,
что в реальности вообще очень странная какая-то ситуация, ни разу такой не видел.

slonishka

точно самый быстроработающий?
в плане сетевой части - да, всегда и точно, даже стивенс это измерял.
если у тебя какие-то долгоработающие, cpu-пожирающие обработчики запросов, то мультитредный вариант может выиграть на мультипроцессорной тачке, потому что однотредный выжрет один проц и охуеет, а у мультитредного будет еще 7 штук в запасе. но никто не мешает опять же запустить 8 однотредных эвент-лупов на разных портах или нафоркать их при старте (вариант, который используется в nginx). поэтому при правильной эксплуатации, однопоточный асинхронный эвент-луп быстрее и тут.
но разница не очень большая там. 6-7 к 8-9 попугаям где-то.
и от протокола, конечно, тоже зависит, да! стивенс для более http-like штуки измерял,
а у тебя вообще какой-то странный dns, но по tcp...
впрочем, это все лирика, лучше на предыдущий пост ответь. =)

Phoenix

 
а зачем делать второй recv во время обработки данных, полученных из первого?

чтобы сообщения быстрее обрабатывались. грубо говоря, сообщение может обрабатываться DLL 10 секунд, но можно одновременно несколько заслать
Приходят с интервалом 100милисекунд от одного клиента 3 запроса. Если последовательно обработать - он получит 3 ответа через 30 секунд, а должен через 10сенкуд+ ещё чуть-чуть получить
 
в ответ на предыдущие recv-ы никак не удается отправить ничего

 иногда ответы получаются достаточно большие. и они приходят почти подряд.
первый send выполнится, но не всё сообщение уйдёт. А второй сломается.

slonishka

ясно, тяжело тебе. =)
я бы не юзал кипалайвы в твоем случае, и юзал бы новый коннекшен на каждый 10-тисекундный запрос. это позволило бы заюзать простую и всем известную модель из nginx-а, когда одним сокет-дескриптором занимается только один тред. то есть не нужно было бы в мутекс оборачивать вызовы send и recv (и еще неизвестно, что в твоем случае делать с мутексом, если send готов, а мутекс ждет recv-а, короче, не верю я в эту изобретенную тобой модель с N+M тредами, заебешься ты ее писать имхо).
но можешь попробовать написать так, как ты хочешь, если ты такой отважный.

Phoenix

я условно сказал про 10секунд. (зря,да) В реальности, время на установку соединения является существенным.
реальные данные где-то такие.
время обработки 20-30милисекунд. сообщения могут приходить хоть совсем подряд. <1ms между ними

slonishka

время на установку соединения является существенным
время обработки 20-30милисекунд
можно UDP еще попробовать тогда.
типа делать recvfrom одного и того же адреса из нескольких тредов. каждый тред делает:
recvfrom(...);
обработка(...);
sendto(...);

не знаю, как у UDP с огромными ответами (достаточно ли просто буфер сокета подкрутить на обоих концах или есть еще подводные камни доставка соот-но тоже не гарантируется, но это можно опять же написать руками. :grin: :grin: :grin:
ну а с TCP (то есть с твоей N+M моделью) морока другая - придется в мутексы send и recv оборачивать, причем неясно, что делать, если send готов, а recv держит мутекс и в него ничего не приходит. надо таймауты будет ставить, короче тоже заебешься писать. тем более, таймауты - это огромный удар по латенси (готовый send будет ждать, пока recv затаймаутится)! может и хуже выйдет, чем с простой человеческой nginx-моделью.
поздравляю! :grin:
вообще, задача кажется какой-то пиздецовой и надуманной.
можешь описать содержательную часть? что там на самом деле у тебя приходит и обрабатывается?

Phoenix

udp - Это замечательно. в одном месте использую. Но там действительно не так критична потеря пакета. (управляющий GUI к консольному приложению.)
почему не хочу здесь udp внедрять:
во-первых, не хочется, чтобы порядок пакетов мог поменяться.
во-вторых, реально не доходит часть. и я сомневаюсь, что смогу вот так вот и взять реализовать механизм переполучения данных лучше/быстрее, чем он в tcp сделан.
где-то здесь писали о каком-то протоколе http://en.wikipedia.org/wiki/Stream_Control_Transmission_Pro...
вроде как раз то, что нужно. Но он под винду сделан через какой-то драйвер. Вот сейчас хочу его заюзать. Если честно, сомневаюсь, что он вообще полетит.

slonishka

вроде как раз то, что нужно. Но он под винду сделан через какой-то драйвер. Вот сейчас хочу его заюзать. Если честно, сомневаюсь, что он вообще полетит.
я тоже сомневаюсь. =)

Phoenix

обрабатываются ответы от банка. нужно отсылать им всякие приказы, а ответ приходит результат выполнения операции.

slonishka

мне кажется, если уж ты добрался до таких сложностей, как использование sctp и recv/send в один сокет-дескриптор из разных тредов, можно уж тогда написать всю сетевую обработку полностью асинхронно в одном треде и в других тредах только свою либу вызывать, которая ответ генерирует - это явно проще будет...
для оповещения сетевого треда о том, что ответ сгенерирован библиотекой, Spin юзал пайп. писал в него байтик 'w' и читал его в том же евент-лупе, что и сетевые сокеты. работало типа. может проще способ есть, хз.

Phoenix

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

kokoc88

собственно, ты вот привёл инфу, что быстрее второй вариант скорей всего. Так что напишу что-нибудь тестовое. Посмотрю, насколько быстрее.
Его вариант не быстрее, он более масштабируемый. Вполне может оказаться, что до 100 одновременных подключений более производительным будет синхронное решение.
Оставить комментарий
Имя или ник:
Комментарий: