socat: 쉘을 사용하여 간단한 이중 통신을 수행하는 방법은 무엇입니까?

socat: 쉘을 사용하여 간단한 이중 통신을 수행하는 방법은 무엇입니까?

원격 서버에 대한 TCP 연결을 생성 socat하고 클라이언트가 전송합니다.

  1. 이미지 파일 작업( "convert", "open"등)
  2. 이미지(바이너리 콘텐츠) 자체

그러면 서버는 주어진 작업을 사용하여 이미지를 처리합니다.

내 첫 번째 생각은 바이너리 콘텐츠 앞에 이 텍스트 조작 유형을 전달하는 것이었습니다.

# client
socat tcp:server:9999 -\
  < <(echo "open"; cat path/to/imgfile)

# server
socat tcp-listen:9999 - | {
  IFS= read -r action
  cat - > /tmp/imgfile
  # do process image file with action type ...
}

이는 특정 상황에서는 작동하는 것처럼 보이지만 여러 매개변수를 전달해야 하는 경우와 같이 유연하지 않습니다. 또한 바이너리 콘텐츠 앞에 텍스트를 추가하면 이미지 파일이 손상되었을 수도 있다는 생각이 듭니다.

그래서 저는 두 매개변수를 하나씩 전달하는 더 깔끔한 솔루션을 찾으려고 노력했습니다(여기서는 이중 통신이라고 함).

여기에 이미지 설명을 입력하세요.

지금까지 제가 시도한 것은 다음 분야의 전문가가 아니라는 것입니다 socat.

# client
socat\
  tcp:server:9999\
  system:'echo "open"; IFS= read -r ack; cat path/to/image.png'

# server
socat tcp-listen:9999\
  system:'IFS= read -r action; echo "ACK"; binary=$(cat -); echo "process image with action now..."'

지금은 허락해줘

깨진 파이프

이러한 유형의 이중 통신이 가능합니까 socat(새 포트를 열지 않는 최상의 경우)?

더 간단한 대안을 고려해 보았는가?

아마도 이것은 더 이상 프레임을 사용하지 않고, 가능하다면 HTTP 서버 오버헤드 없이 가장 간단하고 간단한 방법으로 수행되어야 한다고 말하고 싶습니다. 쉘은 Bash이며 사용 가능한 ZSH가 없습니다. 일반 Python을 사용할 수 있습니다.

답변1

socat을 사용하여 이러한 유형의 이중 통신이 가능합니까(새 포트를 열지 않는 최상의 경우)?

아니요. 하지만 그것은 그것과 아무 관련이 없습니다 socat. TCP 연결이 닫혀 더 이상 사용할 수 없습니다. 그러나 특히 cat -Stéphane이 지적한 것처럼 귀하의 메커니즘은 연결을 닫는 데 의존합니다.

쉘은 Bash이며 사용 가능한 ZSH가 없습니다. 일반 Python을 사용할 수 있습니다.

그래서, 평범한 파이썬입니다.

~에서 포함된 모듈에 대한 Python 문서, 실제로는 약간만 수정되었습니다.

from xmlrpc.server import SimpleXMLRPCServer
from xmlrpc.server import SimpleXMLRPCRequestHandler

PORT = 8000

class RequestHandler(SimpleXMLRPCRequestHandler):
    rpc_paths = ('/RPC2',)

with SimpleXMLRPCServer(('localhost', PORT),
                        requestHandler = RequestHandler,
                        use_builtin_types = True) as server:
    server.register_introspection_functions()

    # Register a function under function.__name__.
    @server.register_function
    def convert(target_format: str, img: bytes):
        # do something with img instead of just returning it,
        # but you get the idea.
        return img

    @server.register_function
    def length(img: bytes):
        return len(img)


    server.serve_forever()

클라이언트 측에서는

import xmlrpc.client

PORT = 8000

server_handle = xmlrpc.client.ServerProxy(f'http://localhost:{PORT:d}',
                                          use_builtin_types = True)

with open("inputimage.jpg", "rb") as infile:
    converted = server_handle.convert("image/png", infile.read())

