socat:如何与shell进行简单的双工通信?

socat:如何与shell进行简单的双工通信?

我想创建一个到远程服务器的 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 服务器开销。 Shell 是 Bash,没有可用的 ZSH。也可以使用普通 Python。

答案1

socat 是否可以进行这种类型的双工通信(最好的情况是无需打开新端口)?

不;但这与 无关socat。 TCP 连接被关闭,然后不能再使用。但你的机制,特别cat -是 Stéphane 指出的,依赖于关闭连接。

Shell 是 Bash,没有可用的 ZSH。也可以使用普通 Python。

所以,普通的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())

请注意,这绝对是普通的 python。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.

然后客户端的 shell 脚本编写就变得非常简单:

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

答案2

Socat 的解决方案:

您尝试中发生的情况是:第一次成功传输后,shell 写入文件内容并终止。 Socat 将数据传输到服务器(并使用 TCP halfclose 发送 EOF)。服务器接收数据(和EOF),并发送处理消息。客户端想要将处理消息写入外壳,但它已经终止,因此“管道损坏”。

您想知道 Socat 是否有解决方案。是的,有一个,有近一百万个技巧......它通过了简单的测试,但可能会因实际要求而失败。

问题是 shell 必须发送 EOF,但仍然必须能够接收消息。我不知道有什么方法可以让 shell 执行 halfclose,所以技巧是使用两个 shell,使用管道在 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超时时间,单位为秒,且必须长于最长处理时间;

客户端打开两个shell,一个数据源仍然等待从另一个shell传递的ACK,它在文件传输后终止;第二个 shell 从服务器获取所有数据,尤其是 ACK,然后触发第一个 shell,最后将服务器输出传输到 stderr。

相关内容