Bash에서 TCP 서버 작성

Bash에서 TCP 서버 작성

그래서 최근에 파일을 통해 TCP 패킷을 보내는 방법을 알아냈는데 /dev/tcp/hostname/port, 가장 큰 문제는 그것을 어떻게 받는가 입니다. TCP 서버를 호스팅할 수 있는 bash 스크립트와 TCP 클라이언트 역할을 하는 또 다른 스크립트를 만들고 싶습니다. 약간의 도움이 좋을 것입니다. 나는 또한 이것을 학습 경험으로 하고 있다는 점을 지적하고 싶습니다. 이것이 내가 원하는 작업을 수행하는 방법으로 ssh또는를 사용하지 않는 이유입니다.telnet

답변1

~에 따르면이것, 아니요. bash시도하지 않았기 때문에 할 수 없습니다.bind(2)소켓이지만 시도해 보세요.connect(2).

netcat서버를 시작하는 몇 가지 방법은 다음과 같습니다 .

(나는 사용한다nmap~의ncat왜냐하면암소 비슷한 일종의 영양nc2004년 이후 업데이트를 본 적이 없음)


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작성된 모든 내용이 stdin 으로 파이프 dash되고 dash작성된 모든 내용이 stdin 으로 파이프된다는 것입니다 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작업 제어 셸이 -i제어 터미널에서 데이터를 읽으려고 시도하고 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/ptmxLinux 시스템의 마스터 장치에서 하나를 얻을 수 있습니다.tty(먼저 그룹의 회원이 되고 싶지 않다면 사용자가 그룹의 회원이어야 할 수도 있습니다 chown.). 만약 우리가< open(2)ptmx 장치는 다음과 같은 경우 새로운 의사 터미널을 생성합니다.unlockpt(3)읽고 쓸 수 있는 새로운 장치 파일입니다.

얼마 전에 나는 쉘을 사용하여 이 작업을 수행하는 쉬운 방법이 없다는 것을 알았습니다.나는 그것을 위해 작은 C 프로그램을 작성했습니다.. 몇 가지 설명과 간단한 조립 지침을 확인할 수 있습니다.(실제로는 쉘 프롬프트에 복사/붙여넣기하면 됩니다)해당 링크에서 - 이것은 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대체합니다 . 하지만 모든 I/O는 다른 곳에(새로운 의사 터미널) 있고 유일한 링크는 포트 9999에서 시작된 서버에 있기 때문에 아무 일도 일어나지 않는 것 같습니다. 여기서는 대화형 스위치가 필요하지 않습니다. 자동으로 자체 Ltd.가 됩니다.<>bashncat-ibash


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- 을 가지고 있지만 거기에 이상한 이중 에코가 있다는 것을 알 수 있으며 마지막 프롬프트에서 흥미로운 탈출은 실제로 ^위쪽 화살표 키를 누르는 것입니다. 여기서 문제는 두 가지 수준의 의사 터미널이 있다는 것입니다. 내 클라이언트가 실행 중인 의사 터미널은 서버가 실행 중인 의사 터미널과 동일하게 ncat설정되어 있습니다 . stty echo그리고 클라이언트 터미널은 라인 지향적이며 입력 개행당 한 번씩 출력을 플러시하고 stty echoctl기본 설정에 따라 ctrl-char 이스케이프도 인쇄하므로 bash^up 화살표 키는 전혀 수신되지 않고 재미있는 Little 이스케이프만 볼 수 있습니다.

좋아요 우리도 이 문제를 처리할 수 있습니다.


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 > )

이 함수는 표준 입력이 터미널(로컬 터미널)인지 확인하고 그렇지 않은 경우 입력을 client 에 전달합니다 ncat. 하지만 trap만약 그렇다면출구,끊다, 그리고방해하다신호는 종료 시 표준 터미널 상태를 다시 시작합니다. 이것도 좋은 일이니까.변화ncat표준을 호출하기 전의 상태입니다.

로컬 에코가 비활성화되었습니다. 그렇지 않으면 로컬 터미널이 다음으로 설정됩니다.날것의모드 - 모든 키 누름이 ncat즉시 서버로 전송됩니다. 실제로 모든 특수 로컬 모드 키가 비활성화됩니다.와는 별개로현지의정수- 대개CTRL+C, 그러나 여기의 구성은 다음과 같습니다.CTRL+A반대로 - 왜냐면CTRL+Cbash서버에서 해석됩니다 .

다시 하고 싶지만 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 포트 소켓.

청취 소켓을 생성하고 그로부터 수용 소켓을 생성하는 등 더 많은 작업을 수행하려는 경우 리디렉션을 사용하여 수행할 수 없습니다.

zshzsh/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무궁무진합니다. 자세한 내용은 매뉴얼 페이지를 참조하세요.

예를 들어, 서버가 셸 세션을 생성하도록 하려면 다음을 수행하세요.

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 ...

따라서 쉘 스크립트는 각 클라이언트 연결에 대해 실행되고 stdin/stdout을 소켓에 직접 추가합니다.

관련 정보