所以,我最近发现了如何通过文件发送 TCP 数据包/dev/tcp/hostname/port
,但我的主要问题是如何接收它们。我想制作一个可以托管 TCP 服务器的 bash 脚本,以及另一个作为 TCP 客户端的脚本。一些帮助会很好。我还想指出,我这样做是作为一种学习经历,这就是为什么我不使用ssh
或telnet
作为执行我想要的任务的方式。
答案1
根据这, 不。你做不到,因为bash
你没有尝试bind(2)
套接字,但只是尝试connect(2)
。
以下是启动netcat
服务器的几种方法:
(我用nmap
的ncat
因为GNUnc
自 2004 年以来就没有看到更新)
local $ :|{ ncat -l 9999 --keep-open --allow localhost |
local > PS1='ncat $ ' PS2='ncat > ' dash +m -i 2>&1
local > } 1<>/dev/fd/0
[1] + Running : | { ncat -l 9999 --keep-open --allow localhost | PS1='ncat $ ' PS2='ncat > ' dash +m -i 2>&1; } 1<>/dev/fd/0
我借用了:|
最后的管道 -dash
写入它并ncat
读取它,因为:
null 命令当然不需要它。有时比 更容易mkfifo
。
无论如何,重点是所有ncat
写入的内容都通过管道传输到dash
的标准输入,并且所有dash
写入的内容都通过管道传输到ncat
的标准输入。我知道,这不是bash
,但我会解决这个问题。但至少请注意该+m
选项。
local $ ncat localhost 9999
ncat $ echo $0 $$
dash 31231
ncat $ ls -l /dev/fd/[012]
lr-x------ 1 mikeserv mikeserv 64 Dec 11 23:28 /dev/fd/0 -> pipe:[3563412]
l-wx------ 1 mikeserv mikeserv 64 Dec 11 23:28 /dev/fd/1 -> pipe:[3567682]
l-wx------ 1 mikeserv mikeserv 64 Dec 11 23:28 /dev/fd/2 -> pipe:[3567682]
ncat $ ^C
local $
我必须使用,+m
因为-m
受监控的作业控制 shell - 通常默认情况下将-i
交互式 shell 设置为打开 - 会尝试从其控制终端读取数据,并且会发送一个SIGTTIN
- 默认情况下会自动挂起它,或者别的(如果它试图忽略或阻止它)杀了它。
[1] + Stopped(SIGTTIN) : | { ncat -l 9999 | dash -i 2>&1; } 1<>/dev/fd/0
但这里没有终端 - 正如您在输出中看到的那样,这些只是管道ls
- 因此-m
监视是不行的。
bash
碰巧的是,即使在禁用状态下,其初始交互式终端关联似乎也没有那么灵活readline
。
[1] + Stopped(SIGTTIN) : | { ncat -l 9999 | bash --noediting +m -i 2>&1 ; } 1<>/dev/fd/0
那么该怎么办?好吧,我们需要一个伪终端。就像我们在模拟器中的东西一样。
local $ ls -l /dev/fd/[012]
lrwx------ 1 mikeserv mikeserv 64 Dec 11 23:30 /dev/fd/0 -> /dev/pts/0
lrwx------ 1 mikeserv mikeserv 64 Dec 11 23:30 /dev/fd/1 -> /dev/pts/0
lrwx------ 1 mikeserv mikeserv 64 Dec 11 23:30 /dev/fd/2 -> /dev/pts/0
/dev/ptmx
我们可以从Linux 系统上的主设备获取一个tty
(尽管如果您不想先成为该组的成员,您的用户可能需要成为该组的成员chown
)。要是我们<
open(2)
ptmx 设备将创建一个新的伪终端,如果我们之后unlockpt(3)
新的设备文件,我们可以对其进行读写。
不久前,我了解到使用 shell 确实没有一种简单的方法可以做到这一点,所以我为它写了一个小C程序。您会找到一些解释和简单的构建说明(实际上只是复制/粘贴到 shell 提示符中)在那个链接 - 这是pts
我在下面使用的程序。
local $ { 9>&- setsid -c -- bash <> "$({ pts &&
local > >&9 ncat -l 9999 -k --allow localhost
local > } <&9 &)" 2>&0 >&2 ; } 9<> /dev/ptmx
local $
有一个bash
开始为会议主持人在伪终端上pts
解锁并命名为其标准输出 - 它替换了<>
重定向中的标准bash
。不过,显然什么也没有发生,因为所有的 i/o 都在其他地方 - 新的伪终端 - 唯一的链接是ncat
在端口 9999 上启动的服务器。-i
这里不需要交互式开关 -bash
将自动在它自己的有限公司。
local $ ncat localhost 9999
[mikeserv@desktop ~]$ echo hey
echo hey
hey
[mikeserv@desktop ~]$ ls -l /dev/fd/[012]
ls -l /dev/fd/[012]
lrwx------ 1 mikeserv mikeserv 64 Dec 12 00:27 /dev/fd/0 -> /dev/pts/4
lrwx------ 1 mikeserv mikeserv 64 Dec 12 00:27 /dev/fd/1 -> /dev/pts/4
lrwx------ 1 mikeserv mikeserv 64 Dec 12 00:27 /dev/fd/2 -> /dev/pts/4
[mikeserv@desktop ~]$ ^[[A^[[A
好吧,我们快到了。所以我们肯定有一个套接字化的bash
- ,但你可能注意到那里有奇怪的双回声,最后一个提示符处有趣的转义实际上是我按下了 ^up 箭头键。这里的问题是我们有两级伪终端 - 我的ncat
客户端运行的伪终端设置stty echo
为与服务器运行的伪终端相同。而且由于客户端终端是面向行的,并且每个输入换行符刷新输出一次,并且还根据默认stty echoctl
设置打印 ctrl-char 转义符,bash
因此根本不会收到 ^up 箭头键,我们只看到有趣的小逃脱。
好的。我们也可以处理这个问题。
local $ ncatsh()(
local > ${2+":"} set localhost "${1:?ncatsh(): No port number provided!}"
local > stty=" stty -F'$(tty)'" || unset stty
local > trap " ${stty- ncat '${1##*\'*}' '${2##*\'*}' </dev/null} \
local > ${stty+$(for a in -g 'raw -echo isig intr "^A" quit "" susp ""'
local > do eval "$stty $a";done)}
local > trap - 0 1 2; exit" 0 1 2
local > ncat "$@"
local > )
该函数将检查其标准输入是否为终端(您的本地终端),如果不是,则仅将输入传递给客户ncat
端。但如果是这样的trap
话出口,挂断, 和打断信号在退出时恢复其标准的终端状态。这是一件好事,因为它也改变ncat
在调用标准之前的状态。
本地回显被禁用,本地终端否则设置为生的模式 - 因此每次按键都会ncat
立即发送到服务器。事实上,所有特殊的本地模式键都被禁用除了本地的整数- 通常是CTRL+C
,但这里配置为CTRL+A
相反 - 因为CTRL+C
将由bash
服务器解释。
我会再做ls
一次,但只需按 ^ 向上箭头即可RETURN
。
local $ ncatsh 9999
[mikeserv@desktop ~]$ ls -l /dev/fd/[012]
lrwx------ 1 mikeserv mikeserv 64 Dec 12 01:00 /dev/fd/0 -> /dev/pts/4
lrwx------ 1 mikeserv mikeserv 64 Dec 12 01:00 /dev/fd/1 -> /dev/pts/4
lrwx------ 1 mikeserv mikeserv 64 Dec 12 01:00 /dev/fd/2 -> /dev/pts/4
[mikeserv@desktop ~]$ ^C
[mikeserv@desktop ~]$ ^C
[mikeserv@desktop ~]$ ^C
[mikeserv@desktop ~]$
local $
你在那里看不到它,因为按下它时本地回显被禁用,但我按下了CTRL+A
中断会话并返回本地提示符,此时所有本地终端配置都恢复为正确状态。不过,服务器bash
仍然存在,ncatsh 9999
如果我愿意的话,它会立即带我回去。
答案2
为了简化您的工作,请尝试使用“netcat”工具。它可以用作服务器或客户端,并且能够发送和接收任意数据。
那就不用费心处理低级的东西了..
#server listening on port 8000/tcp
$>netcat -l 8000
#client sending stuff to localhost 8000/tcp
$>netcat localhost 8000
blah
答案3
bash
的虚拟/dev/tcp/host/port
文件(最初是 ksh 功能)只能用于连接TCP 端口的套接字。
如果您想做更多事情,例如创建侦听套接字并从中生成接受套接字,则无法通过重定向来完成。
zsh
zsh/net/tcp
通过其模块和内置对此提供支持ztcp
。
zmodload zsh/net/tcp
handle-connection() (
IFS= read -ru4 line || exit
print -r "Got: $line"
print -ru4 "Hello $line"
ztcp -c 4
)
ztcp -l -d 3 1234 # create a TCP listening socket on port 1234 on fd 3
while ztcp -ad4 3 # accept connection on fd 4
do handle-connection & exec 4>&-
done
它仍然受到限制,因为您无法指定要绑定的地址或侦听队列的大小,或仅关闭一个方向,或设置套接字选项...它也不执行 UDP 或 SCTP(您可以执行 Unix域套接字虽然使用zsocket
命令)。
如果你想要更高级的东西或者如果你没有 zsh,你可以使用socat
.例如,这里有bash
导出函数:
handle_connection() {
IFS= read -r line &&
printf 'Hello %s\n' "$line"
}
export -f handle_connection
socat tcp-listen:1234,reuseaddr,fork 'exec:bash -c handle_connection'
使用socat
,可能性是无限的,请参阅手册页了解详细信息。
例如,要让服务器生成 shell 会话:
socat tcp-listen:1234,reuseaddr,fork exec:bash,pty,ctty,setsid,stderr,sane
在客户端,您可以连接为:
socat -,raw,echo=0 tcp:host:1234
答案4
最简单的方法是创建自己的xinetd
服务,如下所示:
/etc/services
:
...
foo 500/tcp
...
/etc/xinetd.d/foo
:
service foo
{
disable = no
bind = 127.0.0.1
socket_type = stream
protocol = tcp
log_on_failure += USERID
server = /usr/local/lib/foo.sh
user = il
instances = UNLIMITED
wait = no
log_on_success =
}
/usr/local/lib/foo.sh
:
read ...
...
echo ...
因此,您的 shell 脚本会针对每个客户端连接执行,并将 stdin/stdout 直接附加到套接字。