Форум программистов, компьютерный форум CyberForum.ru

Server Socket и прием данных - C++

Восстановить пароль Регистрация
 
Рейтинг: Рейтинг темы: голосов - 1, средняя оценка - 5.00
Zerorc
1 / 1 / 0
Регистрация: 14.03.2014
Сообщений: 44
08.04.2014, 11:45     Server Socket и прием данных #1
Доброго времени суток.
Никак не могу разобраться с компонентом ServerSocket. Итак, ситуация. У меня есть модуль XBee WiFi, на него с платы подается сигнал на вход по UART, модуль передает данные мне на компьютер, модуль использует TCP/IP протоколы, чтобы доставка пакета была гарантированна. Сервер работает асинхронно (stNonBlocking).
Так вот, проблема номер раз. Если модуль выходит из зоны покрытия, а потом снова появляется в ней, к примеру я дерну питание или вырублю вайфай на компе, выбивает асинхронную ошибку 10053. От этого избавиться можно, ладно. Беда в другом.
Расшарил сеть сниффером, вот какая ситуация. Я так понимаю что соединение с другой стороны, со стороны модуля, открыто и тот продолжает забрасывать данные в никуда, набивая свой буфер (IP протокол ведь TCP) но когда связь налаживается, вижу что от компьютера приходит ответ, мол пакет полчуен, ОК, но ServerSocket упрямо отказывается набивать мой массив, продолжая слушать. У кого есть какие идеи на этот счет? Пробовал отключать соединение по таймеру со своей стороны если данные не приходят, скажем 5 секунд, но тогда и вовсе соединение не восстанавливается, connect не происходит на моем уровне. Хотя по снифферу показывает что с компьютера приходит ответ о получении пакета. Есть ли какие-то методы подхватить существующую попытку соединения с другой стороны и все таки вытаскивать данные из сокета, а не молчать пока информация поступает. Откуда я знаю что сервер не желает работать? Нет, сокет-то открыт, но мой вектор не набивается новыми элементами. Вот такие дела. Пробовал пробежаться по ивентам OnAccept и OnConnect, но они тоже не срабатывают при такой ситуации.
Проблема номер два: Я набиваю данными свой вектор таким вот нехитрым образом.
C++
1
2
3
4
5
6
7
c = Form1->ServerSocket1->Socket->Connections[0]->ReceiveLength(); //узнать длину пришедшего пакета
        Form1->ServerSocket1->Socket->Connections[0]->ReceiveBuf(buf,c);  //копирую в буфер пакет
                for(int i =0;i<c;i++)   //забиваю пакет в вектор поэлементно
        dataAll.push_back(buf[i]);
 
        Contime = 0;  //переменная таймера соединения
        Form1->Edit2->Text = r+=c;   //счетчик байтов
Проблема в обработке этих данных. Если пакеты приходят четко по моему протоколу:
2 1 1 2 2 2 2 = 30 байт
Двухбайтовый заголовок Длина пакета Адрес Счетчик 10 каналов Дискреты Checksum = 17 элементов

То все ок, обрабатываю данные легко:
C++
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
for(unsigned i=0;i<dataAll.size();)
        {
 
                if(dataAll[i] == 85 && dataAll[i+1] == 170) { //dataAll обьявлен как unsigned char, то есть данные в ней побайтово
                dataSort.push_back(dataAll[i]);   //dataSort вектор обьявлен как unsigned short
                dataSort.push_back(dataAll[i+1]);
                dataSort.push_back(dataAll[i+2]);
                dataSort.push_back(dataAll[i+3]);  i+=4;     }
                        else                              {
                                a=dataAll[i+1];        //счетчик, каналы и дискреты
                                a<<=8;                  //двухбайтовые значения
                                a+=dataAll[i];         //ничего лучше не придумал, чем сдвиг в переменной.
                                dataSort.push_back(a);  i +=2  }
 
         }
