В результате ее работы в массиве array оказывается пакет размером BUF_SIZE. Есть ли какое-то стандартное решение при работе с этой функцией, чтобы постоянно принимать пакеты, да еще не все подряд а, отфильтрованные по значению в одном из элементов массива. Я пытался вот такой подход использовать:
do
result = recvfrom(iSocket, array, BUF_SIZE, 0, (struct sockaddr *)&sipx, (socklen_t*)&len_addr) ;
while ( array[8] == 42 ) ;// принимать пакет пока в array не окажется то, что нужно
Устраивало, до тех пор, пока не потребовалось еще одному процессу читать через этот сокет.
Как ты себе представляешь работу с одним сокетом и толпой клиентов без потоков/процессов?
Единственный вариант: заставлять клиентов ждать, пока обслуживаешь очередного. Если у тебя время обслуги — минут 10, а клиентов тысяча, то это будет жесть!
Если у тебя время обслуги — минут 10, а клиентов тысяча, то это будет жесть!
В таких случаях обычно «обслуга» отправляется в отдельный пул потоков на обработку, а клиенты потом вовремя получают статус выполнения (опять же, можно асинхронно)
Нет, я не могу понять, как можно более чем с одним клиентом работать на одном потоке!
Вот, допустим, я открываю сокет. Его одновременно открывают три клиента (вообще, это один клиент, но открываем с разными ключами: один канал изображения гонит, другой принимает и обрабатывает управляющие команды, третий передает клиенту сообщения об ошибках).
Ну и как я в этот сокет что-нибудь напишу, чтобы оно именно нужному клиенту пришло? Хранить пул дескрипторов? А теперь представь, что у тебя одновременно толпа народу может к этому сокету обратиться. В итоге у тебя получится бешеный пул дескрипторов и ты в реальном времени вынужден будешь выдирать из пула те, в которые видео писать, писать туда видео; выдирать нужные командные и обрабатывать полученное оттуда...
В общем, геморройное же дело! Проще по потоку завести, и пусть они себе висят и обрабатывают все.
А в многонитевом приложении ты как клиенты различаешь? Пойми ты, многонитевое приложение и конечный автомат для обработки нескольких соединений это одно и то же, просто когда есть нити, тебе этого автомата не видно, его ядро и/или библиотека поддержки нитей скрывают.
#include<stdio.h>#include<vector>#include<signal.h>#include<zCommon/zException.hpp>#include<zCommon/zEnviroment.hpp>#include<zCommon/zUtils.hpp>#include<zIO/zMainLoop.hpp>#include<zIO/zIOStreamNormal.hpp>#include<zIO/_zSocketServer.hpp>#include<zIO/zUnixSocketHelper.hpp>#include"MyServer.hpp"
using namespace zCommon;
using namespace zIO;
using namespace std;
intmain(int argc,char** argv)
{
zEnviroment::Init(); //Инициализация некоторых сущностей, ничего интересного
zMainLoop loop; //Основной цикл обработки сообщений
MyServer* server = MyServer::Create(1234); //Создаем обработчик сервера
loop.Handler_Add(server); //Добавляем обработчик в цикл обработки сообщений
loop.Run(); //Запускаем цикл обработки сообщений, в недрах этого метода будет вызываться epoll()return0;
}
#pragma once#include<zIO/_zTCPSocketServer.hpp>/*
* Класс описывет сервер
*/classMyServer : public zIO::_zTCPSocketServer
{
public:
voidserver_Open(); //Метод вызывается при создании сервераvoidserver_Close(); //Метод вызывается при закрытии сервера/*
* Этот метод вызывается каждый раз, когда кто-то устанавливает соединение.
* Метод должен вернуть обработчик отдельного соединения
*/
zIO::_zTCPSocketServer::local_Session* server_CreateSession(struct sockaddr_in& addr);
voidserver_Think();
static MyServer* Create(int port);
};
#pragma once#include<zIO/_zTCPSocketServer.hpp>#include<zCommon/zTime.hpp>/*
* Класс отдельного соединения
*/classMyServerSession : public zIO::_zTCPSocketServerSess
{
private:
zCommon::zTime t0; //Метка времени последней активности
public:
virtual voidstream_BeginStream(); //Вызывается, когда соединение установлено и готово для обмена
virtual voidstream_EndStream(); //Вызывается, когда соединение разорвалось
virtual voidstream_Recv(); //Вызывается каждый раз, когда приходят какие-нибудь данные
virtual voidstream_Think(); //Вызывается каждые 100 милисекунд, нужно для таймаутов
virtual voidstream_IOError(int error_source,int error_num); //Вызывается при сбоях
private:
voidSendFrame(char r); //В этом методе мы послаем ответный фрейм с XOR-кодом
};
#include"MyServerSession.hpp"#include<zCommon/zCursor.hpp>#include<iostream>
using namespace std;
using namespace zCommon;
using namespace zIO;
voidMyServerSession::stream_BeginStream()
{
t0.SetNow();
cout << "session: begin stream" << endl;
}
voidMyServerSession::stream_EndStream()
{
cout << "session: end stream" << endl;
}
voidMyServerSession::stream_Recv()
{
while(true) //Зачем нужен цикл? Теоретически, мы можем получить два фрейма за одно пробуждение
{
/*
* inq - это циклический буффер, содержащий принятые байты (да, ещё одна обертка)
* Если данных в буфере меньше трех байт, то выходим и ждем получения
* остальных байт
*/if(inq.GetDataSize()<3)
break;
uint8_t sig=0;
uint8_t func=0;
uint8_t frame_size=0;
char data[256];
/*
* Данные можно доставать прямиком из циклического буфера inq,
* но мы будем использовать курсор буфера.
* Курсор полезен, когда для того чтобы понять, пришел ли весь фрейм
* надо достать часть данных из буфера. Если фрейм полностью пока ещё
* не пришел, то операция отменяется и из циклического буфера как бы и ничего
* и не доставали.
* В данном случае нас интересует байт frame_size с размером. Без него мы не
* узнаем каких размеров весь фрейм
*/
zCursor c(&inq);
c.Pop_UI8(sig);
c.Pop_UI8(func);
c.Pop_UI8(frame_size);
/*
* Проверяем на ошибки
*/if(sig!=0xAF)
{
cout << "Invalid frame " << endl;
this->CloseForced();
return;
}
if(func!=1)
{
cout << "Unknown function " << (int)func << endl;
this->CloseForced();
return;
}
if(frame_size==0)
{
cout << "Invalid frame " << endl;
this->CloseForced();
return;
}
c.Pop_CharArray(data,frame_size); //Достаем последние данные
c.ApplyChanges(); //Достали весь кадр, теперь фиксируем изменения в циклическом буфереchar r = data[0];
for(int i=1;i<frame_size;i++)
r^=data[i]; //Считаем XOR
SendFrame(r); //Отсылаем фрейм назад
t0.SetNow(); //устанавливаем метку времени последней активности
}
}
voidMyServerSession::stream_Think()
{
zTime now;
now.SetNow();
if(now.isElapsed(t0,10000))
{
this->CloseForced(); //Десять секунд прошло, никакой активности не было, закрываемreturn;
}
}
voidMyServerSession::stream_IOError(int error_source,int error_num)
{
cout << "session: io error " << error_num << endl;
}
voidMyServerSession::SendFrame(char r)
{
uint8_t sig=0xAF;
uint8_t func=2;
uint8_t frame_size=1;
Write_Begin();
outq.Push_UI8(sig);
outq.Push_UI8(func);
outq.Push_UI8(frame_size);
outq.Push_CharArray(&r,1);
Write_End();
}
Кто договаривался? Я ни слова про Си не сказал. Пиши хоть на брейнфаке, главное чтобы было компактнее, понятнее, красивее и при этом была напрямую работа с системным вызовом select()/poll()/epoll() без всяких архитектурных абстракций, прослоек, оберток.
Что-нибудь на коленке сварганю, чтобы обслуживать N клиентов в N потоков. Скажем, тупое эхо.
Неее, код должен отражать всю ту логику, что в задании приведена. Указана структура фрейма и логика работы сервера. Ну и ещё про 10 секундный таймаут не забудь.
Правильно, epoll() скрыт в недрах класса zMainLoop. В том то и смысл оберток, что они скрывают реализацию. Если бы ты видел epoll(), то это была бы уже прямая работа с системным вызовом. Такая задача стоит у тебя.
Я хотел показать, как много можно скрыть за обертками. И это очень сильно облегчает труд программиста.
Мы тут отделяем общую часть, и специфичную для конкретного сервера. Общая часть не привязана ни к какой конкретной задаче.
Написали один раз такую библиотек-обертку или взяли готовую типа Boost::Asio, а потом пишем только специфичный код для конкретной задачи. Удобно, ты концентрируешься только на прикладной логике, а не захламляешь текст программы кусками кода, которые повторяются (примерно), от одного сервера к другому.
Весь приведенный код, это ровно то, что мне бы понадобилось для решения указанной задачи. Указанные классы не эфемерны, они взяты из реальной библиотеки, которую я написал и использую для таких задач.
Все украдено до нас! Очень напоминает обсуждение проблемы обслуживания 10000 клиентов. Вот классическая статья http://www.kegel.com/c10k.html. Там рассмотрены разные стратегии решения. (кстати статье уже 15 лет).
Там есть ссылки на примеры реализации и на С и на С++ и на Java
Мне надо ни мистический с10к, ни какой либо другой, а надо именно его код. Тем более с10к протух лет 10назад, ибо сейчас о чем-то кроме с500к активных соединений даже говорить не стоит.
Можно мне увидеть эти самый наработки? Можно мне рабочий код?
Не, я не хочу выкладывать. Там слишком много всего. Код явно не рассчитан на обучение и выкладывание и я считаю этот код своей интеллектуальной собственностью.
Если тема интересна, я может накатаю упрощенный вариант библиотеки, где все будет проще и понятнее и оформлю это в виде отдельного треда. Но обещать не могу. Я ленивый.
Сомневаюсь, что есть хоть какой-то шанс, что твоей код меня может чему-то научить.
я считаю этот код своей интеллектуальной собственностью
Что из этого следует?
Если тема интересна, я может накатаю упрощенный вариант библиотеки, где все будет проще и понятнее и оформлю это в виде отдельного треда. Но обещать не могу. Я ленивый.
Ты мне хотябы wс -l выкати своих портянок.
Т.е. берёшь свою «интеллектуальную» собственность и считаешь в ней кол-во строк. Выкатываешь результат. Всё просто. Или это тоже «„интеллектуальную“ собственность»?