在socketChannel上发送jpeg图像

时间:2016-02-23 19:34:27

标签: java image nio socketchannel

我目前正在测试我最初计划编写的小游戏所需的编程技巧,而且我目前仍然坚持通过套接字通道传输图像。我计划通过向你的对手发送某种“头像”或“个人资料图片”来编写我所写的“战舰”计划。 我有一个正常套接字的工作示例:

服务器端:

    try {
        ServerSocket serverSocket = new ServerSocket(port); //provided at an earlier point in the code
        Socket server = serverSocket.accept();
        BufferedImage img = ImageIO.read(ImageIO.createImageInputStream(server.getInputStream()));
        //here would be code to display the image in a frame, but I left that out for readability
        server.close();
        serverSocket.close();
    } catch(Exception e) {   //shortened version to improve readability
               e.printStackTrace();
    }

客户方:

    Socket client = new Socket(ip, port);
    bimg = ImageIO.read(getClass().getResource("/images/ship_1.jpeg"));
    //the image is located at /resources/images/ship_1.jpeg
    ImageIO.write(bimg,"JPG",client.getOutputStream());
    client.close();

到目前为止,一切都按预期进行 现在,socketChannels(Java NIO)的问题:

客户方:

    BufferedImage bimg = ImageIO.read(getClass().getResource("/images/ship_1.jpeg"));
    ByteArrayOutputStream outputArray = new ByteArrayOutputStream();
    //i do NOT know if the following line works - System.out.println() statements after it are not executed, so ... probably doesn't work either.
    ImageIO.write(bimg, "jpeg", socketChannel.socket().getOutputStream());

服务器端:

    ByteBuffer imgbuf = ByteBuffer.allocate(40395);
    int imageBytes = socketChannel.read(imgbuf);
    while (true) {
        if (imageBytes == (0 | -1)) {
            imageBytes = socketChannel.read(imgbuf);
        } else {
            break;
        }
    }
    byte[] byteArray = imgbuf.array();
    System.out.println(byteArray.length);
    InputStream in = new ByteArrayInputStream(byteArray);
    BufferedImage img = ImageIO.read(in);

到目前为止,我还没有真正使用过图像,因此在使用缓冲区或其他任何我找不到的内容时可能会出现一些错误。
无论如何,如果我执行程序(有很多不同的代码工作正常),我在服务器端提供的最后一行收到异常:
javax.imageio.IIOException: Invalid JPEG file structure: missing SOS marker

任何帮助将不胜感激!

3 个答案:

答案 0 :(得分:1)

你不需要大部分内容。

客户方:

BufferedImage bimg = ImageIO.read(getClass().getResource("/images/ship_1.jpeg"));
ByteArrayOutputStream outputArray = new ByteArrayOutputStream();
//i do NOT know if the following line works - System.out.println() statements after it are not executed, so ... probably doesn't work either.
ImageIO.write(bimg, "jpeg", socketChannel.socket().getOutputStream());

你根本不需要ImageIO。它只是一个简单的字节副本:

InputStream in = getClass().getResource("/images/ship_1.jpeg");
byte[] buffer = new byte[8192];
int count;
while ((count = in.read(buffer)) > 0)
{
    socketChannel.socket().getOutputStream().write(buffer, 0, count);
}

服务器端:

ByteBuffer imgbuf = ByteBuffer.allocate(40395);
int imageBytes = socketChannel.read(imgbuf);
while (true) {
    if (imageBytes == (0 | -1)) {

这并没有任何意义。它将imageBytes0 | -1进行比较,即{0xffffffff,它仅在流结束时为真。

        imageBytes = socketChannel.read(imgbuf);

在这种情况下进行另一次阅读是徒劳的。它只会返回另一个-1。

    } else {
        break;

如果你没有得到-1,那么你就会被打破,即只要你真正阅读了一些数据。

    }
}
byte[] byteArray = imgbuf.array();
System.out.println(byteArray.length);
InputStream in = new ByteArrayInputStream(byteArray);
BufferedImage img = ImageIO.read(in);

你也不需要这些。

ByteArrayOutputStream baos = new ByteArrayOutputStream();
ByteBuffer imgbuf = ByteBuffer.allocate(40395);
while ((imageBytes = socketChannel.read(imgbuf)) > 0)
{
    imgbuf.flip();
    while(imgbuf.hasRemaining())
    {
        baos.write(imgbuf.get());
    }
    imgbuf.compact();
}
BufferedImage img = ImageIO.read(new ByteArrayInputStream(baos.toByteArray()));

答案 1 :(得分:0)

我看到的最大问题是假设imgbuf.array()累积了所有数据。该方法只返回支持缓冲区的数组。将缓冲区视为一个数据块,因为它是什么(ref)。

您需要一整套具有完整图像的数据才能在"服务器"侧。所以你必须做一些不同的事情。这至少不是优化代码,但它应该可以帮助您入门:

ArrayList<byte> fullImageData = new ArrayList<byte>();
ByteBuffer imgbuf = ByteBuffer.allocate(40395);
int imageBytes = socketChannel.read(imgbuf);

while ((imageBytes = socketChannel.read(imgbuf)) > 0)
{
    imgbuf.flip(); // prepare for reading

    while(imgbuf.hasRemaining())
    {
        fullImageData.add(imgbuf.get());
    }

    imgbuf.clear(); // prepare for next block
}

byte[] byteArray = fullImageData.toArray();
System.out.println(byteArray.length);
InputStream in = new ByteArrayInputStream(byteArray);
BufferedImage img = ImageIO.read(in);

NIO是围绕数据块而不是数据流构建的。它以这种方式提供更多吞吐量。另一个选择是使用FileChannel立即将缓冲区写入临时文件,然后使用标准FileStream读取数据 - 这将保护您免受应用程序崩溃,因为图像太大。在这种情况下,循环变得更加简化:

while ((imageBytes = socketChannel.read(imgbuf)) > 0)
{
    imgbuf.flip(); // prepare for reading

    fileChannel.write(imgbuf); // write to temp file

    imgbuf.clear(); // prepare for next block
}

答案 2 :(得分:-2)

我最终自己找到了解决方案,首先将图像转换为bytearray,然后再将其发送到套接字上 代码:

客户方:

    BufferedImage bimg = ImageIO.read(getClass().getResource("/images/ship_1.jpeg"));
    byte[] byteArray;
    ByteArrayOutputStream baos = new ByteArrayOutputStream();
    ImageIO.write(bimg, "jpg", baos);
    baos.flush();
    byteArray = baos.toByteArray();
    baos.close();
    socketChannel.socket().getOutputStream().write(byteArray);

服务器端:

ByteBuffer imgbuf = ByteBuffer.allocate(40395);
int imageBytes = socketChannel.read(imgbuf);
while (true) {
    if (imageBytes == (0 | -1)) {
        imageBytes = socketChannel.read(imgbuf);
    } else {
        break;
    }
}
byte[] byteArray = imgbuf.array();
System.out.println(byteArray.length);
InputStream in = new ByteArrayInputStream(byteArray);
BufferedImage img = ImageIO.read(in);

我得到了将图像转换为字节数组的代码,反之亦然here,以防您感兴趣。