Но вот не в лабораторных условиях мой вектор будет забит искаженными пакетами, огрызками данных и прочей лабудой. Что-то не дойду до того как написать универсальный алгоритм, чтобы отшелушить заголовок, длину, адрес, чексумму. Оставить только каналы и дискреты.
Есть идея создать еще один вектор, копию того куда набиваются данные и с ним работать, резать там его и прочее, циклически отсеивать по пакету, по нахождению заголовка, отмеряя по длине и сверяя чексумму, обрезать пакет, копировать в новый вектор и удалять обработанные пакетики, уже сохраненные удобночитаемо. Подскажите метод такой работы с вектором. И буду благодарен если у кого-то есть лучшее решение обработки двухбайтовых значений.

Проблема номер три: Механизм сохранения у меня реализован не лучшим образом.
C++
1
2
3
4
 if (SaveDialog1->Execute()){
    std::ofstream output_file(SaveDialog1->FileName.c_str());
    std::ostream_iterator<int> output_iterator(output_file,"\n");
    std::copy(dataAll.begin(), dataAll.end(), output_iterator);
Подскажите как мне, к примеру, отмерять 30 байт и только потом переходить на следующую строку? Чтобы было видно что это вроде бы как пакет. Или еще лучше искать контрольную сумму, к примеру число 0x1010 (2байта) и после него делать "\n"?
Механизм считывания и занесения в буфер у меня вообще не реализован. Буду благодарен и за эту помощь.
Вопросов много, но что поделать..)
Similar
Эксперт
41792 / 34177 / 6122
Регистрация: 12.04.2006
Сообщений: 57,940
08.04.2014, 11:45     Server Socket и прием данных
Посмотрите здесь:

C++ Прием врача
C++ ip address socket windows
Прием по modbus. Как указать число считываемых байтов C++
UDP отправка и прием пакетов C++
C++ Прием MPI_Bcast
После регистрации реклама в сообщениях будет скрыта и будут доступны все возможности форума.
Ilot
Модератор
Эксперт С++
1765 / 1140 / 221
Регистрация: 16.05.2013
Сообщений: 3,017
Записей в блоге: 5
Завершенные тесты: 1
08.04.2014, 12:44     Server Socket и прием данных #2
Если я правильно понял вопрос №3:
C++
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
short mask = 0x1010;
//Это ваш контейнер
vector<short> base;
...
vector<short>::iterator iter_lo = base.begin();
vector<short>::iterator iter_up = base.begin();
vector<short>::iterator end  = base.end();
 
std::ofstream output_file(SaveDialog1->FileName.c_str());
while(iter ! = end) {
    iter_up = find(iter_up, contener.end(), mask);
    std::copy(iter_lo, iter_up, std::ostream_iterator<short>(output_file, ""));
    output_file << std::endl;
    iter_lo = iter_up;
}
stima
429 / 284 / 16
Регистрация: 22.03.2011
Сообщений: 923
Завершенные тесты: 1
08.04.2014, 12:48     Server Socket и прием данных #3
1. Не совсем понял, о чем идет речь, но если TCP был разорван одной из сторон, он будет разорван и противоположной. Смею предположить, то что Вы видете в снифере это не пакеты протокола TCP, а думаю пакеты протокола ICMP. Какие Ваши действия?, Вам SocketServer должен в таком случае выйти из обработчиков и вернуть соответствующие ошибки connection closed by peer/broken pipe (чтение/запись).
2. Во первых передавайте сразу указатель на начало буффера который Вы резерврирете вектором.
C++
1
2
3
4
5
6
7
8
std::vector<char> buff;
....
 
length = Form1->ServerSocket1->Socket->Connections[0]->ReceiveLength(); //узнать длину пришедшего пакета
buff.clear();
buff.resize(length, 0);
 
Form1->ServerSocket1->Socket->Connections[0]->ReceiveBuf(&buff[0], lenght);
Вы избежите лишнего копирования.
Во вторых Так как это потоковая передача данных, то Вы должны следить за тем, что Ваш пакет не будет разбит или сконкатенирован я с другим.
В третьих Вы должны следить за очисткой буфер/или использованием правильного размера, так как необязательно
C++
1
dataAll.size() != pkt.size
3. Пишите побайтно
http://www.cplusplus.com/reference/o...ostream/write/
Zerorc
1 / 1 / 0
Регистрация: 14.03.2014
Сообщений: 44
08.04.2014, 14:47  [ТС]     Server Socket и прием данных #4
Привет, Ilot.
Отличная идея искать в векторе по указателям. Я так понял сначала оба указателя это начало контейнера. Потом верхним указателем мы ищем 0х1010. Если нашли, то копируем область от верхнего указателя до нижнего, который на начале и нижним догоняем верхний. Делаем перенос строки и по новой на всю длину контейнера. Под себя пока не подстроил, да и что-то не получается, виснет, но главное принцип понял и попробую, спасибо.

