Модераторы: feodorv

Поиск:

Ответ в темуСоздание новой темы Создание опроса
> Порядок байт и битовые операции. 
:(
    Опции темы
konshyn
Дата 6.8.2014, 16:29 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Опытный
**


Профиль
Группа: Участник
Сообщений: 295
Регистрация: 19.9.2013

Репутация: нет
Всего: нет



Ребят, нет возможности проверить (ни одной машины нет с нужным порядком байт)
У меня на машине little-endian порядок байт, сетевой порядок байт - big-endian.
Так вот, хочу работать с заголовками с помощью битовых операций. Пример:
Код

// принял пакет на raw сокет ( в buffer), т.е. заголовок ip и, к примеру, udp и полезную нагрузку.
// мне нужно получить, к примеру, порт отправителя и получателя в udp заголовке
// что я делаю:
port_src = (buffer[(buffer[0] & 0x0F) * 4] << 8) | buffer[(buffer[0] & 0x0F) * 4 + 1];

и я получаю именно тот порт, с которого была отправлена датаграмма. я так понимаю, с порядком big-endian на машине я получу такой же результат.
но вот загвоздка, если у меня little-endian, то мне нужно опять использовать htons(), чтобы записать этот порт, к примеру, в sockaddr_in, а если big-endian - не нужно.
Как сделать это элегентно, и с возможностью работать через битовые операции (намного удобнее, чем через всякие структуры и просто копирование. по крайне мере мне так нравится smile ) ?


--------------------
«Потому что ценность акта действия в этой стране возрастает в несколько раз».
PM MAIL Skype   Вверх
volatile
Дата 6.8.2014, 17:57 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
****


Профиль
Группа: Завсегдатай
Сообщений: 2107
Регистрация: 7.1.2011

Репутация: нет
Всего: 85



Цитата(konshyn @  6.8.2014,  16:29 Найти цитируемый пост)
нужно опять использовать htons(), чтобы записать этот порт, к примеру, в sockaddr_in, а если big-endian - не нужно.

Неверно. htons() следует использвать в любом случае.
На машине, где это нужно она перевернет порядок байт, а на машине,где не нужно - ничего делать не будет.
htons(), ntohs() и подобые, специально придуманы чтоби писать платформонезависимый код.
А возится с порядком байт вручную, не стоит.



Это сообщение отредактировал(а) volatile - 6.8.2014, 17:58
PM MAIL   Вверх
konshyn
Дата 6.8.2014, 18:19 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Опытный
**


Профиль
Группа: Участник
Сообщений: 295
Регистрация: 19.9.2013

Репутация: нет
Всего: нет



Цитата(volatile @  6.8.2014,  17:57 Найти цитируемый пост)
Неверно

Почему неверено? Верно, дальше по тексту вы сами объясняете, что будет smile
Цитата(volatile @  6.8.2014,  17:57 Найти цитируемый пост)
htons(), ntohs() и подобые, специально придуманы чтоби писать платформонезависимый код.

Верно, но, к примеру, когда я принимаю через recvfrom() датаграмму, т.е. я туда еще отправляю структуру sockaddr_in, чтобы мне ядро скинуло ip-адрес отправителя, то этот ip-адрес записан в big-endian порядке. Жаль, что порт туда не записывается. хотя, структура sockaddr_in специально для AF_INET сделана, другими словами для TCP и UDP. они имеют поля портов на одном и том же месте в своем заголовке - в начале. логично было бы делать это.
так вот, я бы хотел делать как писал выше, но для этого придется писать htons(), но, если я буду делать как делают все, то не нужно, т.к. они уже записаны в big-endian порядке.
Думал-думал, как элегантно сделать, но не придумал...придется как всегда:(
Код

struct udp_header
{
    u_int16 src_port;
    u_int16 dst_port;
    u_int16 length;
    u_int16 checksum;
};
...
struct sockaddr_in src_addr, dst_addr;
...
recvfrom(..., buffer, ..., (struct sockaddr *)&src_addr, (void*)&length);
struct udp_header *udp;
udp = (struct udp_header *)(buffer + (buffer[0]&0x0F) * 4);
...
dst_addr.sin_port = udp->src_port;
...
 



Это сообщение отредактировал(а) konshyn - 6.8.2014, 18:28


--------------------
«Потому что ценность акта действия в этой стране возрастает в несколько раз».
PM MAIL Skype   Вверх
feodorv
Дата 6.8.2014, 19:55 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
****


Профиль
Группа: Комодератор
Сообщений: 2214
Регистрация: 30.7.2011

Репутация: 10
Всего: 45



Цитата(konshyn @  6.8.2014,  19:19 Найти цитируемый пост)
Жаль, что порт туда не записывается.

Что Вы имеете в виду?
Цитата

struct sockaddr_in {
        short   sin_family;
        u_short sin_port;
        struct  in_addr sin_addr;
        char    sin_zero[8];
};

Всё записывается.

Добавлено через 2 минуты и 58 секунд
Код
... func( struct sockaddr_in *sa, ...)
{
  unsigned int ip = ntohl( sa->sin_addr.s_addr );
  unsigned int port = ntohs( sa->sin_port );
...
}



--------------------
Напильник, велосипед, грабли и костыли - основные инструменты программиста...
PM MAIL   Вверх
konshyn
Дата 6.8.2014, 22:19 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Опытный
**


Профиль
Группа: Участник
Сообщений: 295
Регистрация: 19.9.2013

Репутация: нет
Всего: нет



Цитата(feodorv @  6.8.2014,  19:55 Найти цитируемый пост)
Все записывается

Я имел в виду из функции recvfrom()


--------------------
«Потому что ценность акта действия в этой стране возрастает в несколько раз».
PM MAIL Skype   Вверх
feodorv
Дата 6.8.2014, 23:22 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
****


Профиль
Группа: Комодератор
Сообщений: 2214
Регистрация: 30.7.2011

Репутация: 10
Всего: 45



Цитата(konshyn @  6.8.2014,  23:19 Найти цитируемый пост)
Я имел в виду из функции recvfrom() 

И я имел в виду из функции recvfrom(). Приведённый код как раз выполняется после recvfrom [UDP].


--------------------
Напильник, велосипед, грабли и костыли - основные инструменты программиста...
PM MAIL   Вверх
konshyn
Дата 7.8.2014, 10:35 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Опытный
**


Профиль
Группа: Участник
Сообщений: 295
Регистрация: 19.9.2013

Репутация: нет
Всего: нет



Цитата(feodorv @  6.8.2014,  23:22 Найти цитируемый пост)
И я имел в виду из функции recvfrom(). Приведённый код как раз выполняется после recvfrom [UDP]. 

У меня почему-то на сырой сокет порт не записывается

Код

//recvfrom() на сыром сокете
printf("IP = %s\n", inet_ntop(AF_INET, (void*)&servaddr.sin_addr, ipaddr, sizeof(ipaddr)));
printf("Port = %d\n", ntohs(servaddr.sin_port));
...


Цитата

IP = 127.0.0.1
Port = 0


Добавлено через 1 минуту и 16 секунд
отправляю пакет вот так:
Цитата

echo "hello" > /dev/udp/localhost/58549



--------------------
«Потому что ценность акта действия в этой стране возрастает в несколько раз».
PM MAIL Skype   Вверх
tzirechnoy
Дата 7.8.2014, 13:15 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
***


Профиль
Группа: Завсегдатай
Сообщений: 1173
Регистрация: 30.1.2009

Репутация: 1
Всего: 16



http://commandcenter.blogspot.ru/2012_04_01_archive.html

Добавлено через 3 минуты и 15 секунд
Ровно обратным порядком: buffer[0] = post&0x0F; buffer[1] = (port >> 8)&0x0F;

Впрочем, htons можно просто вставлять всегда.
PM MAIL   Вверх
konshyn
Дата 7.8.2014, 14:06 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Опытный
**


Профиль
Группа: Участник
Сообщений: 295
Регистрация: 19.9.2013

Репутация: нет
Всего: нет



Цитата(tzirechnoy @  7.8.2014,  13:15 Найти цитируемый пост)
Впрочем, htons можно просто вставлять всегда. 

можно, но:
Цитата(tzirechnoy @  7.8.2014,  13:15 Найти цитируемый пост)
buffer[0] = post&0x0F; buffer[1] = (port >> 8)&0x0F;

этот вариант намного красивей)



--------------------
«Потому что ценность акта действия в этой стране возрастает в несколько раз».
PM MAIL Skype   Вверх
feodorv
Дата 7.8.2014, 14:54 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
****


Профиль
Группа: Комодератор
Сообщений: 2214
Регистрация: 30.7.2011

Репутация: 10
Всего: 45



Цитата(konshyn @  7.8.2014,  11:35 Найти цитируемый пост)
отправляю пакет вот так:

Гм. Сделайте честную отправку пакета через sendto. Что там с /dev/udp, я не знаю.


--------------------
Напильник, велосипед, грабли и костыли - основные инструменты программиста...
PM MAIL   Вверх
konshyn
Дата 7.8.2014, 15:11 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Опытный
**


Профиль
Группа: Участник
Сообщений: 295
Регистрация: 19.9.2013

Репутация: нет
Всего: нет



Вот код:
Код

#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/ioctl.h>
#include <sys/epoll.h>
#include <sys/time.h>
#include <linux/if_packet.h>
#include <linux/if_ether.h>
#include <linux/if_arp.h>
#include <netinet/in.h>
#include <string.h>
#include <unistd.h>
#include <stdlib.h>
#include <arpa/inet.h>

int main(int argc, char *argv[])
{
    int udp_socket = socket(PF_INET, SOCK_RAW, IPPROTO_UDP);
    if (udp_socket < 0)
    {
        perror("socket() failed");
        return -1;
    }

    struct sockaddr_in servaddr;
    memset((void*)&servaddr, 0, sizeof(struct sockaddr_in));
    servaddr.sin_family = AF_INET;                  
    servaddr.sin_addr.s_addr = htonl(INADDR_ANY);   
    servaddr.sin_port = 0;       
   
    if ((bind(udp_socket, (struct sockaddr *)&servaddr, sizeof(struct sockaddr_in))) < 0)
    {
        perror("bind() failed");
        return -1; 
    }
    
    unsigned char buffer[2000] = {0};
    int length = 0;
    int i = 0;
    int counter = 0;
    struct timeval tv_ioctl = {0};

    int length_sock;
    char ipaddr[256] = {0};

    while (1)
    {
        memset((void *)&servaddr, 0, sizeof(struct sockaddr_in));
        memset((void *)&ipaddr, 0, sizeof(ipaddr));
        length = recvfrom(udp_socket, buffer, 2000, 0, (struct sockaddr *)&servaddr, (void*)&length_sock);

        printf("ip = %s\n", inet_ntop(AF_INET, (void*)&servaddr.sin_addr, ipaddr, sizeof(ipaddr))); 
        printf("Port = %d\n", servaddr.sin_port);

        unsigned short int port = (buffer[22] << 8) | (buffer[23]);

        printf("Port = %u\n", port);
        printf("Port = %u\n", (buffer[(buffer[0] & 0x0F) * 4 + 2] << 8) | buffer[(buffer[0] & 0x0F) * 4 + 3]);
    }
    return 0;
}

вот результат одно из пакетов:
Цитата

ip = 192.168.1.76
Port = 0;
Port = 55955
Port = 55955


Добавлено через 6 минут и 52 секунды
Но если делать не сырой сокет, а обычный датаграммный (верхнуюю часть заменить на это)
Код

    int udp_socket = socket(PF_INET, SOCK_DGRAM, 0);
    if (udp_socket < 0)
    {
        perror("socket() failed");
        return -1;
    }

    struct sockaddr_in servaddr;
    memset((void*)&servaddr, 0, sizeof(struct sockaddr_in));
    servaddr.sin_family = AF_INET;                  
    servaddr.sin_addr.s_addr = htonl(INADDR_ANY);   
    servaddr.sin_port = htons(58549);    

то да, recvfrom пишет в sin_port номер порта, с которого пришел пакет.
Все дело в сыром сокете.


--------------------
«Потому что ценность акта действия в этой стране возрастает в несколько раз».
PM MAIL Skype   Вверх
feodorv
Дата 7.8.2014, 21:53 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
****


Профиль
Группа: Комодератор
Сообщений: 2214
Регистрация: 30.7.2011

Репутация: 10
Всего: 45



Цитата(konshyn @  7.8.2014,  16:11 Найти цитируемый пост)
Все дело в сыром сокете. 

Ага. Видимо, сетевой уровень в этом случае не разбирает датаграмму...


--------------------
Напильник, велосипед, грабли и костыли - основные инструменты программиста...
PM MAIL   Вверх
konshyn
Дата 8.8.2014, 10:43 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Опытный
**


Профиль
Группа: Участник
Сообщений: 295
Регистрация: 19.9.2013

Репутация: нет
Всего: нет



Цитата(feodorv @  7.8.2014,  21:53 Найти цитируемый пост)
Ага. Видимо, сетевой уровень в этом случае не разбирает датаграмму... 

Но source IP записывает.


--------------------
«Потому что ценность акта действия в этой стране возрастает в несколько раз».
PM MAIL Skype   Вверх
feodorv
Дата 8.8.2014, 21:36 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
****


Профиль
Группа: Комодератор
Сообщений: 2214
Регистрация: 30.7.2011

Репутация: 10
Всего: 45



Цитата(konshyn @  8.8.2014,  11:43 Найти цитируемый пост)
Но source IP записывает. 

Гм. Значит, разбирает только до уровня заголовка IP))) Сырой сокет, однако)))

