Форум программистов, компьютерный форум, киберфорум
Наши страницы
C++ и сети
Войти
Регистрация
Восстановить пароль
 
ALEXDOC
0 / 0 / 0
Регистрация: 29.01.2013
Сообщений: 44
Завершенные тесты: 1
#1

Искажение данных при передаче через TCP соединение. Winsock - C++

01.01.2017, 16:56. Просмотров 678. Ответов 19
Метки нет (Все метки)

Здравствуйте, у меня клиент-сервер приложение, и при передаче относительно большого числа, возникает его искажение. Хотя при передаче пакетов большого размера такой проблемы не наблюдалось.

Отправка:
C++
1
2
3
4
int size = 339 355;
 
if ( send(Socket,(char*)&size, sizeof(int),0) == -1 )
    return -1;
Получение:
C++
1
2
3
4
int size = 0;
    
if (recv(cl->getSocket(), (char*)&size, sizeof(int), 0) == -1) 
    return -1;
При получении получал либо очень огромное число(типо такого 607114558), либо какое-то отрицательное.
При передаче небольших чисел(4, 10 ...) работает правильно.

p.s. Отправка происходит на клиенте, получение на сервере.
http://www.cyberforum.ru/cpp-networks/thread8997.html
0
Надоела реклама? Зарегистрируйтесь и она исчезнет полностью.
Similar
Эксперт
41792 / 34177 / 6122
Регистрация: 12.04.2006
Сообщений: 57,940
01.01.2017, 16:56
Я подобрал для вас темы с готовыми решениями и ответами на вопрос Искажение данных при передаче через TCP соединение. Winsock (C++):

Границы сообщения при передаче через TCP
Есть клиент/сервер, общаются через TCP. Логика следующая: клиент отправляет...

Написать соединение: много клиентов - сервер (Winsock; на сервере 3 сокета: 2 TCP и 1 UDP)
Всем здрасьте !!!! Мне надо написать соединение: много клиентов - сервер ...

Исходники TCP (Winsock) чата на C++ с GUI
Мне сложно разбираться в "туториалах", ведь намного легче когда есть исходный...

TCP-эхо клиент-сервер (WinSock)
Здравствуйте! Подскажите, пожалуйста, почему TCP-эхо клиент-сервер неправильно...

Winsock(соединение с сервером)
Тестирую свою приложение. Появилась проблема, а именно невозможность...

19
Hikari
Хитрая блондиночка $)
1451 / 960 / 399
Регистрация: 21.12.2015
Сообщений: 3,785
01.01.2017, 17:13 #2
Цитата Сообщение от ALEXDOC Посмотреть сообщение
либо очень огромное число(типо такого 607114558), либо какое-то отрицательное
Т.е. еще и не стабильно одно и то же значение?
Сколько раз у тебя в коде recv() вызывается?
Отладку производил?
0
ALEXDOC
0 / 0 / 0
Регистрация: 29.01.2013
Сообщений: 44
Завершенные тесты: 1
01.01.2017, 17:15  [ТС] #3
Не стабильно. Ну минимум два раза для одного клиента.
0
SEGNET
67 / 67 / 11
Регистрация: 28.12.2012
Сообщений: 471
02.01.2017, 10:39 #4
Не проще ли буферы передать в send() и recv() ?
0
ALEXDOC
0 / 0 / 0
Регистрация: 29.01.2013
Сообщений: 44
Завершенные тесты: 1
02.01.2017, 11:00  [ТС] #5
А можно более конкретно. Вы предлагаете int переменную превратить в массив байт и передать ?
0
Hikari
Хитрая блондиночка $)
1451 / 960 / 399
Регистрация: 21.12.2015
Сообщений: 3,785
02.01.2017, 11:21 #6
Цитата Сообщение от SEGNET Посмотреть сообщение
Не проще ли буферы передать в send() и recv() ?
А в коде именно так и делается. Лишние контейнеры для данных тут ни к чему.
Цитата Сообщение от ALEXDOC Посмотреть сообщение
Не стабильно. Ну минимум два раза для одного клиента.
Т.е. в остальных случаях все таки число передается без потерь?
Значит надо делать ставку на искоренение болячки склейки/расклейки передаваемых пакетов.
Я не зря спрашивала сколько раз и как у тебя вызывается прием и передача, возможно ты не совсем то что надо передаешь.
0
ALEXDOC
0 / 0 / 0
Регистрация: 29.01.2013
Сообщений: 44
Завершенные тесты: 1
02.01.2017, 11:24  [ТС] #7
Что мне надо сделать ? На что обратить внимание ?
0
SEGNET
67 / 67 / 11
Регистрация: 28.12.2012
Сообщений: 471
02.01.2017, 12:25 #8
Предположу что происходит усечение значения так как происходит преобразование из int в char*. Char в свою очередь одно байтовое число.
0
Убежденный
Ушел с форума
Эксперт С++
15941 / 7252 / 1176
Регистрация: 02.05.2013
Сообщений: 11,637
Записей в блоге: 1
Завершенные тесты: 1
02.01.2017, 12:52 #9
Цитата Сообщение от ALEXDOC Посмотреть сообщение
Что мне надо сделать ?
Убедись, для начала, что recv возвращает sizeof (int). А она может вернуть меньше.
И покажи код, где ты выводишь принятое число.
1
ALEXDOC
0 / 0 / 0
Регистрация: 29.01.2013
Сообщений: 44
Завершенные тесты: 1
02.01.2017, 12:58  [ТС] #10
Она точно возвращает sizeof(int), это я проверил первым. А число я не вывожу, проверял его в режиме отладке с брейкпоинтами.

