То, что я хочу сделать, ДОЛЖНО быть довольно простым, но уже целый день потерян для этого. Я немного переделываю вопрос, чтобы помочь сосредоточиться:
Общая цель состоит в том, чтобы иметь «сервер» на основе bash, который, используя либо socat
(предпочтительно), либо ncat
(также должно работать хорошо), получает соединения от одного или нескольких «клиентов», обрабатывает данные от клиентов и, при необходимости, возвращает ответ клиентам. Этот сценарий также должен иметь возможность выполнять несколько различных задач «управления системой», которые относятся ко всему этому, и это упоминается только потому, что перенаправление сценариев туда и обратно было бы проблематичным.
В целом, довольно постоянная проблема заключается в одновременном получении двунаправленного потока данных к и от клиентов и этого bash-скрипта. То или иное в любой момент времени кажется относительно легким, но и того, и другого я еще не достиг, и когда я это сделаю, этот вопрос будет решен.
Ранние исследования:
Кажется, этот вопрос задает правильный вопрос, но он не дал мне ответов, которые могли бы мне помочь.
Самое близкое, что я нашел на данный момент, было в этом ответе, но он не делает то, что я хочу. Он передает оба/все операции ввода-вывода подключающемуся клиенту, тем самым делая его сервером... Что мне в нем нравится, так это то, что он действительно ДЕЙСТВИТЕЛЬНО настраивает ввод и вывод ncat полезным способом. Таким образом, идеи, содержащиеся в нем, были достаточно интересными, чтобы их можно было попробовать, и я это сделал. Однако они используют канал для ввода и перенаправление вывода на основе >, и, оглядываясь назад, возможно, мне следует придерживаться этой стратегии немного дальше.
Однако я также попробовал этот ответ, который тоже кажется очень многообещающим, но тоже не работает.
В комментариях и предлагаемом ответе стало ясно, что существует путаница в терминах stdin
и stdout
, ошибочно принимая их за stdin
и stdout
самого bash-скрипта. Не помогло то, что я использовал стандартный ввод для получения пользовательского ввода с клавиатуры, чтобы показать отправку клиентам, а также получение.
Конечно, каждая программа имеет свой собственный взгляд на то, что является стандартным, и в своих статьях я всегда имел в виду только stdout
и stdin
с точки зрения socat
или ncat
.
Вот пример того, как может выглядеть готовый код в «псевдокоде»:
while read line
do
# ...Process line based on it's contents.
#
# When a response is needed, stored in a variable
# called reply, then this might happen:
if [ -n "$reply" ] ;
then
echo "$reply" > socat_stdin
fi
done < socat_stdout
По пути я пробовал много версий этого:
while read line
do
echo "$line"
echo "Back at you: $line" >&3
done < $( exec 3> >(ncat -l -k $port) )
К настоящему моменту я попробовал все способы перенаправления на socat
stdin
с помощью < и в некоторых конфигурациях он не начинает говорить, что не указано два соединения. Очевидно, я еще не набрал правильную комбинацию.
Вот на чем я остановился вчера вечером:
#!/bin/bash
#
port=7654
inFile=/tmp/in
outFile=/tmp/out
logFile=/tmp/log
exec /bin/socat -U TCP-LISTEN:$port,fork EXEC:"tail -f $inFile" > $outFile 2>$logFile &
while read line
do
if [ -z "$line" ] ;
then
echo "EMPTY LINE!"
else
echo "user: $line"
echo "Back at you: $line" > "$inFile"
fi
done < $(tail -f "$outFile")
Если бы я мог выяснить, как заставить exec (не EXEC!) выполнять составную команду, я бы хотел попробовать поставить перед ней tail -f
и передать ответы от сервера клиенту(ам).
При диагностике этого lsof
показывает, среди прочего:
socat 3588549 root 0r CHR 1,3 0t0 4 /dev/null
...ДА, у него постоянно есть открытый файловый дескриптор /dev/null?! Я думаю, это явный признак «Я делаю это неправильно!» И действительно, в приведенном выше сценарии клиенты отключены. Хм...
Если есть вопросы по этому поводу, просто задавайте и т. д. Спасибо за помощь...
...На данный момент вопрос остается; как это делается?
Работая над этим, я нашел в литературе ссылку на этот файл: /usr/share/doc/socat/mail.sh
Для тех, кому интересно узнать об этом файле, он указывает путь, НО для этого требуются ДВЕ программы или вторая итерация; было бы ЗАМЕЧАТЕЛЬНО исключить один, и я, возможно, хочу адаптировать предложение Филиппа по именованной трубе.
Вот содержимое этого файла — оно довольно короткое:
#! /bin/sh
# source: mail.sh
# Copyright Gerhard Rieger and contributors (see file CHANGES)
# Published under the GNU General Public License V.2, see file COPYING
#set -vx
# This is an example for a shell script that can be fed to socat with exec.
# Its clue is that it does not use stdin/stdout for communication with socat,
# so you may feed the mail message via stdin to the script. The message should
# contain appropriate mail headers without continuation lines.
# socat establishes the connection to the SMTP server; the script performs the
# SMTP dialog and afterwards transfers the message body to the server.
# Lines with only a dot are not permitted - use two dots as escape.
# This script supports multiline answers from server, but not much more yet.
# Usage: cat message.txt |socat exec:"mail.sh [email protected]",fdin=3,fdout=4 tcp:mail.relay.org:25,crlf
while [ "$1" ]; do
case "$1" in
-f) shift; mailfrom = "$1"; shift;;
*) break;;
esac
done
rcptto = "$1"
[ -z "$1" ] && rcptto = "root@loopback"
#server=$(expr "$rcptto" : '[^@]*@\(.*\)')
[ -z "$mailfrom" ] && mailfrom = "$USER@$(hostname)"
# this function waits for a complete server message, checks if its status
# is in the permitted range (terminates session if not), and returns.
mail_chat () {
local cmd = "$1"
local errlevel = "$2"; [ -z "$errlevel" ] && errlevel=300
if [ "$cmd" ]; then echo "> $cmd" >&2; fi
if [ -n "$cmd" ]; then echo "$cmd" >&4; fi
while read status message <&3;
(
case "$status" in
[0-9][0-9][0-9]-*) exit 0;;
[0-9][0-9][0-9]*) exit 1;;
*) exit 1;;
esac
)
do :; done
if [ -z "$status" ]; then echo smtp connection failed >&2; exit; fi
echo "< $status $message" >&2
if [ "$status" -ge "$errlevel" ]; then
echo $message >&2
echo "QUIT" >&4; exit 1
fi
}
# expect server greeting
mail_chat
#...more calls to the mail_chat function...
@Philippe Меня не особенно волнует, ЧТО вы их называете, но с точки зрения процесса bash, используя в качестве ссылки то, что произойдет в реальном интерактивном сеансе с socat, стандартный ввод поступает от человека за клавиатурой, стандартный вывод поступает от клиентов (другие процессы, подключившиеся к порту), и, конечно же, протоколируется stderr, о котором вы не спрашивали. Конечно, чтобы обеспечить быстроту реагирования, должен существовать постоянный цикл чтения того, что приходит от клиентов, а в интерактивном случае это будет «стандартный вывод», выводимый на экран. Затем на это будут действовать и отвечать с использованием «stdin» socat.
@Philippe Я добавил кое-что, что может дать тебе лучшее представление.
Если вы перенаправите ввод в socat
, то эта сторона будет доступна только для чтения. Поэтому вам следует запускать socat
в однонаправленном режиме (-U
в этом случае или -u
, если писатель и читатель поменялись местами). Однако я не уверен, является ли невыполнение этого требования причиной вашей проблемы.
Итак, socat читает со стандартного ввода и передает прочитанное в порт, читает из порта и выбрасывает прочитанное на стандартный вывод. Верно ли это понимание?
@JohnBollinger Попробовал и ДА, ПОМОГЛО! СПАСИБО! Это еще не вся история: я делаю что-то не так с частью передачи из bash-скрипта в клиент... Но это улучшение!
@Philippe Давайте не зацикливаться на ярлыках: выходные данные скриптов должны быть входными данными socat, которые затем поступают в порт. Стандартный выход Socat - ОТ КЛИЕНТОВ, ИСПОЛЬЗУЮЩИХ ПОРТ - нужно попасть в скрипт. И недавнее изменение msot, основанное на вкладе Джона, является шагом в этом направлении — эта часть теперь работает, и клиент не выходит из строя после двух попыток записи.
@JohnBollinger ... Я был не совсем прав или совсем не прав: с флагом -u НЕТ, данные не попадают в сценарий от клиентов, но теперь они попадают в выходной файл ?! У меня есть хвост -f в моей тестовой среде и еще один в сценарии; последний не получает данные?! Должно быть, что-то простое, я делаю неправильно! Арг!
Вы пробовали именованные каналы (mkfifo
)?
@RenaudPacalet Да, это был аспект одного из примеров, которые я нашел и попробовал, и я сказал, что он работает, но передает управление тому, кто подключается к порту! Однако, внося изменения оттуда, я не увидел, насколько это сильно отличается от использования < и > для файлов, и поскольку файлы имеют преимущество в том, что оставляют след на диске, если хотите, я сейчас нахожусь именно там. Однако МОЖЕТ быть, я (я!) неправильно использовал < и >. ... Через несколько минут я собираюсь немного переработать вопрос, чтобы прояснить ситуацию, и, вероятно, приведу больше примеров того, «где я был до сих пор».
Просто мысль: в какой-то момент вам может оказаться более продуктивным написать сервер на более функциональном языке, используя потоки и встроенную поддержку сокетов; например, Python, Ruby, Perl и т. д. Скорее всего, вы сможете выразить то, что хотите, с помощью гораздо меньшего количества хакерского кода.
@nneonneo Спасибо... Как уже отмечалось где-то в этих комментариях, для этой задачи уже существовал код Bash, написанный задолго до того, как эти более удобные платформы стали доступны. Если бы я знал, что перенести его на ncat/socat будет так сложно, я бы наверняка посвятил время уже начатому - но дольше написанию с нуля - усилиям по Java. Но теперь эти усилия уже улучшили старый код, и он, скорее всего, будет «запущен в производство» сегодня или, может быть, на выходных. ...Переход на socat теперь помогает будущим усилиям, устраняя узкое место в тестировании клиентов.
Моя цель - запустить экземпляр
socat
(предпочтительно) илиncat
(также должен работать хорошо) в качестве «сервера» и обрабатывать оба направления двунаправленного трафика - stdin и stdout - сценарием bash.
Я так понимаю, вы хотите использовать socat
в качестве сетевого интерфейса для сценария оболочки. Клиент, подключающийся к socat
из сети, будет отправлять данные, которые socat
пересылаются на стандартный ввод сценария, а стандартный вывод сценария возвращается к socat
для пересылки клиенту.
Я могу придумать несколько способов добиться этого, но проще всего будет использовать сопроцесс.
#!/bin/bash
port=7654
logFile=/tmp/log
coproc echo_server { cat; }
/bin/socat TCP-LISTEN:$port,fork STDIO <&${echo_server[0]} >&${echo_server[1]} 2>$logFile
В качестве альтернативы вы можете использовать тип адреса socat
или SYSTEM
, чтобы подключить его к сценарию. Это было бы проще всего сделать с помощью отдельного скрипта, но не намного сложнее сделать это с помощью всего лишь одного скрипта:
#!/bin/bash
if ! [ $1 = --server ]; then
port=7654
logFile=/tmp/log
exec /bin/socat TCP-LISTEN:$port,fork EXEC:"${BASH_SOURCE[0]} --server" 2>$logFile
else
# echo server
cat
fi
Этот вариант не зависит от особенностей оболочки Bash.
Я отметил слово «coproc», упомянутое в других ответах моего исследования, но в МОЕЙ системе его, похоже, нет, поэтому я его проигнорировал. «который coproc» ничего не возвращает. ... Если это буйтин, то я его ПОЛНОСТЬЮ пропустил!
Спасибо, Джон, попробовал... Первое решение не работает, и я не знаю, как это исправить. В нем говорится: «строка 19: 63: нет такого файла или каталога», а номер строки — это тот, который пытается — но безуспешно — запустить socat. ... Кстати: я не требую использования stdin/stdout и предпочел бы, чтобы они этого не делали. Но я возьму то, что работает, и со временем научусь. А пока объясните, пожалуйста, что здесь пытается сделать coproc? ... И: Почему вызов exec работает, а не напрямую?! ...Я пытаюсь здесь не только научиться, но и решить проблему! ПЛЮС, Я НЕ ПОНИМАЮ, что делает if во втором решении.
Ни одно из решений не работает так, как указано. ИДК, что мешает запуску socat на первом, так как я не понимаю coproc - пока, полагаю, - но я пошел дальше, и второй тоже не работает: после подключения клиенты сообщают "Разбит канал".
which coproc
ничего не возвращает, потому что coproc
— это ключевое слово в bash, например while
или if
, поэтому это часть языка bash
, а не команда, которую which
можно было бы найти, просматривая ваш PATH
. Он был представлен в bash 4.0, поэтому, если у вас нет более старой версии bash, чем та, которая у вас есть, используйте type coproc
вместо which coproc
, чтобы вы увидели, что она у вас есть, а затем попробуйте. См. gnu.org/software/bash/manual/html_node/Coprocesses.html и unix.stackexchange.com/a/86372/133219 для получения дополнительной информации о coproc
.
@RichardT, прошу прощения, я написал неправильные операторы перенаправления для альтернативы coproc
. Я не могу сказать вам, почему версия EXEC
у вас не работает, но я бы начал устранение неполадок с помещения кода сервера в отдельный скрипт и присвоения ему EXEC
. Вы также можете попробовать SYSTEM
вместо EXEC
, как уже упоминалось в этом ответе. В любом случае, адреса SYSTEM
и EXEC
являются основным механизмом socat
того, что вы пытаетесь сделать, подключая один конец канала, опосредованного socat
, к программе.
@JohnBollinger Спасибо, Джон, извинения с радостью приняты - РАД за всю помощь/обучение... Я начал свой новый ответ на основе примера socat (теперь цитируемого в вопросе), но отвлекся от его завершения, ну, сделав не теряю времени, реализуя код, который мне нужно завершить! Он также использует exec, но вместо stdio, что, как я также отметил в обновлении q, проблематично; использование fd3 и fd4 вместо этого очень помогает, и то, что я делаю сейчас. ...Разочаровано, что разработчики socat не нашли способа сделать это для процесса вызова, учитывая всю его сложность.
@RichardT, я думаю, ты неправильно понимаешь комментарий в цитируемом тобой сообщении. Насколько я прочитал, смысл не использовать stdin/stdout для связи с socat
в этом случае заключается в том, чтобы эти потоки можно было использовать для чего-то другого (получения текста сообщения электронной почты), а не потому, что socat
имеет какие-либо особые проблемы с использованием стандартные потоки.
@JohnBollinger (Вы имеете в виду КОД, который я процитировал, а не «сообщение»?) ... О, УВЕРЕН, но меня не волнует, ПОЧЕМУ они хотели это сделать: мое решение использует один набор кода, который может быть/является командой - строко-ориентированный инструмент управления системой, а также daemonize , и у него НЕ обременены stdin и stdout для socat, что позволяет одному сценарию делать все это без особой боли. ... Если бы socat позволил нам объявить fds из родительского процесса, это было бы хорошо и, конечно же, возможно, но они просто этого не сделали. ...Я не плохой bash-программист, просто у меня есть пробелы. В конце концов, я использовал его с конца 1980-х годов! Просто до недавнего времени я никогда не относился к этому слишком серьёзно.
Попробуйте эту версию:
#!/bin/bash
if test "$1" = server; then
while read input; do
echo "Hello $input"
done
exit
fi
exec {socat_in}<> <(true)
exec {socat_out}<> >(true)
socat - EXEC:"./test.sh server" <&$socat_in >&$socat_out &
socat_pid=$!
while read -r -p "Input (Ctrl-D to quit): " line; do
echo "User input: $line"
echo "$line" >&$socat_in
echo "Response from socat:"
head -n 1 <&$socat_out
done
exec {socat_in}>&-
exec {socat_out}>&-
kill $socat_pid
Бежать с :
chmod +x test.sh
./test.sh
Эм... как к клиентам подключиться? Упс! Вы пробовали это? Вся причина использования socat или ncat (netcat) — получение ввода-вывода от удаленных клиентов. ... Исходный код еще до того, как я начал, уже использовал файлы, смонтированные по nfs, и эта работа заключается в том, чтобы перенести этот древний код «в современную эпоху» и сделать его гораздо более гибким - например, NFS не нужен. ...Моя сейчас цель - проанализировать довольно загадочные правила синтаксиса socat, чтобы найти способ указать файловые дескрипторы сценария вызова. Вызов FD дочернего сценария, запускаемого socat, является вторым лучшим вариантом...
Кстати, запрос инициатора серверной части был лишь временной удобной функцией для генерации текста для ответа клиентам. И в первой версии, которую я выложил, была ошибка. Но в любом случае я переписал вопрос; вы могли бы взглянуть по-новому. ... А еще СПАСИБО за работу над этой проблемой!
@JohnBollinger Филипп, Джон, обратите внимание на обновление вопроса с кодом ОТ команды разработчиков socat — стоит посмотреть!
Действительно, разработчики socat предоставили важную подсказку, которую, однако, трудно найти! - с их файлом, включенным в мой дистрибутив:
/usr/share/doc/socat/mail.sh
Покопавшись и попробовав, я получил что-то вроде этого:
ONE скрипт использует функции следующим образом:
function startSocat()
{
# ... check for free port, etc
socat exec:"${BASH_SOURCE[0]} -SD",fdin=3,fdout=4 TCP-LISTEN:$port,fork
# -SD indicates, "socat-daemon"
#
# The above tells it to start this same code, pass it -SD and connect to it
# using file descriptors 3 and 4 and, of course where to listen, etc.
}
function socatLoop
{
while read line <&3;
do
# Process the read in line - in variable line!
done
}
function replyToClient()
{
echo "$@" >&4
}
И это в значительной степени охватывает все, что вам действительно нужно сделать. ... Я надеюсь, что читатель довольно легко поймет, как их вызывать, но буду говорить явно:
Конечно, если вы хотите организовать приватный диалог с отдельным клиентом, это потребует больше усилий с вашей стороны, но теперь, когда вы знаете основы, это не так сложно!
Небольшая придирка — function funcname() {
сочетает в себе устаревший синтаксис ksh funcname {
и синтаксис стандарта POSIX funcname() {
таким образом, что это совершенно несовместимо ни с устаревшим ksh, ни с POSIX sh.
@CharlesDuffy -chuckle- Это скорее случайность публикации, чем что-либо еще. Я обычно объявляю функции как: function fname() { и т. д., и причина прагматична: я могу в любой момент найти определение функции (в vi), используя либо /ion <start-of-f-name>, либо /< end-of-f-name>(, в зависимости от того, что кажется проще. Кстати, я УВЕРЕН, потратил на это много времени! Я ожидал, что socat будет вести себя как большинство других процессов, но даже такие вещи, как именованные каналы, не сработали - я продолжал думать, что я делал это неправильно, но теперь я в этом не уверен... Как бы то ни было, у меня все работает. Спасибо за ответ!
В вашем сценарии socat есть стандартный ввод, стандартный вывод и порт, каковы их отношения?