Привет, stima.
По первому вопросу, я различаю пакеты ICMP, это именно TCP, но тут другая загадка, если я выключаю сервер и снова включаю с интервалом, к примеру, 2 секунды, то соединение держится, при условии что данные к примеру бегут с интервалом 100 мс ~ 300 Bps. Если же скорость прихода каждого пакета 1 миллисекунда ~ 30KBps, то вообще все соединение при разрыве только с моей стороны закрывается практически мгновенно. Возможно это связано с переполнением буфера устройства передачи. По крайней мере я так предполагаю, ибо тесты сами за себя говорят. Но вы правы в том, что соединение закрывается с обеих сторон. Было для меня тоже новостью.
По поводу второго, данные бегут быстро, скорость приема каждого пакета 1 миллисекунда, ресайзить, очищать и инициализировать.. Это не слишком долго? Или все таки 30 циклов дольше отрабатывают? К тому же если я хочу набить буфер, пакетов очень много, указатель должен перемещаться. Это еще инициализация указателя вместо &buff[0]? Я же правильно понял метод? Если не затруднит, напишите ваш вариант с указателем, чтобы вектор заполнялся а не обновлялся после каждого пакета, чтобы до меня дошло. Или можно использовать копирование векторов, сделав еще один, а этот используя и как вектор и как буфер? Запутался в общем. Про пакеты понял, но бывает что может прийти пакет и длинной в 2 байте, а после остатки с набитыми огрызками пакетов, к примеру 1444 байта. И в догонку пару байт. Эту фишку TCP имеете ввиду, по поводу разбиения на мои пакеты?
Побайтно что-то не разобрался. Можно на примере не копирования документов а из вектора в файл?
stima
429 / 284 / 16
Регистрация: 22.03.2011
Сообщений: 923
Завершенные тесты: 1
08.04.2014, 15:13     Server Socket и прием данных #5
По поводу std::vector<char>,
1. насколько я понял, у вас ассинхронный, но не паралельный вызов обработчика, а это значит, что вы можете использовать один вектор, как общий буффер без дополнительных ограничений.
2. Если Вам реально важна скорость, Вы можете зарезервировать максимум возможной памяти, т.е. грубо говоря выделить буффер сразу при старте программы.
3. Так или иначе, как я понял по коду, Вы склеиваете пакеты в один общий, а это значит, что ресайзете вектор. Короче говоря не замарачивайтесь, по поводу ресайза/очистки, во первых потому что Вы не теряете там много, во вторых потому что предвременная оптимизация чревата, сделайте так, чтобы работал потом оптимизируйте.
4. Вектор, как и массив представляет собой линейную память &buff[N] говорит взять указатель на N-ый элемент в данной памяти, т.е. если Вы введете дополнительный маркер смещения то можете дополнять один и тотже вектор, просто брав разный указатель.
5. Если вы знаете размер пакета, и вектор состоит из пакетов, то можно писать побайтно:
C++
1
2
3
4
int marker = 0;
file.write(&buff[marker], pkt_size);
//можно дописать делиметр/перенос строки
marker += pkt_size;
Грубо говоря простая итерация только инкриментируете вы на размер пакета.
6. TCP является потоковым протоколом по своей сути, и поэтому да. возможно, но маловероятно, Вам прийдет 1 байт, а потом (pkt_size - 1) байт. Вы должны это обработать.
Zerorc
1 / 1 / 0
Регистрация: 14.03.2014
Сообщений: 44
08.04.2014, 16:21  [ТС]     Server Socket и прием данных #6
stima,
Что-то намудрил на приеме, не выходит. Весь контейнер забит нулями, хотя, думаю, смысл я уловил, но все равно не получается. Вот что происходит OnClientRead:
C++
1
2
3
        length = Form1->ServerSocket1->Socket->Connections[0]->ReceiveLength();
        lengthAll+=length; Form1->Edit2->Text = lengthAll;
        Form1->ServerSocket1->Socket->Connections[0]->ReceiveBuf(&dataAll[lengthAll],length);