Добавлено через 57 секунд
C++
1
std::cout << (int)size;
0
Убежденный
Ушел с форума
Эксперт С++
15941 / 7252 / 1176
Регистрация: 02.05.2013
Сообщений: 11,637
Записей в блоге: 1
Завершенные тесты: 1
02.01.2017, 13:00 #11
Цитата Сообщение от ALEXDOC Посмотреть сообщение
Она точно возвращает sizeof(int), это я проверил первым
Это нужно проверять всегда, а не эпизодически.

Цитата Сообщение от ALEXDOC Посмотреть сообщение
А число я не вывожу, проверял его в режиме отладке с брейкпоинтами.
Плохо. Потому что отладчик может и "соврать".
1
ALEXDOC
0 / 0 / 0
Регистрация: 29.01.2013
Сообщений: 44
Завершенные тесты: 1
02.01.2017, 13:03  [ТС] #12
Я учту Ваши замечания, спасибо. При выводе и при отладке выдаётся одно и тоде число.
0
Убежденный
Ушел с форума
Эксперт С++
15941 / 7252 / 1176
Регистрация: 02.05.2013
Сообщений: 11,637
Записей в блоге: 1
Завершенные тесты: 1
02.01.2017, 13:06 #13
Покажи исправленную версию кода (со всеми проверками и выводом числа).
И еще вопрос: до send что-нибудь в сокет отправлялось?
Может быть, в recv просто приходят данные, которые были отправлены ранее?
0
Evg
Эксперт CАвтор FAQ
18937 / 6898 / 512
Регистрация: 30.03.2009
Сообщений: 19,432
Записей в блоге: 30
02.01.2017, 13:09 #14
Цитата Сообщение от Убежденный Посмотреть сообщение
Убедись, для начала, что recv возвращает sizeof (int). А она может вернуть меньше
Для send'а тоже надо бы проверять
0
Убежденный
Ушел с форума
Эксперт С++
15941 / 7252 / 1176
Регистрация: 02.05.2013
Сообщений: 11,637
Записей в блоге: 1
Завершенные тесты: 1
02.01.2017, 13:10 #15
Цитата Сообщение от Evg Посмотреть сообщение
Для send'а тоже надо бы проверять
Да, совершенно верно.
0
Evg
Эксперт CАвтор FAQ
18937 / 6898 / 512
Регистрация: 30.03.2009
Сообщений: 19,432
Записей в блоге: 30
02.01.2017, 13:12 #16
Для порядку передаваемое и принимаемое число пиши в 16-ричном виде. И передавай числа вида 0x11223344. Уже по виду получаемого числа можно будет понять, что за беда происходит.

