我目前正在测试我计划最终编写的小游戏所需的编程技能,我目前正停留在通过套接字通道传输图像上。我计划在我编写的"战舰"程序上测试这一点,方法是向您的对手发送某种"头像"或"个人资料图片"。我有一个带有普通套接字的工作示例:
服务器端:
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
任何帮助将不胜感激!
你不需要大部分。
客户端:
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)) {
这没有丝毫意义。它将imageBytes
与0 | -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()));
我看到的最大问题是假设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
}
我最终自己找到了解决方案,首先将图像转换为字节数组,然后再通过套接字发送。
法典:
客户端:
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);
如果您有兴趣,我从这里获得了将图像转换为字节数组的代码,反之亦然。