Вроде все верно. Указатель наращивает значение и вектор должен заполняться.

По поводу сохранения, выскакивает ошибка [C++ Error] Unit1.cpp(324): E2108 Improper use of typedef 'file'
C++
1
2
3
for(unsigned i=0;i<dataAll.size();) {
file.write(&dataAll[i], 30);
i += 30;                     }
Еще была идея оформить вектор поэлементно, чтобы каждый элемент был указателем на начало целого пакета, но тогда не очень-то понимаю как к ним обращаться. И к тому же, если нужно будет заглянуть в биты дискретов, это тройная операция, отыскать указатель, по нему начало пакета, сдвинуться на номер дискрета и глянуть его бит. Выходит не слишком.
stima
429 / 284 / 16
Регистрация: 22.03.2011
Сообщений: 923
Завершенные тесты: 1
08.04.2014, 16:30     Server Socket и прием данных #7
Семен Семеныч, будте внимательны.
1. Вы сначала смещаете маркер, потом пишите. А должны наоборот.
2. А у вас есть такая пременная как file? Или она всетаки по другому назывется например output_file?))

Добавлено через 5 минут
Цитата Сообщение от Zerorc Посмотреть сообщение
Еще была идея оформить вектор поэлементно, чтобы каждый элемент был указателем на начало целого пакета, но тогда не очень-то понимаю как к ним обращаться. И к тому же, если нужно будет заглянуть в биты дискретов, это тройная операция, отыскать указатель, по нему начало пакета, сдвинуться на номер дискрета и глянуть его бит. Выходит не слишком.
Не правильная математика, если Вы знаете номер пакета, то такая операция константна (O(1)). Иначе, в любом случае и при любом контейнере, эта операция линейна + константа (O(N) + M)

п.с. Вам просто не привычно итерироватся не на ++ или --, вот например вы же не задумываетесь что ++ для int и ++для char разные?, а задумайтесь ведь sizeof(int) != sizeof(char)
Zerorc
1 / 1 / 0
Регистрация: 14.03.2014
Сообщений: 44
08.04.2014, 16:51  [ТС]     Server Socket и прием данных #8
Ага, точно. Теперь работает.. Хм, правда асинхрон выбивает на приеме, над этим еще поработаю. И по идее это будет проблема модуля, установить соединение после входа в зону точки доступа, ведь сервер и так будет слушать сокет.
Сдается мне что тут дело в разыменовывании, по поводу int и char. Ну, теперь хоть знаю как допилить программу.
Zerorc
1 / 1 / 0
Регистрация: 14.03.2014
Сообщений: 44
10.04.2014, 12:37  [ТС]     Server Socket и прием данных #9
Снова вопрос к спецам, что-то я запарился чутка.
В общем приходят мне данные, все отлично, вот только теперь нужно посчитать количество выбитых пакетов. Пока без контрольной суммы, просто меряя по счетчику, то есть по 5-6 байту, значение которых я загоняю в одну двухбайтовую переменную. Подача little endian'ом.
К примеру есть у меня десять пакетов:
ЗаголовокЗаголовокДлинаАдресСчетчикСчетчик..10каналов..ДискретДискретСуммаСумма
155AA19FF0100.............
255AA19FF0200.............
355AA19FF0300.............
455AA19FF0400.............
555AA19FF7032.............
655AA19FF0500.............
755AA19FF0600.............
855AA19FF22FF.............
955AA19FF0900.............
1055AA19FF1000.............
То есть график конкретно скачет, мне нужно посчитать эти скачки и просуммировать в счетчик выбитых пакетов.
stima
429 / 284 / 16
Регистрация: 22.03.2011
Сообщений: 923
Завершенные тесты: 1
10.04.2014, 13:04     Server Socket и прием данных #10
Как понять выбитых пакетов?
Zerorc
1 / 1 / 0
Регистрация: 14.03.2014
Сообщений: 44
10.04.2014, 15:37  [ТС]     Server Socket и прием данных #11
В данный момент, без контрольной суммы, просто те, байты счетчика в которых искажены в непонятное число. Если за ним идет нормальное значение.