Ну и чисто идиотский вопрос: на машинах, на которых исполняется клиент и сервер, на обеих размер int'а равен 4?
1
Hikari
Хитрая блондиночка $)
1451 / 960 / 399
Регистрация: 21.12.2015
Сообщений: 3,785
02.01.2017, 14:06 #17
Цитата Сообщение от Убежденный Посмотреть сообщение
Может быть, в recv просто приходят данные, которые были отправлены ранее?
Вот и я на это ставку делаю
0
gng
691 / 537 / 159
Регистрация: 08.09.2013
Сообщений: 1,439
02.01.2017, 16:58 #18
Цитата Сообщение от Evg Посмотреть сообщение
Ну и чисто идиотский вопрос: на машинах, на которых исполняется клиент и сервер, на обеих размер int'а равен 4?
В данном контексте это совсем не идиотский вопрос. Еще стоит спросить, порядок байт в числе на передающей и принимающей платформе одинаков или нет.
После этих вопросов ТС, возможно, поймёт, что числа в сетевых программах таким образом не стоит передавать.
0
Evg
Эксперт CАвтор FAQ
18937 / 6898 / 512
Регистрация: 30.03.2009
Сообщений: 19,432
Записей в блоге: 30
02.01.2017, 17:04 #19

Не по теме:

Цитата Сообщение от gng Посмотреть сообщение
В данном контексте это совсем не идиотский вопрос
Под "идиотскостью" я подразумевал, что ТС должен был сам это проверить, а я просто напомнил, сыграв роль КО. Хотя сейчас уже подозреваю, что ТС о таких вещах даже не задумывался



Цитата Сообщение от gng Посмотреть сообщение
Еще стоит спросить, порядок байт в числе на передающей и принимающей платформе одинаков или нет
С учётом того, что маленькие значения передаются правильно, скорее всего одинаков. Но в качестве матчасти ТС'у следовало бы подумать и об этой стороне вопроса
0
Орион
1 / 1 / 1
Регистрация: 28.04.2013
Сообщений: 35
06.01.2017, 18:53 #20
Нужно выделить область памяти и класть в неё пакет и уже с этой областью памяти работать.
C++
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
char *buf = new char[1440];
 
while (true)    
{
 
        RusultPacket = recv(Sock, (char *)buf, 1440, 0);
 
        if (RusultPacket <= 0)  {
            delete[] buf;
            NoConnection(Sock, Client);
        }
 
        Packet(buf, Sock);
}
 
 
delete[] buf;
Для правильной работы с буфером, который вы приняли или отправили, требуется использовать приведение типов, далее я буду называть это как "кастовать".

Нам нужно создать структуру, с которой мы будем работать, в вашем случае если требуется только int и больше ничего, то это:

C++
1
2
3
4
struct KernelPacket
{
   int number;
}
Собственно буфер нужно будет "кастовать" таким образом:
C++
1
KernelPacket *PacketKernel = (KernelPacket *)buf;
Теперь указатель PacketKernel , будет иметь поле number (PacketKernel->number). По сути когда вы обращаетесь к полю указателя, он обращается к области памяти buf по расчетам размеров переменных.

Так же стоит отметить, что лучше иметь одну главную структуру с буффером внутри:

C++
1
2
3
4
5
6
7
typedef char byte_t;
 
struct KernelPacket
{
   int32_t TypePacket;   // int32_t в <stdint.h>, ну или используйте enum и создайте другой тип
   byte_t  buffer[1440]; // 1440 насколько помню размер минимального пакета, который не режется на части в TCP (MSS)
}
А далее уже от кастования, используйте PacketKernel->buffer и кастуйте на любые другие структуры.
ах да, чуть не забыл, для того что бы собрать пакет, используйте memcpy. Прикреплю кусок кода ниже
C++
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
void strnull(char * p, char *b, unsigned sz)
{
    for (int i = 0; i < sz; i++)
        p[i] = b[i];
}
 
