ПрограммированиеФорумСеть

Winsock2, С++, не могу получить адрес отправителя

Страницы: 1 2 3 4 5 Следующая »
#0
9:36, 12 мая 2018

Добрый день!
Все делаю по рецепту: https://msdn.microsoft.com/en-us/library/windows/desktop/ms738545(v=vs.85).aspx
ТСР-протокол.
Создал клиента, создал сервер. Байты отправляются и принимаются.
Решил использовать для приема функцию recvfrom, с ее помощью (вроде как) можно определить адрес и порт отправителя.
На сервере после коннекта клиента получаю байты теперь так:

                sockaddr_in client_addr;//структура для сохранения порта и адреса
    int client_addr_size = sizeof(client_addr);//и ее длина
    cout <<client_addr_size<< endl;//тут я проверил, задался ли размер: выводит всегда 16 байтов
    count_bytes = recvfrom(ClientSocket, recvbuf, recvbuflen, 0, (sockaddr *)&client_addr, &client_addr_size);//а было recv(ClientSocket, recvbuf, recvbuflen, 0);
    
                //тут я пытаюсь получить из адреса читабельную строку
    char str[INET_ADDRSTRLEN];    
    if (NULL != inet_ntop(client_addr.sin_family, &(client_addr.sin_addr), str, INET_ADDRSTRLEN))
    {
      printf("%s\n", str);
    }
    else//даже если произошла ошибка, я все равно пробую посмотреть
    {
      printf("%s\n", str);
      cout << "error ip" << endl;
    }
    cout<< client_addr.sin_port << endl;//ну и порт посмотреть

Похоже, что структура sockaddr_in client_addr содержит мусор, т.к. если поменять

 inet_ntop(client_addr.sin_family, &(client_addr.sin_addr), str, INET_ADDRSTRLEN)

на

 inet_ntop(AF_INET.sin_family, &(client_addr.sin_addr), str, INET_ADDRSTRLEN)

(у меня все используют IPv4)
то printf выдаст IP 0.0.0.0 и порт 0, даже если я запускаю сервер на другой машине.
Не использую inet_ntoa, т.к. она считается устаревшей и VS ее просто не пропускает.
Полный код сервера:

+ Показать

Код клиента:

+ Показать

В общем, что я делаю не так, и как определить адрес и порт клиента?

#1
12:00, 12 мая 2018

Ramm
> recvfrom
Оно ж, вроде, для UDP?

#2
12:28, 12 мая 2018

Ramm
> и как определить адрес и порт клиента?
собрался банить по IP? : )

#3
12:35, 12 мая 2018

ArchiDevil
> Оно ж, вроде, для UDP?
А как тогда получить адрес для TCP?
Sh.Tac.
> собрался банить по IP? : )
Ну ведь адрес клиента как-то надо получить...

#4
12:43, 12 мая 2018

getpeername
https://msdn.microsoft.com/en-us/library/windows/desktop/ms738533(v=vs.85).aspx
Для получения адреса сокета клиента ТСР...
Т.е. вот этот код показывает адрес:

                char str[INET_ADDRSTRLEN];
    getpeername(ClientSocket, (sockaddr *)&client_addr, &client_addr_size);
    if (NULL != inet_ntop(client_addr.sin_family, &(client_addr.sin_addr), str, INET_ADDRSTRLEN))
    {
      printf("%s\n", str);
    }
    else
    {
      cout << "error ip" << endl;
    }
    cout<< client_addr.sin_port << endl;

А recvfrom в случае с ТСР тупо игнорирует последние 2 параметра.
Так вроде?)

#5
14:07, 12 мая 2018

Адрес клиента берется на приеме входящего коннекта и храниться если нужен. Вместо recvfrom используеться recv.
А еще предпочтительней не шаманить и новый код писать на готовой библиотеке типа poco или boost.asio во избежание собирания всех возможных грабель.

#6
14:51, 12 мая 2018

