我想创建一个到远程服务器的 TCP 连接socat
,客户端发送
- 图像文件的操作(
"convert"
、"open"
等) - 图像(二进制内容)本身
然后服务器将使用给定的操作处理图像。
我的第一个想法是在二进制内容之前传递此文本操作类型:
# 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。