面试题答案
一键面试-
实现思路:
- 创建一个合适大小的ByteBuffer来存储从SocketChannel读取的数据。由于数据量不确定,初始可以设置一个合理的默认大小,如1024字节,并且在需要时动态扩展。
- 使用
SocketChannel.read(ByteBuffer)
方法进行非阻塞读取。该方法返回值表示读取的字节数,若返回 -1 表示通道已到达流的末尾,即数据读取完毕。 - 采用循环读取的方式,直到
read
方法返回0,表明当前缓冲区已满或暂时没有更多数据可读,这样可以确保在一次操作中尽可能多地读取数据。 - 处理完当前缓冲区的数据后,需要将ByteBuffer的position置为0,limit设为capacity,以便下次读取时能正确覆盖原有数据。
- 当检测到数据量超过当前缓冲区大小时,需要创建一个更大的缓冲区,并将原有缓冲区的数据复制到新缓冲区。
-
关键代码:
import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.SocketChannel;
public class NonBlockingSocketReadExample {
public static void main(String[] args) {
try (SocketChannel socketChannel = SocketChannel.open()) {
socketChannel.connect(new InetSocketAddress("example.com", 80));
socketChannel.configureBlocking(false);
// 初始缓冲区大小
int initialBufferSize = 1024;
ByteBuffer buffer = ByteBuffer.allocate(initialBufferSize);
while (true) {
int bytesRead = socketChannel.read(buffer);
if (bytesRead == -1) {
// 流结束,处理缓冲区数据
handleBuffer(buffer);
break;
} else if (bytesRead > 0) {
buffer.flip();
// 处理读取到的数据
handleBuffer(buffer);
buffer.clear();
} else {
// 没有更多数据可读,可做其他处理,如等待一段时间后重试
// 这里简单休眠100毫秒
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
} catch (IOException e) {
e.printStackTrace();
}
}
private static void handleBuffer(ByteBuffer buffer) {
byte[] data = new byte[buffer.remaining()];
buffer.get(data);
System.out.println("Received data: " + new String(data));
}
}
在上述代码中:
socketChannel.configureBlocking(false)
将SocketChannel设置为非阻塞模式。socketChannel.read(buffer)
进行非阻塞读取,返回读取的字节数。buffer.flip()
方法准备从缓冲区读取数据,将position设为0,limit设为当前position。handleBuffer(buffer)
方法处理读取到的数据,在实际应用中,这里可以进行更复杂的数据处理,如解析协议等。buffer.clear()
方法为下一次读取准备缓冲区,将position设为0,limit设为capacity。
如果要处理缓冲区动态扩展,可以按以下方式修改代码:
import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.SocketChannel;
public class NonBlockingSocketReadWithBufferExpand {
public static void main(String[] args) {
try (SocketChannel socketChannel = SocketChannel.open()) {
socketChannel.connect(new InetSocketAddress("example.com", 80));
socketChannel.configureBlocking(false);
// 初始缓冲区大小
int initialBufferSize = 1024;
ByteBuffer buffer = ByteBuffer.allocate(initialBufferSize);
while (true) {
int bytesRead = socketChannel.read(buffer);
if (bytesRead == -1) {
// 流结束,处理缓冲区数据
handleBuffer(buffer);
break;
} else if (bytesRead > 0) {
if (buffer.remaining() < bytesRead) {
// 缓冲区不足,扩展缓冲区
ByteBuffer newBuffer = ByteBuffer.allocate(buffer.capacity() * 2);
buffer.flip();
newBuffer.put(buffer);
buffer = newBuffer;
buffer.put(new byte[bytesRead]);
} else {
buffer.flip();
}
// 处理读取到的数据
handleBuffer(buffer);
buffer.clear();
} else {
// 没有更多数据可读,可做其他处理,如等待一段时间后重试
// 这里简单休眠100毫秒
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
} catch (IOException e) {
e.printStackTrace();
}
}
private static void handleBuffer(ByteBuffer buffer) {
byte[] data = new byte[buffer.remaining()];
buffer.get(data);
System.out.println("Received data: " + new String(data));
}
}
在这个修改后的代码中,当buffer.remaining() < bytesRead
时,即当前缓冲区剩余空间不足以容纳本次读取的数据,就创建一个更大的缓冲区(容量翻倍),并将原有数据复制到新缓冲区,以确保数据的完整性。