Версия для печати темы
Нажмите сюда для просмотра этой темы в оригинальном формате
Форум программистов > C/C++: Сети > Концепция любого класса для работы с winsock


Автор: En_t_end 15.10.2005, 17:55
Занимаюсь разработкой класса инкапсулирующего возможности winsocket'ов. Нужно добиться подобной абстракции:
Псевдокод:
Код

WPsocket wpsock(IP, PORT);
wpsock >> buffer;
wpsock << buffer;

Где >> и << операторы отправки и примема данных соотвественно.
То есть мне надо достичь потоковой гибкости и простоты.
Просто мысли разбегаются... вообще как примерно должен выглядеть такой класс ? Если можно, то в общих чертах.

Автор: Mad 15.10.2005, 18:05
En_t_end
В общих чертах так:
создаеш класс
перегружаеш << операторы для него, в которых делаеш send по длине параметра (для данных переменной длины надо добавить какойто разделитель
перегружаеш >> опрераторы в которых делаеш recv пока не будет данных нужной длинны

Автор: En_t_end 15.10.2005, 18:14
Mad
Цитата(Mad @ 15.10.2005, 22:05)
опрераторы в которых делаеш recv пока не будет данных нужной длинны

В смысле ?... это что перебор получается ? smile
Добавлено @ 18:17
Цитата(Mad @ 15.10.2005, 22:05)
перегружаеш >> опрераторы в которых делаеш recv пока не будет данных нужной длинны

В том-то и проблема, что я не знаю какие данные должны прийти. Вернее я то знаю, обьект класса не знает.
Есть мысля по этому поводу...
Код

wpsock.SetDataSize(HOW_LONG_DATA);
wpsock >> buffer;

То есть так можно будет скрыть от программиста цикл приема данных по частям.

Автор: Mad 15.10.2005, 18:20
Цитата(En_t_end @ 15.10.2005, 17:14)
это что перебор получается

нет, например
Код

public operator >> (int& val)
{
    char buff[4];
    recv(sock, buff, 4, 0);
    val = *(int*)buff;
}

также и для других типов данных(только надо еще добавить проверку саколько принято smile)

Автор: En_t_end 15.10.2005, 18:22
Mad
Есть ещё проблема с размерами сетевых пакетов для протокола TCP/IP.
Нужно каким-то образом точно разбить данные(скорее всего придется узнавать максимально возможный размер пакета, кстати как это можно сделать, как можно переносимей для винды) и отправить их по средством << настолько чисто, так чтобы для программиста, пользующегося обьектом, все это выглядело в одной операции.

Автор: Mad 15.10.2005, 18:30
Цитата(En_t_end @ 15.10.2005, 17:14)
В том-то и проблема, что я не знаю какие данные должны прийти. Вернее я то знаю, обьект класса не знает.

Если в приведенном мной коде добавить контроль поступления
Код

char buff[4];
int received = 0;
while(received < 4)
{
    int i = recv(sock, buff+received, 4-received, 0);
    received += i;
}

тогда у тебя оператор выйдет только после получения 4 байтов (т.е полно int как и нужно было smile)

Цитата(En_t_end @ 15.10.2005, 17:22)
Нужно каким-то образом точно разбить данные

а зачем ? TCP и так дает полный поток. Если в самом операторе ждать получения полных данных, то уже без разницы как там TCP этиданный разбила (тем более, что они могут быть дополнительно разбиты при пересылке роутером)

Автор: En_t_end 15.10.2005, 18:33
Mad
ОК... спасибо... будем работать
Добавлено @ 18:38
Цитата(Mad @ 15.10.2005, 22:30)
а зачем ? TCP и так дает полный поток.

Просто я сам был свидетелем работы одной программы сделанной для отмазки. Там наблюдался глюк с приемом и отправкой большого обьема данных. Получалось, что данные приходили либо не полностью, либо вовсе не приходили.
Мое беспокойство было вызванно именно по поводу отправки... система сама разбивает данные ?
ЗЫ просто сам я кроме больше чем 100 байт через TCP/IP не передовал.

Автор: bel_nikita 15.10.2005, 18:42
En_t_end
Что-то подобное реализовывал 2 года назад.
Можно сделать, что данные в буфер кладутся в течении 50 мсек, а потом отсылаются. И так же контроль буфера, т.е. если буфер полон - данные отсылаются.
Также можно ввести идентификаторы:
Код
 
class CBaseSock{
...
public:
  enum Ident{ START, FLUSH, SIZE, DATA, STOP };
...
  template<class T> CBaseSock& operator << (T tData)
  {
      if ( isChild(tData) )
     {
      ...
      // Если tData - тип Ident::SIZE, то значит в tData придет размер данных
     }
     ...
  }
...
};

Автор: Mad 15.10.2005, 18:52
Цитата(En_t_end @ 15.10.2005, 17:33)
Получалось, что данные приходили либо не полностью, либо вовсе не приходили.

При отправке (вызове send) возвращаеться сколько данных помещенно в буффер, с контролем получаеться так
Код

void SendData(char *buff, int len)
{
    int sended = 0;
    while(sended < len)
    {
        int i=send(sock, buff+sended, len-sended, 0);
        sended += i;
    }
}

так я максимум до 20Мб пересылал

Автор: En_t_end 16.10.2005, 08:33
Mad
Thks!
bel_nikita
Очень интересное предложение, попробую реализовать.

Автор: En_t_end 16.10.2005, 08:44
bel_nikita
Насколько я понимаю, если расширить ваш совет, то можно будет добиться следующей абстракции:
Код

wpsock << DATA_SIZE << buffer;


Автор: Mad 16.10.2005, 12:16
Цитата(En_t_end @ 16.10.2005, 07:44)
wpsock << DATA_SIZE << buffer;

Это уже зависит от того, какие именно данные ты собираешся пересылать
например, если у тебя по сокету идут только структуры фиксированной велечины, и в строгом порядке, то пересылка их размера просто увеличит обьем генерируемого трафика, ез какойто выгоды
а вот если ты будеш пересылать бинарные данные различной длины (напримерjpeg файлы smile) то без пересылки размера вообще не обойтись

TCP предоставляет только транспорт, протокол обменна клиент<->сервер(протокол высокого уровня) надо разрабатывать самому

Автор: bel_nikita 16.10.2005, 18:56
En_t_end
Цитата
Насколько я понимаю, если расширить ваш совет, то можно будет добиться следующей абстракции

Да, именно так.
Код

 wpsock << DATA_SIZE << LenghtBuffer << pBuffer; 

Хотя это уже разновидности реализации на усмотрение разработчика.
Вообще, хорошо бы разбить все на уровни: транспортный, пакетный и пользовательский. Транспортный(физический) только непостредственно прием/передача. Пакетный - создание пакетов для отсылки или распаковка при приеме. Пользовательский или сервисный уровень - это и есть перегрузка операторов << >>.
Так же хорошо(как и говорил выше) сделать отдельный тред на отсылку, или другими словами таймер. Как только первая порция(назавем так) данных поступипа - включается таймер и данные кладуться в буфер. Как только прошло допустим 50 мсек или в буфере нет уже места - данные отсылаются. Ну и конечно, предусмотреть идентификатор FLUSH - отсылка/очистка буфера, т.е. данные сразу отсылаются без таймера.
Ну и конечно, нужно знать для чего это все нужно? Если это файлы, то тут лучше использовать FTP. Если какие-то большие объемы информации, то обязательно нужно реализовывать пакетный уровень. Ведь в TCP первый высланный пакет может прийти последним smile

Автор: En_t_end 17.10.2005, 11:01
bel_nikita
Цитата(bel_nikita @ 16.10.2005, 22:56)
Ведь в TCP первый высланный пакет может прийти последним 

То есть надеется на последовательное заполнение буфера в цикле recv не стоит ?
Добавлено @ 11:10
Пока делаю так...
Создал класс родитель LSocket:
Код

class LSocket
{};

И два потомка:
Код

class LSockAccept : public LSocket
{};

class LSockJoin : public LSocket
{};

Теперь реализую в потомке Accept сокет переводящийся в режим ожидания подключения.
А для Join - организацую подключения со стороны клиента.

Подумываю организовать специальный протокол обмена. То есть обеспечить автоматическое выделение канала FTP для передачи файлов, если будет получен соотвествующий запрос по TCP.

Автор: Mad 17.10.2005, 12:58
Цитата(En_t_end @ 17.10.2005, 10:01)
Ведь в TCP первый высланный пакет может прийти последним

smilesmilesmile откуда такая инфа ? smilesmilesmile

Автор: En_t_end 17.10.2005, 13:39
Mad
Цитата(Mad @ 17.10.2005, 16:58)
откуда такая инфа ?

Опять начинается ? smile
Было же уже обсуждение этого вопроса smile

Автор: Mad 17.10.2005, 13:43
En_t_end
Цитата(En_t_end @ 17.10.2005, 12:39)
Было же уже обсуждение этого вопроса

скинь сылку посмотреть

Автор: En_t_end 17.10.2005, 13:52
http://forum.vingrad.ru/index.php?showtopic=57342

Автор: bel_nikita 17.10.2005, 14:03
Mad
Цитата
откуда такая инфа ?
Приведу лишь пару цитат:
Цитата
- В TCP нет видимой пользователю концепции «пакета». Он просто передает поток байтов, и нельзя точно предсказать, сколько байтов будет возвращено при очередном чтении.
- Из-за перегрузок в интенсивно используемой глобальной сети, особенно в Internet, данные могут доставляться как внезапно, так и пакетами неожиданного размера. Это требует от вас особой осторожности в допущениях о том, сколько данных может прийти в определенный момент и с какой частотой они поступают.


http://files.rsdn.ru/20918/tcpip.zip

Автор: Mad 17.10.2005, 14:14
bel_nikita
Цитата(bel_nikita @ 16.10.2005, 17:56)
Ведь в TCP первый высланный пакет может прийти последним

Цитата(bel_nikita @ 17.10.2005, 13:03)
В TCP нет видимой пользователю концепции «пакета».

И как по твоему эти две фразы стыкуютья ? smile

Цитата(bel_nikita @ 17.10.2005, 13:03)
нельзя точно предсказать, сколько байтов будет возвращено при очередном чтении

Это верно, поэтому я и привел примеры методов чтения нужного кол-ва байтов с ожиданием полного получения, но это не изменения порядка следования
Поясню

отправляем буффер (1,2,3,4,5,6}
при получении, могут быть такие варианты:
{1,2,3,4,5,6}
{1,2}{3,4,5}{6}
{1,2,3,4}{5,6} и т.д
но никогда не будет {1,2}{6}{3,4,5} или подобного

Автор: bel_nikita 17.10.2005, 14:52
Mad
Цитата
И как по твоему эти две фразы стыкуютья ?
Так и знал, что-то вроде этого напишут smile Стыкуются. TCP/IP - и есть стЫк протоколов. Так или иначе данные TCP передаются посредством IP пакетов. И когда говорим TCP - подразумеваем TCP/IP.
Ну, лопухнулся немного smile Даже стыдно стало smile первый высланный пакет может прийти последним - это касается IP уровня smile

Автор: Mad 21.10.2005, 14:06
Цитата(En_t_end @ 21.10.2005, 12:28)
    send(LSocket.sock, ltoa(SizeMess, buffer, 10), SizeMess, 0);

ты пытаешся послать SizeMess байт smile вместо длины получившегося размера

для такого протокола код будет такой :

Код

void sendlen(SOCKET sock, char *buff, unsigned long len)
{
    unsigned long sended = 0;
    while(sended < len)
        sended += send(sock, buff+sended, len-sended, 0);
}

void recvlen(SCKET sock, char *buff, unsigned long len)
{
    unsigned long recived = 0;
    while(recived < len)
        recived += recv(sock, buff+recived, len-recived, 0);
}

LSocket& operator <<(LSocket &LSocket,const char* message)
{
    unsigned long SizeMess = lstrlen(message);
    sendlen(LSocket.sock, &SizeMess, sizeof(unsigned long));
    sendlen(LSocket.sock, message, SizeMess);
    return LSocket;
}

LSocket& operator >>(LSocket &LSocket,char** buffer)
{
    unsigned long len;
    recvlen(LSocket.sock, &len, sizeof(unsigned long));
    *buffer = new char[len+1];
    memset(buffer, 0, len);
    recvlen(LSocket.sock, *buffer, len);
    return LSocket;
}

Автор: En_t_end 21.10.2005, 14:14
Mad
Цитата(Mad @ 21.10.2005, 18:06)
ты пытаешься послать SizeMess байт

А.... теперь понятно, то есть здесь надо учитывать, что раз SizeMess - u_long - значит надо и послать размер сообщения размером с sizeof u_long. А я пытался преобразовывать размер к char*, а затем уже отсылать размер, опять же ошибочно забывая о том, что каждый символ - не есть один байт.

Автор: Mad 21.10.2005, 14:32
Цитата(En_t_end @ 21.10.2005, 13:14)
А.... теперь понятно

Несовсем так, если ты посмотриш мой код, то увидиш
1. u_long парасылаеться как 4 байта в бинарном виде (нет смысла приобразовавыть его в текст)
2. по приему его клиент выделяет буффер по данные, с укзанным (в занчении u_long) размером
3. идет непосредственно пересылка данных.

в данном слечае совсем не важно что именно ты пересылаеш, текст или бинарные данные

кстати, в случае с текстом, можно упростить алгоритм:
отправитель - отсылает текстовый буфер вместе с завернающим 0x0 байтом
получатель - читает поток побайтно, пока не встретит байт 0x0 smile

Автор: En_t_end 22.10.2005, 10:30
Mad
Цитата(Mad @ 21.10.2005, 18:32)
отправитель - отсылает текстовый буфер вместе с завернающим 0x0 байтом
получатель - читает поток побайтно, пока не встретит байт 0x0

Я как не пробывал, не хочет отсылаться этот конечный байт smile Если видишь, то в я в своем классе даже добавил флаг, который отвечает за то, надо или нет добавлять в конец полученной строки нуль-терминатор.

ЗЫ спасибо! Вопросы у меня ещё есть, как сформулирую их получше - выложу.
Добавлено @ 10:37
Хотя нет... все таки можно и нужно отправлять нуль-терминатор. Просто нужно отправлять (lstrlen(message)+1)*sizeof(char) - столько байт

Powered by Invision Power Board (http://www.invisionboard.com)
© Invision Power Services (http://www.invisionpower.com)