with open("outputimage.png", "wb") as outfile:
    outfile.write(converted)

# Print list of available methods
print(server_handle.system.listMethods())

이것은 확실히 일반 파이썬이라는 점에 유의하십시오. xmlrpc.server표준 Python에는 그 이상도 socket그 이하도 포함되어 있지 않습니다 math.


여기에서 바닐라 개념을 확장할 수 있습니다. venv이는 Python 표준 라이브러리의 일부이기도 합니다. 여러분이라면 정말 좋을 것 같아요가지다표준 Python입니다.

python3 -m venv serverstuff
. serverstuff/bin/activate
pip install Flask

조금 더 편안함을 주세요. 그런 다음 다음과 같은 서버를 작성할 수 있습니다.빠른 시작 예시:

from flask import Flask
from flask import request
from flask import Response

app = Flask(__name__)


@app.route("/")
def say_hello():
    return "helloo"


@app.route("/convert/<target_fmt>"):
def convert(target_fmt):
    print(target_fmt)
    try:
        infile = request.files["file"]
    except Exception as e:
        print(e)
        return Response(response="", status=406)
    # convert it; infile is a Werkzeug.Filestorage
    # https://werkzeug.palletsprojects.com/en/2.3.x/datastructures/#werkzeug.datastructures.FileStorage
    data = infile.read()
    return Response(response=data, mimetype=f"image/{target_fmt}", status=200)

로 저장 server.py하고 실행하세요 python3 -m flask --app server run --port 8000.

그러면 클라이언트측 쉘 스크립팅이 매우 간단해집니다.

$ myfiletoupload=/home/mosaic/catpicture.jpg
$ curl http://127.0.0.1:8000/convert/png -F "file=@${myfiletoupload}"

답변2

Socat의 솔루션:

시도 시 발생하는 상황은 다음과 같습니다. 첫 번째 성공적인 전송 후 쉘은 파일 내용을 쓰고 종료됩니다. Socat은 데이터를 서버로 전송하고 TCP halfclose를 사용하여 EOF를 보냅니다. 서버는 데이터(및 EOF)를 수신하고 처리 메시지를 보냅니다. 클라이언트는 처리 메시지를 셸에 쓰고 싶었지만 종료되었으므로 "깨진 파이프"가 발생했습니다.

Socat에 해결책이 있는지 궁금합니다. 예, 하나가 있고 거의 백만 가지의 트릭이 있습니다... 간단한 테스트를 통과하지만 실제 요구 사항으로 인해 실패할 수도 있습니다.

문제는 쉘이 EOF를 보내야 하지만 여전히 메시지를 받을 수 있어야 한다는 것입니다. 쉘이 반닫기를 수행하도록 하는 방법을 모르므로 두 개의 쉘을 사용하고 파이프를 사용하여 Socat 외부에서 ACK 조건을 전달하는 것이 요령입니다.

고객:

set +H; rm -f /tmp/pipe0; mkfifo /tmp/pipe0; 
socat -t 3600 \
  tcp:server:9999 \
  system:'echo "open"; cat /tmp/pipe0 >/dev/stderr; cat /path/to/imgfile'!!system:'IFS= read -r ack; echo $ack >/tmp/pipe0; cat >/dev/stderr'

섬기는 사람:

socat -t 3600\
  tcp-listen:9999,reuseaddr \
  system:'IFS= read -r action; echo "ACK"; binary=$(cat -); echo "process image with action now..."'

설명하다:

"set +H"를 사용하면 bash에서 "!"를 사용할 수 있습니다.

파이프는 판독기에 의해 처음 열리므로 이미 존재해야 합니다.

"-t 3600"은 halfclose 시간 초과를 초 단위로 설정하며 최대 처리 시간보다 길어야 합니다.

클라이언트는 두 개의 셸을 엽니다. 하나의 데이터 소스는 다른 셸에서 전달된 ACK를 기다리고 파일 전송 후에 종료됩니다. 두 번째 셸은 서버에서 모든 데이터, 특히 ACK를 가져온 다음 첫 번째 셸을 트리거하고 마지막으로 서버 출력은 stderr로 파이프됩니다.

관련 정보