#c #linux #sockets #shell
#c #linux #сокеты #оболочка
Вопрос:
Я обновляю программу фильтрации, написанную на C. Программа предназначена для приема потока данных с последовательного порта, слегка модифицируйте его; затем отправьте его на ip-адрес беспроводного туннеля tcp-rs485. В настоящее время программа использует перенаправление ввода-вывода из сценария оболочки — это вызывается следующим образом:
./packetfilter </dev/ttyS4 >/dev/tcp/10.0.50.101/4660
Оболочка предоставляет мне tcp / ip бесплатно. Теперь мне нужны оба stderr и stdout.
Какой самый простой способ записать мои последовательные данные на сетевой адрес?
Ответ №1:
Открыть сокет на C очень легко, но в большинстве кода, который вы найдете, используются уродливые, устаревшие методы, из-за которых это выглядит сложно. Вот правильный способ:
struct addrinfo *ai;
int fd;
if (getaddrinfo(host_string, port_string, 0, amp;ai)) goto failed;
fd = socket(ai->ai_family, SOCK_STREAM, 0);
if (connect(fd, ai->ai_addr, ai->ai_addrlen)) goto failed2;
Заполните обработку ошибок.
Комментарии:
1. Спасибо. Это выглядит вполне выполнимо. Что мне #включить?
2.
<netdb.h>
и<sys/socket.h>
3. Вам также следует вызвать
freeaddrinfo(ai)
, если вы не собираетесь сохранять адрес после завершения, но, возможно, захотите сохранить его на случай, если вам понадобится повторно подключиться позже.4. В приведенном выше коде первая строка должна быть: struct adrinfo * ai; . Это должен быть указатель
Ответ №2:
Вам нужно разделить IP-адрес на «точки» и сформировать 32-разрядное целое число (предполагая, что вы беспокоитесь только об IPV4 — если IPV6, у вас впереди больше работы …).
Каждое «пунктирное» число станет одним байтом без вашего IP-адреса — так что в шестнадцатеричном формате ваш адрес в примере станет 0x0A003265
(0x0A = 10, 00 = 0, 0x32 = 50, 0x65 = 101, если я не напортачил)
ЗАТЕМ передайте 32-разрядное целое число через функцию htons, чтобы оно было помещено «в правильном» (сетевом) порядке (htons преобразует порядок байтов с хоста на сетевой). Подключите результат к вашему сокету, и все готово.
Комментарии:
1. Спасибо. Я смотрю на этот прототип: int getaddrinfo(const char * node, const char * service, const struct addrinfo *hints, struct addrinfo **res);. Это принятие адреса в виде строки. Я что-то упускаю?
2. Ответ, который вы дали, — это именно тот старый, уродливый способ, которым вы не должны этого делать. В этом году закончилось пространство IPv4. В ближайшие несколько лет поддержка IPv6, вероятно, станет проблемой «сделай сам» для приложений; будет большая аудитория, которая просто не сможет их использовать, если они не поддерживают IPv6. Так что не пишите код в этом старом уродливом стиле, где вам приходится думать о преобразовании двоичных форматов адресов самостоятельно. Просто вызовите
getaddrinfo
, и все будет сделано за вас, и IPv4, IPv6 и даже IPv7будут работать просто отлично, без каких-либо усилий.
3. Вы имеете в виду IPv8; IPv7 будет (?) строго экспериментальным протоколом.
Ответ №3:
Перенаправьте на более высокий FD, затем используйте write(2)
.
./packetfilter </dev/ttyS4 3>/dev/tcp/10.0.50.101/4660
Комментарии:
1. Этот ответ не на C, это bash. Подделка
/dev/tcp
— это расширение bash, и это источник многих проблем. Некоторые дистрибутивы отключают его, поскольку он выдает несоответствующую оболочку и потенциально создает риски безопасности в среде с ограниченным доступом, основанной только на оболочке chroot.2. @R..: В любом случае, это также то, что в настоящее время использует OP.
3. Да, спасибо. Но я хотел бы передать ip-адрес в командной строке и обработать вывод из программы. Я никогда не занимался сетевым программированием и при необходимости научусь. Но если есть простой способ записать поток байтов на ip-адрес — дублирующий то, что в настоящее время выполняется bash — мне было бы полезно узнать, как это сделать.
4. Простого эквивалента не существует. Записать данные в сокет достаточно просто, но для настройки сокета в первую очередь требуется несколько шагов.
5. Да, но OP искал способ получше. Подход OP также очень плох, потому что, если соединение прерывается, у программы нет возможности попытаться восстановить его.