【问题标题】:Sending buffered images between Java client and Twisted Python socket server在 Java 客户端和 Twisted Python 套接字服务器之间发送缓冲图像
【发布时间】:2012-06-12 21:53:52
【问题描述】:

我有一个使用 Python 图像库绘制图像的服务器端函数。 Java 客户端请求一个图像,该图像通过套接字返回并转换为 BufferedImage。

我在数据前面加上要发送的图像大小,后跟一个 CR。然后我从套接字输入流中读取这个字节数,并尝试使用 ImageIO 转换为 BufferedImage。

在客户端的缩写代码中:

public String writeAndReadSocket(String request) {
    // Write text to the socket
    BufferedWriter bufferedWriter = new BufferedWriter(new OutputStreamWriter(socket.getOutputStream()));
    bufferedWriter.write(request);
    bufferedWriter.flush();

    // Read text from the socket
    BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(socket.getInputStream()));

    // Read the prefixed size
    int size = Integer.parseInt(bufferedReader.readLine());

    // Get that many bytes from the stream
    char[] buf = new char[size];
    bufferedReader.read(buf, 0, size);
    return new String(buf);
}

public BufferedImage stringToBufferedImage(String imageBytes) {
    return ImageIO.read(new ByteArrayInputStream(s.getBytes()));
}

和服务器:

# Twisted server code here

# The analog of the following method is called with the proper client
# request and the result is written to the socket.
def worker_thread():
    img = draw_function()
    buf = StringIO.StringIO()
    img.save(buf, format="PNG")
    img_string = buf.getvalue()
    return "%i\r%s" % (sys.getsizeof(img_string), img_string)

这适用于发送和接收字符串,但图像转换(通常)失败。 我试图理解为什么图像没有被正确读取。我最好的猜测是客户端没有读取正确的字节数,但老实说我不知道​​为什么会这样案例。

旁注:

  • 我意识到 char[]-to-String-to-bytes-to-BufferedImage Java 逻辑是迂回的,但是直接读取字节流会产生同样的错误。
  • 我有一个这样的工作版本,其中客户端套接字不是持久的,即。处理请求并断开连接。该版本运行良好,因为我不需要关心图像大小,但我想了解为什么建议的方法不起作用。

【问题讨论】:

  • 将二进制转换为文本通常是一个非常糟糕的主意(很多事情可能会出错)为什么不直接将二进制数据发送到您的服务器?
  • 你绝对是对的。我在化学实验室工作,我们历来忽略任何不能直接转化为论文的东西。我正在努力解决这个问题,但我并不是一个经验丰富的编码员。

标签: java python sockets twisted


【解决方案1】:
  1. BufferedReader.read() 不保证会填满缓冲区,将图像转换为字符串并返回不仅没有意义而且是错误的。

  2. String 不是二进制数据的容器,往返不保证有效。

最好重新设计协议,这样您就可以摆脱readLine(),并以二进制形式发送长度,并且可以使用DataInputStream读取整个流。

一般在处理二进制协议时,答案总是DataInputStreamDataOutputStream,除非字节顺序不是规范的网络字节顺序,这是协议设计错误,在这种情况下你需要查看按字节排序的ByteBuffers

【讨论】:

  • 此诊断不正确。他正在通过同一个缓冲区读取计数和图像。 (问题是他将二进制数据推入字符 I/O 堆栈......然后返回。)
  • @StephenC 同意。他还假设bufferedReader.read(buf, 0, size) 填满了缓冲区。
  • 我将所有内容都添加了 32 位二进制文​​件并切换到 DataInputStream。现在一切正常(!),虽然它随机错误〜5%的时间。关于写这些东西,我显然还有很多东西要学。你会推荐任何资源吗?我的标准方法(即谷歌)返回了很多相互矛盾的建议:-(
  • @PattimusPrime 在处理二进制协议时,答案总是DataInputStreamDataOutputStream,除非字节顺序不是规范的网络字节顺序,这是一个协议错误,其中如果您需要查看按字节排序的ByteBuffers。我已经调整了答案。
【解决方案2】:

在服务器代码中,您对sys.getsizeof 的使用是错误的。这将返回字节串对象的大小,而您想要的是字节串中的字节数,即它的长度len(img_string)

另外,在客户端代码中,.readLine 方法会读取字符,直到它看到 '\r' 可能跟在 '\n''\n' 之后,因此使用 '\r' 作为终止符会导致问题,如果图片数据恰好是0x0A,即'\n'

【讨论】:

  • 感谢您的意见!我从sys.getsizeof 切换到len,这绝对是个问题。我也摆脱了readLine 方法。
【解决方案3】:

我预计问题在于您正在尝试使用ReadergetBytes() 来读取二进制数据(图像)。

Reader 堆栈将从底层套接字流中获取字节,将它们转换为字符(使用平台的默认字符编码),并将它们作为字符串返回。然后再次使用默认编码将字符串内容转换回字节。对于二进制数据,字节到字符的初始转换可能是“有损”的。

解决方法是不使用Reader / BufferedReader。使用InputStreamBufferedInputStream。通过发送编码为文本的图像大小并不能让自己变得容易,但是您可以通过一次读取一个字节直到获得换行符并将它们“手动”转换为整数来处理它。

(如果大小以“网络顺序”作为固定大小的二进制整数发送,则可以使用DataInputStream 代替...)

【讨论】:

  • 我显然是这里的新手...你能定义“网络顺序”吗?在这一切中我应该遵循一些约定吗?如有任何建议,我将不胜感激。
  • @PattimusPrime 见en.wikipedia.org/wiki/…。 (也称为大端顺序;即最高有效字节在前。)
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2021-12-23
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2017-05-26
  • 1970-01-01
  • 2012-12-14
相关资源
最近更新 更多