Заметил в коде недочет. Переменная length_sock должна быть правильно инициализирована перед вызовом recvfrom.


--------------------
Напильник, велосипед, грабли и костыли - основные инструменты программиста...
PM MAIL   Вверх
konshyn
Дата 9.8.2014, 13:04 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Опытный
**


Профиль
Группа: Участник
Сообщений: 295
Регистрация: 19.9.2013

Репутация: нет
Всего: нет



Цитата(feodorv @  8.8.2014,  21:36 Найти цитируемый пост)
Заметил в коде недочет. Переменная length_sock должна быть правильно инициализирована перед вызовом recvfrom. 

С одной стороны - да. Так Стивенс писал. Но я не знаю, как в стандарте. Но все работает, т.к. в эту переменную записывается длина структуры, а не считывается.
А если инициализировать так, как нужно, результат не меняется


--------------------
«Потому что ценность акта действия в этой стране возрастает в несколько раз».
PM MAIL Skype   Вверх
Ответ в темуСоздание новой темы Создание опроса
0 Пользователей читают эту тему (0 Гостей и 0 Скрытых Пользователей)
0 Пользователей:
« Предыдущая тема | C/C++: Сети | Следующая тема »


 




[ Время генерации скрипта: 0.1624 ]   [ Использовано запросов: 22 ]   [ GZIP включён ]


Реклама на сайте     Информационное спонсорство

 
По вопросам размещения рекламы пишите на vladimir(sobaka)vingrad.ru
Отказ от ответственности     Powered by Invision Power Board(R) 1.3 © 2003  IPS, Inc.