oriPacket *p_OriPacket = new oriPacket;
 
     strcpy(p_OriPacket->salt,  SALT);
     strcpy(p_OriPacket->buffer, msg);
 
     if (b_Type_Packet)
     strcpy(p_OriPacket->typecomand, Local_Type_Packet);
 
    KernelPacket *Packet = new KernelPacket;
    strcpy(Packet->salt_packet, KERNEL_SALT);
 
    Packet->type_packet_protocol = TYPE_STRUCT_PROTOCOL;
    strnull(Packet->buffer, (char*) p_OriPacket, sizeof(oriPacket));
 
    OriSendKernel(Packet);
  b_Type_Packet = false;
 
  delete p_OriPacket;
Добавлено через 11 секунд
Нужно выделить область памяти и класть в неё пакет и уже с этой областью памяти работать.
C++
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
char *buf = new char[1440];
 
while (true)    
{
 
        RusultPacket = recv(Sock, (char *)buf, 1440, 0);
 
        if (RusultPacket <= 0)  {
            delete[] buf;
            NoConnection(Sock, Client);
        }
 
        Packet(buf, Sock);
}
 
 
delete[] buf;
Для правильной работы с буфером, который вы приняли или отправили, требуется использовать приведение типов, далее я буду называть это как "кастовать".

Нам нужно создать структуру, с которой мы будем работать, в вашем случае если требуется только int и больше ничего, то это:

C++
1
2
3
4
struct KernelPacket
{
   int number;
}
Собственно буфер нужно будет "кастовать" таким образом:
C++
1
KernelPacket *PacketKernel = (KernelPacket *)buf;
Теперь указатель PacketKernel , будет иметь поле number (PacketKernel->number). По сути когда вы обращаетесь к полю указателя, он обращается к области памяти buf по расчетам размеров переменных.

Так же стоит отметить, что лучше иметь одну главную структуру с буффером внутри:

C++
1
2
3
4
5
6
7
typedef char byte_t;
 
struct KernelPacket
{
   int32_t TypePacket;   // int32_t в <stdint.h>, ну или используйте enum и создайте другой тип
   byte_t  buffer[1440]; // 1440 насколько помню размер минимального пакета, который не режется на части в TCP (MSS)
}
А далее уже от кастования, используйте PacketKernel->buffer и кастуйте на любые другие структуры.
ах да, чуть не забыл, для того что бы собрать пакет, используйте memcpy. Прикреплю кусок примерного кода ниже
C++
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
void strnull(char * p, char *b, unsigned sz)
{
    for (int i = 0; i < sz; i++)
        p[i] = b[i];
}
 
oriPacket *p_OriPacket = new oriPacket;
 
     strcpy(p_OriPacket->salt,  SALT);
     strcpy(p_OriPacket->buffer, msg);
 
     if (b_Type_Packet)
     strcpy(p_OriPacket->typecomand, Local_Type_Packet);
 
    KernelPacket *Packet = new KernelPacket;
    strcpy(Packet->salt_packet, KERNEL_SALT);
 
    Packet->type_packet_protocol = TYPE_STRUCT_PROTOCOL;
    strnull(Packet->buffer, (char*) p_OriPacket, sizeof(oriPacket));
 
    OriSendKernel(Packet);
  b_Type_Packet = false;
 
  delete p_OriPacket;
0
06.01.2017, 18:53
MoreAnswers
Эксперт
37091 / 29110 / 5898
Регистрация: 17.06.2006
Сообщений: 43,301
06.01.2017, 18:53
Привет! Вот еще темы с решениями:

Шифрование данных при передаче на сервер
Есть задача передать данные на сервер. Получать и отправлять данные будет...

Winsock проверка закрыто ли соединение
Как проверить закрыто ли соединение. Не хочу использовать recv, т.к. будет...

Передача пакета данных TCP через интернет
Доброе время суток!:) Я студент, пишу дипломную работу по передачи данных...

Использование не блокируемого WinSock TCP сокета. Реализации таймаута для ожидания connect()
Необходимо реализовать TCP подключение и обменяться данными. В случае не...


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

Или воспользуйтесь поиском по форуму:
20
Ответ Создать тему
Опции темы

КиберФорум - форум программистов, компьютерный форум, программирование
Powered by vBulletin® Version 3.8.9
Copyright ©2000 - 2018, vBulletin Solutions, Inc.
Рейтинг@Mail.ru