Java 通过SocketChannel读取(写入)的正确方法
我的问题比下面的场景更一般,尽管这涵盖了所需的一切。 它适用于Java和正确的套接字编程实践 场景:Java 通过SocketChannel读取(写入)的正确方法,java,loops,nonblocking,writing,socketchannel,Java,Loops,Nonblocking,Writing,Socketchannel,我的问题比下面的场景更一般,尽管这涵盖了所需的一切。 它适用于Java和正确的套接字编程实践 场景: 一台服务器和多个客户端。非阻塞I/O的使用 服务器是另一台服务器的客户端。阻塞I/O的使用 每种情况下有两种情况:一种情况下,所有数据都适合分配的bytebuffer,另一种情况下,它们不适合(仅适用于一次迭代,而不适用于程序的生命周期) 我发现的所有非阻塞I/O示例如下所示: InetAddress host = InetAddress.getByName("localhost"); Se
- 一台服务器和多个客户端。非阻塞I/O的使用
- 服务器是另一台服务器的客户端。阻塞I/O的使用
- 每种情况下有两种情况:一种情况下,所有数据都适合分配的bytebuffer,另一种情况下,它们不适合(仅适用于一次迭代,而不适用于程序的生命周期)
InetAddress host = InetAddress.getByName("localhost");
Selector selector = Selector.open();
ServerSocketChannel serverSocketChannel = ServerSocketChannel.open();
serverSocketChannel.configureBlocking(false);
serverSocketChannel.bind(new InetSocketAddress(host, 1234));
serverSocketChannel.register(selector, SelectionKey. OP_ACCEPT);
while (true) {
if (selector.select() <= 0)
continue;
Set<SelectionKey> selectedKeys = selector.selectedKeys();
Iterator<SelectionKey> iterator = selectedKeys.iterator();
while (iterator.hasNext()) {
key = (SelectionKey) iterator.next();
iterator.remove();
if (key.isAcceptable()) {
SocketChannel socketChannel = serverSocketChannel.accept();
socketChannel.configureBlocking(false);
socketChannel.register(selector, SelectionKey.OP_READ);
// Do something or do nothing
}
if (key.isReadable()) {
SocketChannel socketChannel = (SocketChannel) key.channel();
ByteBuffer buffer = ByteBuffer.allocate(BUFFER_SIZE);
socketChannel.read(buffer);
// Something something dark side
if (result.length() <= 0) {
sc.close();
// Something else
}
}
}
InetAddress host=InetAddress.getByName(“localhost”);
选择器=选择器。打开();
ServerSocketChannel ServerSocketChannel=ServerSocketChannel.open();
serverSocketChannel.configureBlocking(false);
绑定(新的InetSocketAddress(主机,1234));
serverSocketChannel.register(选择器,SelectionKey.OP_ACCEPT);
while(true){
如果(selector.select()如果您没有调用configureBlocking(false)
,那么是的,您将使用一个循环来填充缓冲区
但是…非阻塞套接字的要点是不要挂起等待任何一个套接字,因为这会延迟从所有剩余套接字读取的数据,这些剩余套接字的选定密钥尚未被迭代器处理。实际上,如果有十个客户端连接,其中一个连接速度较慢,则其他一些或所有客户端都会失败我们也会经历同样的缓慢
(未指定所选键集的确切顺序。查看选择器实现类的源代码是不明智的,因为缺少任何顺序保证意味着允许Java SE的未来版本更改顺序。)
为了避免等待任何一个套接字,您不会尝试一次性填满缓冲区;相反,您可以通过每次select()
调用只读取一次套接字提供给您的内容而不阻塞
由于每个ByteBuffer可能包含一部分数据序列,因此您需要记住每个套接字的每个ByteBuffer的进度
您还需要记住从每个套接字读取的字节数。因此,现在您需要记住每个套接字的两件事:字节计数和字节缓冲
class ReadState {
final ByteBuffer buffer = ByteBuffer.allocate(BUFFER_SIZE);
long count;
}
while (true) {
// ...
if (key.isAcceptable()) {
SocketChannel socketChannel = serverSocketChannel.accept();
socketChannel.configureBlocking(false);
// Attach the read state for this socket
// to its corresponding key.
socketChannel.register(selector, SelectionKey.OP_READ,
new ReadState());
}
if (key.isReadable()) {
SocketChannel socketChannel = (SocketChannel) key.channel();
ReadState state = (ReadState) key.attachment();
ByteBuffer buffer = state.buffer;
state.count += socketChannel.read(buffer);
if (state.count >= DATA_LENGTH) {
socketChannel.close();
}
buffer.flip();
// Caution: The speed of this connection will limit your ability
// to process the remaining selected keys!
anotherServerChannel.write(buffer);
}
对于阻塞通道,您可以只使用一个写入(缓冲区)
调用,但正如您所看到的,使用阻塞通道可能会限制主服务器使用非阻塞通道的优势。将与其他服务器的连接设置为非阻塞通道也可能是值得的。这将使事情更加复杂,因此除非您要我这样做,否则我不会在这里讨论它。我知道什么是非阻塞通道区别和好处(阻塞/非阻塞),我只是问每种情况下读写的正确方式。缓冲区也可以很大,适合所有情况,或者不适合。我想要2(读/写)*2(阻塞/非阻塞)*2(缓冲区足够大或不够大)=8种不同情况。我需要循环吗(单读/单写,例如获取一个客户端请求/发送一个服务器请求)?我试图回答这个问题。答案是否定的,你不应该使用循环。这样做会破坏非阻塞套接字通道的好处。阻塞写入不需要循环,因为