ShadowTeolog
> во избежание собирания всех возможных грабель
Например, каких? Зато они все известны, на многие вопросы есть ответы (за эти 20 лет существования winsock), да и народу работающих с сокетами очень много.
Ну и выглядит все не очень сложно: создай сокет и пускай пакеты по адресам=)

#7
14:59, 12 мая 2018

Ramm
> Зато они все известны, на многие вопросы есть ответы (за эти 20 лет
> существования winsock), да и народу работающих с сокетами очень много.
> Ну и выглядит все не очень сложно: создай сокет и пускай пакеты по адресам=)
В общем, за год-полтора разберешься, завалив по дороге пару проектов ;)
Это не шутка, в самом деле так.
Сложность в том, что вся необходимая информация размазана тонким слоем повсюду, ее так просто не соберешь. Очень много тонкостей и не все они в API. Так что, даже использование готовых библиотек не факт что спасет. Может местами облегчить освоение, а может и наоборот, к заморочкам сети добавить заморочки созданные библиотекой.

#8
15:54, 12 мая 2018

Zab
> В общем, за год-полтора разберешься, завалив по дороге пару проектов ;)
Замотивировал...=)
Вопрос, который покажется отдаленным от передачи данных... Но...
Функция send принимает в качестве пакета массив char-ов.
В C# есть тип byte, и удобный конвертер из любого типа... В С++ даже byte-ов нет, эта функция возложена на byte. Как конвертнуть double-int-float в байты (char-ы, получается), т.е. любой int или float должны занять всего 4 char-а (они же в С++ по 4 байта)?

#9
16:01, 12 мая 2018

Ramm
> Как конвертнуть double-int-float в байты (char-ы, получается), т.е. любой int или float должны занять всего 4 char-а (они же в С++ по 4 байта)?
union тебе на что? Или же просто двоичным копированием. Или прямым преобразованием типов (она хочет char*, но ты можешь скормить ей все что угодно). В C++ у тебя полная власть над памятью, делай что хочешь, но и за последствия тоже отвечаешь только ты.
Учитывай что числа на разных платформах имеют разное представление. В чисто двоичном аппаратно зависимом виде их гонять по сети опасно, надо специфицировать формат. Т.е. можно гонять, но тот, у кого формат другой обязан будет их конвертировать у себя.

#10
16:16, 12 мая 2018

Zab
Я на шарпе делал так: я конвертировал в цикле нужные мне дробные, целые и прочие символы в массивы байтов, создавал один массив длиной с сумму длин этих, а потом отправлял его. А на той стороне в том же порядке конвертировал обратно.
Тут я хочу то же самое. union - немного не то, слишком много неопределенностей и ограничений.

  int i = 345;
  char c2[4];
  memcpy(&c2, &i, sizeof(int));
  cout << c2 << endl;

  int a;
  memcpy(&a, c2, sizeof(int));
  cout << a << endl;

Типа такого...
Вот только cout << c2 << endl выдает какую-то фигню, типа кракозябр. Ну да, блин, он думает там символы лежат. А как байты увидеть?=)

  for (int z=0;z<4;z++)
  cout << (int)c2[z] << endl;

Вроде этого?

Вообще, так можно делать? А потом по сети передавать?

А еще можно так, вроде:

  int a2;
  memcpy_s(&a2, sizeof(int), c2, 4);
  cout << a2 << endl;

И я так понимаю, что мне не нужно думать за уничтожение и освобождение памяти. Справится сборщик мусора, я же память не выделял...

#11
16:47, 12 мая 2018

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

Насторожила фраза про "пакеты". Осознаешь, что TCP - совсем не пакетный протокол, он потоковый? Прийти тебе может нарезанным совсем не теми кусками, как ты посылал. Это не UDP.

#12
17:02, 12 мая 2018

