completion port, WSARecv, buflen

Тема в разделе "WASM.NETWORKS", создана пользователем Aspire, 15 фев 2009.

  1. Aspire

    Aspire New Member

    Публикаций:
    0
    Регистрация:
    19 май 2007
    Сообщения:
    1.028
    Простой вопросик для тех кто в теме.
    Длина пришедших данных больше размера буфера. Крутим цикл динамически увеличивая размер буфера дописывая туда данные.
    Мой колбэк на порту завершения будет вызван:
    а. как только заполнен весь буфер и в каждый последующий цикл при дочитывании данных;
    б. как только считаются все данные из очереди.

    Надеюсь, что понятно задал вопрос. Спасибо.
     
  2. Aspire

    Aspire New Member

    Публикаций:
    0
    Регистрация:
    19 май 2007
    Сообщения:
    1.028
    Провел маленький эксперимент.
    В итоге, правильно будет вызывать WSARecv в цикле до тех пор, пока она будет возвращать 0. При этом коллбэк на порту завершения будет вызываться каждый раз.
    Чтобы определить разрыв соеденения, стоит проверять, что кол-во полученных байт при это != нулю.
     
  3. Aspire

    Aspire New Member

    Публикаций:
    0
    Регистрация:
    19 май 2007
    Сообщения:
    1.028
    Поскольку правильным является вариант "а", то придется передавать обработчику на порту информацию о том, что получены все данные.
     
  4. max7C4

    max7C4 New Member

    Публикаций:
    0
    Регистрация:
    17 мар 2008
    Сообщения:
    1.203
    а посмотрите пожалуйста функцию ioctlsocket с константой FIONREAD во втором параметре.
    если не где или не поняли что я имею ввиду, то позволю себе небольшую цитату
    в моей реализации я поступаю так:
    1. запускаю поток слушать порт на предмет входящих соединений (опционально сервер/клиент). каждое принятое соединение добавляется в динамический синхронный (между потоками) список.
    2. запускаю поток, где проверяю пришли ли данные, к какому-либо клиенту, с помощью описанной выше функции, получаю данные, добавляю в динамический список (уже уникальный для каждого клиента) указатель на буфер с данными и устанавливаю у клиента (в клиентской структуре данных) флаг о приеме данных.
    3. с помощью внутренней функции я могу узнать состояние каждого клиента и получить (разом!) все накопившиеся данные (предварительно склеив их в динамический буфер, а в прочем это происходит еще при добавлении) от конкретного клиента.
    4. разрыв связи же определяется по результату ioctlsocket и соответственно закрываются (свои соответственно я закрываю сам) все структуры по данному клиенту, но по хорошему, при окончание связи, я посылаю пакет об окончании сеанса.
    может это громоздко, но эта реализация работает без создания окон и производительность при удерживание до 5000 клиентов на одном потоке обеспечивается отличная.
     
  5. Aspire

    Aspire New Member

    Публикаций:
    0
    Регистрация:
    19 май 2007
    Сообщения:
    1.028
    max7C4 Спасибо, но.
    1. ioctlsocket с флагом FIONREAD возвращает не размер всего доступного буфера, а размер данных, которые могут быть получены за один вызов recv.
    2. Данные вполне могут еще не попасть в буфер в момент вызова ioctlsocket, но это отнюдь не означает что их не будет при последующем вызове WSARecv.
    3. Цитата из "Network Programming For Microsoft Windows":
    4. Цитата из Winsock Lame List item #26:
    Вся информация взята и процитирована с форумов rsdn.ru, где этот вопрос периодически обсуждается на протяжении хз скольки лет.
     
  6. max7C4

    max7C4 New Member

    Публикаций:
    0
    Регистрация:
    17 мар 2008
    Сообщения:
    1.203
    А буфер Вам ни кто и не даст. Его Вы должны сами выделить. А вот какой длинны его выделять вот это уже и требуется узнавать.
    Ну так если Вы собираетесь получать данные через пол часа - то конечно, а если сразу - все в порядке.
    Не понимаю что там должно удаляться, но у меня все работает, даже быстрее, чем этого хочется! Да и вообще: не нравится - идите в Microsoft и пишите Windows. У правильного программиста все выверено до байта, а не "ой, мне пожалуй еще понадобится!" - как у домохозяйки все на глазок!
    Вы не в теме. Вас ни кто не просит дожидаться конца сообщения и справляться о его длине у этих функций!
     
  7. Aspire

    Aspire New Member

    Публикаций:
    0
    Регистрация:
    19 май 2007
    Сообщения:
    1.028
    max7C4 :) Привет. А чо случилось, что столько восклицательных знаков? Я вам, значит, "спасибо", а вы мне - идитена переписывайте виндовс..
    1. Представьте, что у вас есть большое кол-во данных, которые не получить с помощью одного вызова recv, и вам придется вызывать его в цикле.
    Что вернет ioctlsocket+FIONREAD в этом случае? Подозреваю, что то кол-во данных, которое можно получить за один вызов recv. Проведите эксперимент. [add/] Я - уже ;) [add]
    2. Собсно это вытекает из первого, но может, есть какие-то другие причины (может, загрузка проца или еще что-нить). Я их не знаю.
    3.
    Не знаю как это вы замерили, что у вас ioctlsocket+recv работают быстрее чем recv.
    Если вы эту мысль даже не рассматриваете, то вскоре нарветесь на переполнение буфера (см п1). И вашу программу будут юзать уже совсем другие люди :lol:
     
  8. max7C4

    max7C4 New Member

    Публикаций:
    0
    Регистрация:
    17 мар 2008
    Сообщения:
    1.203
    Глупость. Прекрасно представляю. Могу выдержать аудио поток в телефонном качестве (правда без помех, которые предоставляет АТС) от 5к клиентов на поток. загрузка сервера на гигабитном канале 35-40% (iP4 HT).
    [add]для подтверждения.
    48кбит/с * stereo * 16 бит=192 кбит/с * 5000 = 937,5 Мбит/с = 117,1875 Мб/с
    больше поток не берет[/add]
    чушь городите. загрузка процессора ни как не влияет на квантование времени между задачами. загрузка процессора - это лишь отражение использования выделенных квантов с пользой (или без нее) (все выше сказанное точно можно отнести к платформе NT начиная с Windows NT 4.0).
    ioctlsocket+alloc+recv
    собрать буферы в очередь не фиксированной длинны 3 команды
    Код (Text):
    1. ;eax - указатель на начало буфера
    2. ;ebx - указатель на структуру служебной информации, которую использую я
    3. ;ecx - длина буфера (получили из ioctlsocket)
    4. mov edx, [ebx+bufptr]
    5. mov [edx], eax
    6. mov [eax], ecx
    вместо Realloc который будет стараться выделить Вам линейный участок памяти
    alloc+recv+realloc+recv+realloc+recv+...
    у кого что болит. А вот у меня что-то почему-то такого не случается за 7,5 лет написания программ на ассемблере (хотя ассемблер это не первый мой язык и по этой причине писать программы выверяя все до байта я умею!). Ну а что вы скажите если система не найдет для вас памяти? у меня такого не будет, а от переполнения буфера я полностью застрахован! Я знаю сколько данных я получаю
     
  9. Aspire

    Aspire New Member

    Публикаций:
    0
    Регистрация:
    19 май 2007
    Сообщения:
    1.028
    Собрать буферы можно по разному, но мы сейчас говорим не об этом, а о том, зачем нужен лишний вызов ioctlsocket.
    Вот простейший код:
    Код (Text):
    1. #define MEGABYTE 1024*1024
    2.  
    3. PVOID pMem = VirtualAlloc(0, 700*MEGABYTE, MEM_RESERVE, PAGE_EXECUTE_READWRITE);
    4. VirtualAlloc(pMem, MEGABYTE, MEM_COMMIT, PAGE_EXECUTE_READWRITE);
    5.  
    6. tv.tv_usec = 100;
    7. tv.tv_sec = 0;
    8. buf.fd_count = 1;
    9. buf.fd_array[0] = cSocket;
    10. while(TRUE){
    11.     buf.fd_count = 1;
    12.     buf.fd_array[0] = cSocket;
    13.     int bytes = 0;
    14.     int size = MEGABYTE;
    15.  
    16.     _asm push pMem;
    17.  
    18.     while(select(0, &buf, 0, 0, &tv)){
    19.         bytes = recv(cSocket, (char*)pMem, size, 0);
    20.         if(bytes == SOCKET_ERROR){
    21.             // Очищаем ресурсы, выходим.
    22.         }
    23.         pMem += bytes;
    24.         size -= bytes;
    25.         if(size <= bytes){
    26.             VirtualAlloc((PVOID)((long)pMem+size), MEGABYTE, MEM_COMMIT, PAGE_EXECUTE_READWRITE);
    27.             size = MEGABYTE;
    28.         }
    29.         if(!bytes){ // disconnected
    30.             // Очищаем ресурсы, выходим.
    31.         }
    32.     }
    33.     _asm pop pMem;
    34. }
    Это всего лишь для демонстрации. На асинхронном сокете, это будет еще быстрее, тк, уберется ненужный select.
    Примерно:
    Код (Text):
    1. #define MEGABYTE 1024*1024
    2.  
    3. PVOID pMem = VirtualAlloc(0, 700*MEGABYTE, MEM_RESERVE, PAGE_EXECUTE_READWRITE);
    4. pMem = VirtualAlloc(pMem, MEGABYTE, MEM_COMMIT, PAGE_EXECUTE_READWRITE);
    5.  
    6. while(TRUE){
    7.     DWORD Flags = 0;
    8.     int bytes = 0;
    9.     wsb.len = MEGABYTE;
    10.     wsb.buf = pMem;
    11.  
    12.     _asm push wsb.buf;
    13.  
    14.     while(!(WSARecv(cSocket, &wsb, 1, &bytes, &Flags, 0, 0))){
    15.         wsb.buf += bytes;
    16.         wsb.len -= bytes;
    17.  
    18.         if(size <= bytes){
    19.             VirtualAlloc((PVOID)(wsb.buf+wsb.len), MEGABYTE, MEM_COMMIT, PAGE_EXECUTE_READWRITE);
    20.             wsb.len = MEGABYTE;
    21.         }
    22.         if(!bytes){ // disconnected
    23.             // Очищаем ресурсы, выходим.
    24.         }
    25.     }
    26.     _asm pop wsb.buf;
    27. }
    Здесь, я конечно помелочился, выделяя памяти по мегобайту ;)
    Вы видите здесь ioctlsocket? И я нет. Потому, что она здесь нафиг не нужна. У вас, вроде должно рабатать быстрее... Чтож, поменяйте код впихнув в нужное место ioctlsocket и докажите, что будет быстрее.
    Или, если не сможете, то хотя бы приведите кусок кода, где вызов oictlsocket(.., FIONREAD,..) будет просто необходим. Буду вам крайне признателен, потому как, у меня в голове не укладывается зачем вам знать размер доступных данных до байта, до того как они получены. Или вы данные по одному байту пересылаете? При большом объеме пересылаемых данных, вы будете каждый раз выделять новые 4-15 кб (в среднем один rcv) ???
    Вы не можете знать сколько данных еще должно придти (если только не юзаете прикладной протокол). Вы можете знать, лишь сколько данных можете получить за один recv. А это - в большинстве случаев - совершенно бесполезная инфрмация.
    Не тупите. Сброшу данные на диск (или куда они там пришли) и заюзаю уже выделенную.
    Не всегда опыт приходит с возрастом. Иногда возраст приходит один. (с)
     
  10. max7C4

    max7C4 New Member

    Публикаций:
    0
    Регистрация:
    17 мар 2008
    Сообщения:
    1.203
    что-то я не увидел у Вас, что вы вообще реагируете на это.
    Даже не собираюсь. Прямо я так разогнался. Вот за Вашу благодарность я прямо пойду и себе куплю квартиру.
    Теперь минусы Вашего метода.
    У Вас 1, может даже я еще больше по мелочусь 0,25 тысячи соединений.
    На каждое (как вы говорите) мелочно выделяем мегабайт. Вспоминая математику
    0,25 шт * 1 Мб = 250 Мб (проверьте на калькуляторе, вдруг я ошибаюсь). Тогда при моих объемах Вашей реализации придется туго. 5к шт * 1 Мб = ~5 Гб (5000 Мб). Ой. А если синхронизация будет еще более тонкой и количество клиентов достигнет 10 000 шт, а если еще большей. памяти у меня точно не хватит.
    Теперь мое.
    при 5000 шт длина буфера на клиента достигает макс 100 Кб (может меньше, но не больше, это собственно 2 кадра буфера воспроизведения). Считаем. 5000 шт * 100 Кб = 500 000 Кб = ~500 Мб. Память вся уже выделена, ее лишь распределяет мой внутренний Heap Manager. Но речь уже не об этом, хотя на это уходит от 5 до 37 (один раз при переходе от конца к началу) команд. Хотя это приблизительный расчет и 500 Мб почти в 2,2 раза больше того, что на деле требуется и работает. Плюс. С увеличением количества соединений реальная цифра вырастет максимум на 10-15% только за счет внутренней информации.
    Скажите Ваша реализация быстрее. Вот только гоняете вы память гораздо больше.
    В любом случае я больше не собираюсь с Вами спорить. Как знаете.
    [add]
    P.S. Ваша проблема в не ведение! Вы не знаете и Вы счастливы. От того, что вы выбросили лишний системный вызав - поздравляю, вы ничего не выиграли. У меня же вообще сейчас если подумать используется 2 системных вызова. ioctlsocket и recv. (тсс. хотя я подумываю и от них отказаться.) Хотя это не выход. И еще. Самое последнее. Написать и отладить систему для больших цифр - дело не легкое (Ваш код там умрет сам или убьет систему). У Вас тоже это может получиться, но научитесь считать (хотя бы приблизительно), тогда научитесь мерить возможности и соразмеривать желания.
    Иногда они еще приводя с собой и мудрость. (с)
    [/add]
     
  11. Aspire

    Aspire New Member

    Публикаций:
    0
    Регистрация:
    19 май 2007
    Сообщения:
    1.028
    max7C4
    Я те про Фому, а ты мне про Ерёму... Закрыто.
     
  12. max7C4

    max7C4 New Member

    Публикаций:
    0
    Регистрация:
    17 мар 2008
    Сообщения:
    1.203
    Ну кто про что. Но мы тут об одном. Вы совершенно не умеете считать, но прицепились к какому-то системному вызову, пометив его черной меткой не вызову ни в коем случае. Учитесь считать правильно! Прикиньте, сколько таких вот циклов вам понадобится, для больше чем одного соединения. А памяти, а ресурсов процессора и канала передачи данных. То-то же. Не сомневайтесь. Лишний системный вызов не повредит, если он необходим или полезен. Вы полагаетесь на возьмем побольше, все равно кредит безпроцентный. Да отдавать Вам столькоже придется, вот только тут Вам не банк. И не демократия. Компьютер - это капитализм. От каждой программы по возможностям, каждой программе требуемые ресурсы. Чем больше Ваши потребности - тем строже меряют Ваши возможности. Не стоит про это забывать. Взяли - так используйте максимально полезно. Не умеете - не беритесь. Обращусь опять к математике. Вам прислали байт (не стоит ржать, и такое случается). Вы выделяете мегабайт. Чувствуете разницу. Давайте повторим, если вы еще не врубились. Вам прислали 1048577 байт и вы выделяете 2097142 байта. Еще не поняли. У меня же контролируется вплоть до байта весь процесс и система работает максимально эффективно.
     
  13. Aspire

    Aspire New Member

    Публикаций:
    0
    Регистрация:
    19 май 2007
    Сообщения:
    1.028
    max7C4 У вас пока что все на словах. Приведите кусок кода, чтобы не быть голословным. Там, где вы вызываете ioctlsocket и выделяете память. Мой код вы покритиковали, дайте и другим раскритиковать ваш.
     
  14. max7C4

    max7C4 New Member

    Публикаций:
    0
    Регистрация:
    17 мар 2008
    Сообщения:
    1.203
    Ну хорошо.
    Код (Text):
    1. lodsd
    2. mov ebx, eax
    3. push 0
    4. push esp
    5. push FIONREAD
    6. push dword [ebx+socket]
    7. call [ioctlsocket]
    8. pop ecx
    9. add ecx, 8
    10. test eax, eax
    11. jnz error
    12. jecxz exit
    13. ;=====================================================================
    14. ;это упрощенный вариант того что творится, но он вполне сгодится. сразу замечу, что работа с памятью циклична. длина цикла один оборот или 2 сек (т.е. на буфер надо 220-260 Мб для гигабитного канала, если предполагается выжать из него все)
    15. mov edx, [ebp+heap]
    16. add [ebp+heap], ecx
    17. mov eax, [ebp+heap_end]
    18. cmp eax, [ebp+heap]
    19. jae @f
    20. mov edx, [ebp+heap_srt]
    21. lea eax, [edx+ecx]
    22. mov [ebp+heap], eax
    23. @@:
    24. ;=====================================================================
    25. sub ecx, 8
    26. mov [edx], ecx
    27. add edx, 4
    28. push ecx
    29. push edx
    30. push 0
    31. push ecx
    32. push edx
    33. push dword [ebx+socket]
    34. call [recv]
    35. pop edx
    36. pop ecx
    37. cmp ecx, eax
    38. jnz error
    39. lea eax, [edx-4]
    40. add edx, ecx
    41. mov ecx, [ebx+buffer]
    42. mov [ebx+buffer], edx
    43. mov [ecx], eax
    44. or [ebx+flags], 1
    этот кусочек кода можно повторять достаточно быстро и не только для одного сокета
    структуры не привожу. считай что они есть[add]ebp - структура подпрограммы, ebx - структура клиента. Подпрограмму можно вызывать как по таймеру, так и просто в отдельном потоке. Главное успевать обрабатывать данные клиентов.[/add]
     
  15. Aspire

    Aspire New Member

    Публикаций:
    0
    Регистрация:
    19 май 2007
    Сообщения:
    1.028
    Я тут маленько откомментировал, в попытках что-нибудь понять http://www.everfall.com/paste/id.php?b7daeyg1m5iq
    Редактирование разрешено, поэтому буду признателен, если вы меня поправите.
    Конкретно, я не понял что происходит, когда доступные данные вылезают за размер буфера? Вы довыделите имеющийся буфер или выделите новый?
    Если же вы уже выделили буфер фиксированного размера и не собираетесь его превышать (именно так я это понял) сбрасывая данные на диск или куда они у вас пришли, то почему бы не передавать ф-ции recv размер всего буфера (он же все равно один на клиента) расчитывая возможность дозаписи как в моем примере? Проявляя чрезмерную заботу о количестве свободных байт (именно байт, а не килобайт!), вы существенно проигрываете в скорости.
    Впрочем, это - пустой спор. Можете дальше юзать ненужный код. Я разрешаю.
     
  16. max7C4

    max7C4 New Member

    Публикаций:
    0
    Регистрация:
    17 мар 2008
    Сообщения:
    1.203
    А вот тут Вы не правы. Буфер один на всех клиентов и за распределение его частей отвечает мой код. Не собираюсь его приводить. Это закрытая часть этой дискуссии. Но объем буфера равный 200 Мб используется редко (только при пиковой загрузке и в зависимости от параметров системы). Кольцевая модель работы с памятью. Это просто очередь на два кадра. Все что успевает набраться обрабатывается. То сколько успевает набраться - зависит от системы. А не по 700 Мб на клиента. И в отличие от вашего кода операции с памятью проводятся в несколько раз быстрее. Конечно там нету кода учета выравнивания (его я не приводил), да набранное лишь частично демонстрирует алгоритм работы с памятью, но вполне отражает суть происходящего. К сожалению на си это будет очень громоздко. Что касается сети, то если Вы посмотрите на первую команду (которую кстати Вам не прокомментировали), то ее суть я прокомментирую сам. Берем структуру следующего клиента. Кстати в конце процедуры нет также еще проверки на выход за границы "массива" (вернее списка) клиентов, который я Вам решил представить, как линейный массив, чтобы не травмировать Вашу не окрепшую психику. Даже при выполнение всех описанных выше поправок, код, который Вы получите будет работать в несколько раз быстрее, чем Ваше издевательство над компьютером на ЯВУ! Ах да. Заголовок Вам немного не правильно трактовали. В начале идет длина - это правильно, затем полученные данные, это Вы возможно поняли, а в конце указатель на буфер, куда будет помещаться продолжение. Не знаете как организуются динамические списки?
     
  17. Aspire

    Aspire New Member

    Публикаций:
    0
    Регистрация:
    19 май 2007
    Сообщения:
    1.028
    Отчего же? Просто, из Вашего кода это совсем неочевидно.
     
  18. max7C4

    max7C4 New Member

    Публикаций:
    0
    Регистрация:
    17 мар 2008
    Сообщения:
    1.203
    http://www.everfall.com/paste/id.php?b7daeyg1m5iq
    я исправил то, что Вам не удалось понять. Ненавижу писать комментарии.