Добавлено через 2 часа 24 минуты
И еще что-то не получается набить вектор из файла. Сохраняю построчно.
C++
1
2
3
4
 if (SaveDialog1->Execute()){
    std::ofstream output_file(SaveDialog1->FileName.c_str());
    std::ostream_iterator<int> output_iterator(output_file,"\n");
    std::copy(dataAll.begin(), dataAll.end(), output_iterator);  }
А вот считывать не хочет. Надо в unsigned char
C++
1
2
3
4
5
6
7
8
9
10
11
if (OpenDialog1->Execute()){
        ifstream ifs;
        string a;
        ifs.open(OpenDialog1->FileName.c_str());
   while(ifs)
   {
      getline(ifs,a);
      if(""==a)break;
      dataAll.push_back(a.c_str());
   }
        }
std::vector <unsigned char> dataAll; посоветуйте как лучше записать. Хорошо было бы загнать в этот же вектор dataAll
stima
429 / 284 / 16
Регистрация: 22.03.2011
Сообщений: 923
Завершенные тесты: 1
10.04.2014, 17:19     Server Socket и прием данных #12
Цитата Сообщение от Zerorc Посмотреть сообщение
В данный момент, без контрольной суммы, просто те, байты счетчика в которых искажены в непонятное число. Если за ним идет нормальное значение.
Во первых не понятно что за протокол вы используете как нагрузку, во вторых TCP гарантирует доставку и поэтому не понятно как может быть что-то искажено при передаче.

Цитата Сообщение от Zerorc Посмотреть сообщение
while(ifs)
* *{
* * * getline(ifs,a);
* * * if(""==a)break;
* * * dataAll.push_back(a.c_str());
* *}
Это не правильно! Не гуглиьть если не занаете как http://bit.ly/1meCMhR.

C++
1
dataAll.push_back(a.c_str());
Это вообще самоубийство, и говорит о том что Вы не знаете как работать со строкой, а также путаетесь что такое указатель и значение.
MoreAnswers
Эксперт
37091 / 29110 / 5898
Регистрация: 17.06.2006
Сообщений: 43,301
11.04.2014, 10:54     Server Socket и прием данных
Еще ссылки по теме:

Прием, передача и сравнение последовательности Баркера C++
Socket в консоли под Win на g++ C++
C++ C++ socket как работать с https?

Искать еще темы с ответами

Или воспользуйтесь поиском по форуму:
Zerorc
1 / 1 / 0
Регистрация: 14.03.2014
Сообщений: 44
11.04.2014, 10:54  [ТС]     Server Socket и прием данных #13
Быть может поэтому я и прошу мне помочь? Все с чего-то начинали =)
C++
1
2
3
4
5
6
7
8
9
if (OpenDialog1->Execute()){
      ifstream myReadFile;
 myReadFile.open(OpenDialog1->FileName.c_str());
  char output[10000];
 if (myReadFile.is_open()) {
 while (!myReadFile.eof()) {
myReadFile >> output;}
}
}
Как отсюда перебросить в вектор?

Добавлено через 1 час 39 минут
Сам разобрался.
C++
1
2
3
4
5
6
if (OpenDialog1->Execute()){
        string tmp ;
    vector < string > a;
    ifstream in(OpenDialog1->FileName.c_str());
    while (in >> tmp)
        a.push_back(tmp);
Но на повестке дня все еще вопрос с тем как одновременно вывести данные на график и принимать. Я предполагаю потоки с разными приоритетами. Какие еще могут быть варианты?
Потому что данные принимаются довольно быстро, а нужно еще обработать всю эту приемную свалку в пакеты, высчитав контр сумму, посмотреть что в каналах и дискретах и вывести на график. К тому же делать это оперативно, по приходу данных.
Так вот, может каждую 200 точку выводить а уже при загрузке графика все полностью на Chart выкидывать? Как лучше будет?
Yandex
Объявления
11.04.2014, 10:54     Server Socket и прием данных
Ответ Создать тему
Опции темы

Текущее время: 01:55. Часовой пояс GMT +3.
КиберФорум - форум программистов, компьютерный форум, программирование
Powered by vBulletin® Version 3.8.9
Copyright ©2000 - 2016, vBulletin Solutions, Inc.
Рейтинг@Mail.ru