Zab
> Если у тебя такие заморочки чисто с базовыми понятиями языка
Вполне возможно, что что-то позабыл, бывает... Да и что я не так написал? Я, вроде, сам на свои вопросы ответил, я только спросил, правильно ли я рассуждаю и в том ли направлении иду...
Zab
> Осознаешь, что TCP - совсем не пакетный протокол, он потоковый.
> Прийти тебе может нарезанным совсем не теми кусками, как ты посылал. Это не
> UDP.
Если я послал "abcdefghijkl" за раз, за один send, а потом считываю на сервере, то у меня не может "abc" лежать на сокете, а остальная часть придти потом.
В контексте ТСР я подразумевал под "пакетом" одну отправку.
Если я отправил "abcdefghijkl" многократно, то я должен следить за тем, как считывать, т.е. сам нарезать и следить где начинаются и заканчиваются сообщения в "abcdefghijklabcdefghijkl"
> Прийти тебе может нарезанным
нарезанными мне ничего не может прийти.

#13
18:17, 12 мая 2018

Ramm
> Вполне возможно, что что-то позабыл, бывает... Да и что я не так написал?
Представление чисел, способ напечатать их побайтово, если ты этого не знаешь, скорее всего ты вообще очень мало знаешь про С++. Он не осваивается интуитивно, надо знать как что в нем работает, а не догадываться. Ты ж не изолирован от нижнего уровня, все порушить - раз плюнуть, и даже не заметить этого сразу. Никакой аналогии с C#, тот гораздо более высокоуровневый.
Учить языку через форум - занятие дохлое. Не перепечатывать же сюда многие сотни страниц из учебника...
Пара фрагментов, которые ты тут привел - как раз способ все порушить. Так нельзя с С++ обращаться, даже в виде эксперимента. Отсюда я и сделал вывод, что ты языка совсем не знаешь.

TCP гарантирует только что байты придут в той же последовательности, в которой ты их отправлял. Какими группами они придут - как получится. Группу могут как разрезать по пути, так и слить несколько воедино.

#14
19:29, 12 мая 2018

Ramm
> И я так понимаю, что мне не нужно думать за уничтожение и освобождение памяти.
> Справится сборщик мусора, я же память не выделял...
В C++ нет сборщика мусора.
Ramm
> Если я послал "abcdefghijkl" за раз, за один send, а потом считываю на сервере,
> то у меня не может "abc" лежать на сокете, а остальная часть придти потом.
Вообще-то, может. Операционная система может по своему усмотрению разрезать и переклеивать пакеты так, как ей вздумается. Например, если ты передашь ОС три посылки: "012345", "6789" и "abcdef", она может перепаковать их в два пакета "012345abc" и "def", просто потому чтобы сэкономить на IP-пакетах. Причём, часть "def" отправится не сразу - ОС ещё немного подождёт, чтобы, если твой код передаст ещё байты, их можно было дописать в тот же пакет, а не конструировать для этого новый.
Если соединение проходит через какой-нибудь прокси, он может перестроить и буферизировать ещё раз, по-своему.
Таким образом, при работе по сети, на места разрезов в TCP-пакетах полагаться нельзя.
То, что нарезки сходятся в твоём конкретном случае - это оптимизация для твоего конкретного случая, когда ОС видит, что оба конца туннеля на одном компьютере, соответственно, весь обмен идёт вообще напрямую через оперативную память в обход сетевых интерфейсов - в этом конкретном случае переклейки принесут больше вреда, чем пользы, поэтому в этом конкретном случае каждая отправка с клиента становится сразу видна целиком на сервере. Но, как я уже несколько раз повторил - это только в твоём конкретном случае, и то без гарантии, что другая версия видноус не сделает по-другому и после очередного обновления твоя программа не сломается.
Ramm
> printf("%s\n", str);
> cout << "error ip" << endl;
Что мешает написать cout << str << endl;? Альтернативно, что мешает написать printf("error ip\n");? Определись, каким из методов ты пользуешься, и не перемешивай их.
> cout <<client_addr_size<< endl;
> cout<< client_addr.sin_port << endl;
Код с как попало расставленными пробелами смотрится ещё хуже, чем код без пробелов. Если у тебя никак не хватает внимательности расставить пробелы одинаково - воспользуйся функцией автоформатирования в своём редакторе.
А вообще, C++ - довольно строгий язык, и к неоплошностям относится непростительно, поэтому лучше всё же научиться поддерживать в коде порядок и делать вещи аккуратно.
Ramm
> union - немного не то, слишком много неопределенностей и ограничений
Неопределённости и ограничения - лишь в твоём воображении, стоит лишь изучить язык - и union становится верным и гибким инструментом.

