如何知道Netty ByteBuf 中没有数据可读?
How can I know if there is no data to read in Netty ByteBuf?
我是 Netty 新手。文件传输有一个问题让我困惑了好几天。我想从客户端向服务器发送 image 文件。
下面的代码是可执行的。但是只有我关闭服务器才能正常打开接收到的图片文件。否则,它会显示“您似乎没有查看此文件的权限。请检查权限并重试”。因此,我想在 ByteBuf 中没有数据时使用 ByteBuf.isReadable() 关闭 fileoutputstream,但是 ServerHandler 中方法 channelRead 中的 else 块永远无法到达。没用。
此外,如果发送text文件,在服务器alive时可以正常打开。 我不想每次传输后都关闭服务器。请给我一些解决问题的建议。
这是 FileClientHandler
public class FileClientHandler extends ChannelInboundHandlerAdapter {
private int readLength = 8;
@Override
public void channelActive(ChannelHandlerContext ctx) throws Exception {
sendFile(ctx.channel());
}
private void sendFile(Channel channel) throws IOException {
File file = new File("C:\Users\xxx\Desktop\1.png");
FileInputStream fis = new FileInputStream(file);
BufferedInputStream bis = new BufferedInputStream(fis);
for (;;) {
byte[] bytes = new byte[readLength];
int readNum = bis.read(bytes, 0, readLength);
// System.out.println(readNum);
if (readNum == -1) {
bis.close();
fis.close();
return;
}
sendToServer(bytes, channel, readNum);
}
}
private void sendToServer(byte[] bytes, Channel channel, int length)
throws IOException {
channel.writeAndFlush(Unpooled.copiedBuffer(bytes, 0, length));
}
}
这是 FileServerHandler
public class FileServerHandler extends ChannelInboundHandlerAdapter {
private File file = new File("C:\Users\xxx\Desktop\2.png");
private FileOutputStream fos;
public FileServerHandler() {
try {
if (!file.exists()) {
file.createNewFile();
} else {
file.delete();
file.createNewFile();
}
fos = new FileOutputStream(file);
} catch (IOException e) {
e.printStackTrace();
}
}
@Override
public void channelRead(ChannelHandlerContext ctx, Object msg)
throws Exception {
try {
ByteBuf buf = (ByteBuf) msg;
if (buf.isReadable()) {
buf.readBytes(fos, buf.readableBytes());
fos.flush();
} else {
System.out.println("I want to close fileoutputstream!");
buf.release();
fos.flush();
fos.close();
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
修复服务器端
在Netty的世界里,有多个"events":
其中 "events",您可能已经知道 channelRead
的作用(自从您使用它以来),但您似乎需要的另一个是 channelInactive
。当另一个端点关闭连接时调用这个,你可以这样使用它:
@Override
public void channelInactive(ctx) {
System.out.println("I want to close fileoutputstream!");
fos.close();
}
@Override
public void channelRead(ChannelHandlerContext ctx, Object msg)
throws Exception {
try {
ByteBuf buf = (ByteBuf) msg;
// if (buf.isReadable()) { // a buf should always be readable here
buf.readBytes(fos, buf.readableBytes());
// fos.flush(); // flushing is always done when closing
//} else {
// System.out.println("I want to close fileoutputstream!");
// buf.release(); // Should be placed in the finally block
// fos.flush();
// fos.close();
//}
} catch (Exception e) {
e.printStackTrace();
} finally {
buf.release(); // Should always be done, even if writing to the file fails
}
}
但是,服务器如何知道连接已关闭?目前客户端并没有关闭服务器,而是让 运行 在后台永远保持连接。
修复客户端
为了正确关闭来自客户端的连接,我们需要调用 channel.close()
,但是,我们不能直接在 return 行之前插入它,因为这会导致发送数据之间的竞争条件, 并关闭网络层中的连接,可能会丢失数据。
为了正确处理这些情况,Netty 使用 Future
系统允许代码在异步操作发生后处理事件。
幸运的是,Netty 已经 has a build in solution for this, we only need to wire it up. To wire this solution up to our code, we have to keep track of the latest ChannelFuture
由 Netty 的写入方法发出。
为了正确实施此解决方案,我们将 sendToServer
更改为 return write 方法的结果:
private ChannelFuture sendToServer(byte[] bytes, Channel channel, int length)
throws IOException {
return channel.writeAndFlush(Unpooled.copiedBuffer(bytes, 0, length));
}
然后我们跟踪这个return值,并在我们想要关闭连接时添加一个包含Netty内置解决方案的监听器:
ChannelFuture lastFuture = null;
for (;;) {
byte[] bytes = new byte[readLength];
int readNum = bis.read(bytes, 0, readLength);
// System.out.println(readNum);
if (readNum == -1) {
bis.close();
fis.close();
if(lastFuture == null) { // When our file is 0 bytes long, this is true
channel.close();
} else {
lastFuture.addListener(ChannelFutureListener.CLOSE);
}
return;
}
lastFuture = sendToServer(bytes, channel, readNum);
}
我是 Netty 新手。文件传输有一个问题让我困惑了好几天。我想从客户端向服务器发送 image 文件。
下面的代码是可执行的。但是只有我关闭服务器才能正常打开接收到的图片文件。否则,它会显示“您似乎没有查看此文件的权限。请检查权限并重试”。因此,我想在 ByteBuf 中没有数据时使用 ByteBuf.isReadable() 关闭 fileoutputstream,但是 ServerHandler 中方法 channelRead 中的 else 块永远无法到达。没用。
此外,如果发送text文件,在服务器alive时可以正常打开。 我不想每次传输后都关闭服务器。请给我一些解决问题的建议。
这是 FileClientHandler
public class FileClientHandler extends ChannelInboundHandlerAdapter {
private int readLength = 8;
@Override
public void channelActive(ChannelHandlerContext ctx) throws Exception {
sendFile(ctx.channel());
}
private void sendFile(Channel channel) throws IOException {
File file = new File("C:\Users\xxx\Desktop\1.png");
FileInputStream fis = new FileInputStream(file);
BufferedInputStream bis = new BufferedInputStream(fis);
for (;;) {
byte[] bytes = new byte[readLength];
int readNum = bis.read(bytes, 0, readLength);
// System.out.println(readNum);
if (readNum == -1) {
bis.close();
fis.close();
return;
}
sendToServer(bytes, channel, readNum);
}
}
private void sendToServer(byte[] bytes, Channel channel, int length)
throws IOException {
channel.writeAndFlush(Unpooled.copiedBuffer(bytes, 0, length));
}
}
这是 FileServerHandler
public class FileServerHandler extends ChannelInboundHandlerAdapter {
private File file = new File("C:\Users\xxx\Desktop\2.png");
private FileOutputStream fos;
public FileServerHandler() {
try {
if (!file.exists()) {
file.createNewFile();
} else {
file.delete();
file.createNewFile();
}
fos = new FileOutputStream(file);
} catch (IOException e) {
e.printStackTrace();
}
}
@Override
public void channelRead(ChannelHandlerContext ctx, Object msg)
throws Exception {
try {
ByteBuf buf = (ByteBuf) msg;
if (buf.isReadable()) {
buf.readBytes(fos, buf.readableBytes());
fos.flush();
} else {
System.out.println("I want to close fileoutputstream!");
buf.release();
fos.flush();
fos.close();
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
修复服务器端
在Netty的世界里,有多个"events":
其中 "events",您可能已经知道 channelRead
的作用(自从您使用它以来),但您似乎需要的另一个是 channelInactive
。当另一个端点关闭连接时调用这个,你可以这样使用它:
@Override
public void channelInactive(ctx) {
System.out.println("I want to close fileoutputstream!");
fos.close();
}
@Override
public void channelRead(ChannelHandlerContext ctx, Object msg)
throws Exception {
try {
ByteBuf buf = (ByteBuf) msg;
// if (buf.isReadable()) { // a buf should always be readable here
buf.readBytes(fos, buf.readableBytes());
// fos.flush(); // flushing is always done when closing
//} else {
// System.out.println("I want to close fileoutputstream!");
// buf.release(); // Should be placed in the finally block
// fos.flush();
// fos.close();
//}
} catch (Exception e) {
e.printStackTrace();
} finally {
buf.release(); // Should always be done, even if writing to the file fails
}
}
但是,服务器如何知道连接已关闭?目前客户端并没有关闭服务器,而是让 运行 在后台永远保持连接。
修复客户端
为了正确关闭来自客户端的连接,我们需要调用 channel.close()
,但是,我们不能直接在 return 行之前插入它,因为这会导致发送数据之间的竞争条件, 并关闭网络层中的连接,可能会丢失数据。
为了正确处理这些情况,Netty 使用 Future
系统允许代码在异步操作发生后处理事件。
幸运的是,Netty 已经 has a build in solution for this, we only need to wire it up. To wire this solution up to our code, we have to keep track of the latest ChannelFuture
由 Netty 的写入方法发出。
为了正确实施此解决方案,我们将 sendToServer
更改为 return write 方法的结果:
private ChannelFuture sendToServer(byte[] bytes, Channel channel, int length)
throws IOException {
return channel.writeAndFlush(Unpooled.copiedBuffer(bytes, 0, length));
}
然后我们跟踪这个return值,并在我们想要关闭连接时添加一个包含Netty内置解决方案的监听器:
ChannelFuture lastFuture = null;
for (;;) {
byte[] bytes = new byte[readLength];
int readNum = bis.read(bytes, 0, readLength);
// System.out.println(readNum);
if (readNum == -1) {
bis.close();
fis.close();
if(lastFuture == null) { // When our file is 0 bytes long, this is true
channel.close();
} else {
lastFuture.addListener(ChannelFutureListener.CLOSE);
}
return;
}
lastFuture = sendToServer(bytes, channel, readNum);
}