void writefloat(std::vector<char>& buffer, float value)
{
  union
  {
    float asfloat;
    char asbytes[4];
  } u;
  u.asfloat = value;
  buffer.insert(
    buffer.end(),
    u.asbytes, u.asbytes + 4);
}

Ramm
> memcpy(&c2, &i, sizeof(int));
Если нужна копия в массив, лучше вместо &c2 записать просто c2. Здесь, по счастливому стечению обстоятельств, оба варианта делают одно и то же, тогда как в общем случае, memcpy(arr, ...) запишет в содержание массива, тогда как memcpy(&arr, ...) запишет в указатель arr, вместо адреса содержания массива, байты по второму аргументу. Причём, если третий аргумент будет больше, чем sizeof(arr) - С++ накажет тебя за неоплошность и сломает программу в каком-нибудь неожиданном месте.
Ramm
> Вот только cout << c2 << endl выдает какую-то фигню, типа кракозябр. Ну да,
> блин, он думает там символы лежат. А как байты увидеть?=)
Одна из фишек С++ - это перегрузка операторов. Это означает, что на каждое имя функции и оператора можно привязать множество вариантов, и то, какой именно из вариантов будет выбран в каждом конкретном случае, зависит от того, с аргументами какого типа вызван этот оператор.
cout << c2 - это то же самое, что и явный вызов operator<<(cout, c2).
Учитывая типы переменных cout и c2, среди множества перегрузок operator<<, будет выбран std::ostream& operator<<(std::ostream&, char const*), который записывает в поток си-строку, переданную вторым аргументом. Притом, так как c2 си-строкой не является, - С++ может наказать за неоплошность и сломать программу.
Если нужно, чтобы в поток было записано текстовое представление числа, нужно выбрать перегрузку, которая принимает число, и передать ей значение байта отдельно. Это можно сделать, например, взяв (int)c2[0]; тогда компилятор выберет std::ostream& operator<<(std::ostream&, int), другую функцию с другим поведением - перевести число в текст, и записать этот текст в поток.

Ну и возвращаясь к сборщику мусора, которого нет. В С++, как правило, объекты жёстко привязаны к переменным - объект создаётся в момент объявления переменной, и уничтожается немедленно в момент выхода переменной из зоны видимости. Например:

void writefloat_test(std::vector<char>& buffer, float f)
// buffer и f созданы в момент вызова функции
{
  union
  {
    float asfloat;
    char asbytes[4];
  } u; // u создан в начале функции
  u.asfloat = value;
  for(int i = 0; i < 4; ++i) // i создан сразу перед первой итерацией
  {
    int asbyte = u.asbytes[i]; // asbyte создан в начале каждой итерации
    std::cout << asbyte << std::endl;
    // asbyte уничтожен в конце каждой итерации
  }
  // i уничтожен сразу после последней итерации
  buffer.insert(
    buffer.end(),
    u.asbytes, u.asbytes + 4);
  // u уничтожен сразу перед выходом из функции
  // buffer, f уничтожены сразу перед выходом из функции
}

Однако, ни в коем случае не следует воспринимать операции создания и уничтожения обектов как что-то, что расходует время - в подавляющем большинстве случаев, компилятор самостоятельно способен увидеть, что одно и то же место в памяти используется в течение длительного времени разными объектами, и сливает их в одит слот памяти. Напоминает о том, как ОС сливает несколько отправок в один пакет.

Страницы: 1 2 3 4 5 Следующая »
ПрограммированиеФорумСеть

Тема в архиве.