Warning: file_get_contents(/data/phpspider/zhask/data//catemap/9/java/368.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Java 如何使用异步socketchannel#循环读取或递归读取?_Java_Nio2 - Fatal编程技术网

Java 如何使用异步socketchannel#循环读取或递归读取?

Java 如何使用异步socketchannel#循环读取或递归读取?,java,nio2,Java,Nio2,我找到了一个示例,但它并没有特别有用,因为它没有提供完整的示例 问题:如何使用固定大小的缓冲区读取未知长度的数据 第一次尝试(读取一次): final int bufferSize=1024; 最终SocketAddress地址=/*ip:port*/; final ThreadFactory ThreadFactory=Executors.defaultThreadFactory(); final ExecutorService executor=Executors.newCachedThre

我找到了一个示例,但它并没有特别有用,因为它没有提供完整的示例

问题:如何使用固定大小的缓冲区读取未知长度的数据

第一次尝试(读取一次):

final int bufferSize=1024;
最终SocketAddress地址=/*ip:port*/;
final ThreadFactory ThreadFactory=Executors.defaultThreadFactory();
final ExecutorService executor=Executors.newCachedThreadPool(threadFactory);
final AsynchronousChannelGroup AsynchronousChannelGroup=AsynchronousChannelGroup.withCachedThreadPool(执行器,5);
最终AsynchronousSocketChannel客户端=AsynchronousSocketChannel.open(AsynchronousChannelGroup);
client.connect(address).get(5,TimeUnit.SECONDS)//阻塞,直到建立连接
//写下请求
Integer BytesWrite=client.write(StandardCharsets.US_ASCII.encode(“二进制格式的自定义请求”)).get();
//阅读回复
final ByteBuffer readTo=ByteBuffer.allocate(缓冲区大小);
最终StringBuilder responseBuilder=新StringBuilder();
read(readTo,readTo,new CompletionHandler(){
公共无效已完成(整数字节读取,字节缓冲区){
flip();
append(StandardCharsets.US_ASCII.decode(buffer.toString());
试一试{
client.close();
}捕获(IOE异常){
e、 printStackTrace();
}
}
公共作废失败(可丢弃的exc、ByteBuffer附件){
试一试{
client.close();
}捕获(IOE异常){
e、 printStackTrace();
}
}
}
);
asyncChannelGroup.awaitTermination(5,时间单位秒);
asyncChannelGroup.shutdown();
System.out.println(responseBuilder.toString());

bytesRead!=-1
(即到达流的末尾)?

在我看来,最简单的方法是将此代码拆分为自己的方法,然后让CompletionHandler在
字节读取时递归调用该方法!=-1
。这样,您可以分离代码的责任,避免在异步读取运行时“忙等待”或使用Thread.sleep()的必要性。当然,您也可以添加一个case when
bytesRead==-1
来处理读入的数据。

由于回调方法
失败
完成
在不受您控制的线程上运行,我不会做任何延长的事情

我知道,即使流已到达其结尾(
bytesRead==-1
),您也希望继续侦听套接字中的新字节。 将
read
方法置于
while(true)
循环中。在它里面,收听由
失败
完成
方法设置的
同步
字段。让我们称之为
myBytesRead

为了能够停止无休止的读取,请将
while(true)
替换为其他一些
synchronized
条件

private static final BYTES_READ_INIT_VALUE = Integer.MIN_VALUE;
private static final BYTES_READ_COMPLETED_VALUE = -1;
private static final BYTES_READ_FAILED_VALUE = -2;
private Integer myBytesRead = BYTES_READ_INIT_VALUE;

private void setMyBytesRead(final Integer bytesRead) {
    synchronized(myBytesRead) {
        this.myBytesRead = bytesRead;
    }
}

private Integer getMyBytesRead() {
    synchronized(myBytesRead) {
        return myBytesRead;
    }
}

...

// in your method
while (true) {
    final int lastBytesRead = getMyBytesRead();
    if (lastBytesRead == BYTES_READ_FAILED_VALUE) {
        // log failure and retry?
    } else if (lastBytesRead != BYTES_READ_COMPLETED_VALUE) {
        // Thread.sleep( a while ); to avoid exhausting CPU
        continue;
    }

    // else lastBytesRead == BYTES_READ_COMPLETED_VALUE and you can start a new read operation
    client.read(readTo, readTo, new CompletionHandler<Integer, ByteBuffer>() {
            public void completed(Integer bytesRead, ByteBuffer buffer) {
                setMyBytesRead(bytesRead);
                buffer.flip();
                responseBuilder.append(StandardCharsets.US_ASCII.decode(buffer).toString());
                try {
                    client.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }

            public void failed(Throwable exc, ByteBuffer attachment) {
                try {
                    setMyBytesRead(BYTES_READ_FAILED_VALUE);
                    client.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    );
}
private static final BYTES\u READ\u INIT\u VALUE=Integer.MIN\u VALUE;
私有静态最终字节\u读取\u完成\u值=-1;
私有静态最终字节\u读取\u失败\u值=-2;
私有整数myBytesRead=BYTES\u READ\u INIT\u值;
私有void setMyBytesRead(最终整数bytesRead){
已同步(myBytesRead){
this.myBytesRead=bytesRead;
}
}
私有整数getMyBytesRead(){
已同步(myBytesRead){
返回myBytesRead;
}
}
...
//用你的方法
while(true){
final int lastBytesRead=getMyBytesRead();
if(lastBytesRead==字节\u读取\u失败\u值){
//记录失败并重试?
}else if(lastBytesRead!=字节\u读取\u完成\u值){
//睡眠(一段时间);避免耗尽CPU
继续;
}
//else lastBytesRead==BYTES\u READ\u COMPLETED\u值,您可以启动新的读取操作
read(readTo,readTo,new CompletionHandler(){
公共无效已完成(整数字节读取,字节缓冲区){
设置我的字节读(字节读);
flip();
append(StandardCharsets.US_ASCII.decode(buffer.toString());
试一试{
client.close();
}捕获(IOE异常){
e、 printStackTrace();
}
}
公共作废失败(可丢弃的exc、ByteBuffer附件){
试一试{
setMyBytesRead(字节\读取\失败\值);
client.close();
}捕获(IOE异常){
e、 printStackTrace();
}
}
}
);
}
我的初次尝试:

package com.example;

import java.nio.ByteBuffer;
import java.nio.channels.AsynchronousSocketChannel;
import java.nio.charset.StandardCharsets;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;

public class LoopingReader implements Callable<String> {
    final AsynchronousSocketChannel client;
    final String responseTerminator;
    final StringBuilder responseBuilder;

    LoopingReader(
        AsynchronousSocketChannel client,
        String responseTerminator
    ) {
        this.client = client;
        this.responseTerminator = responseTerminator;

        responseBuilder = new StringBuilder();
    }

    public String call() {
        boolean doLoop;
        do {
            int bytesRead = executeIteration();//blocking
            boolean didReachEndOfStream = bytesRead == -1;
            boolean didEncounterResponseTerminator = responseBuilder.indexOf(responseTerminator) != -1;

            doLoop =  !didReachEndOfStream && !didEncounterResponseTerminator;
        } while(doLoop);
        return responseBuilder.toString();
    }

    int executeIteration() {
        final ByteBuffer buffer = ByteBuffer.allocate(256);//use pool here
        final int bytesRead;
        try {
            bytesRead = client.read(buffer).get();
        } catch (InterruptedException | ExecutionException e) {
            throw new IllegalStateException("Failed to read", e);
        }
        decodeAndAppend(buffer);
        return bytesRead;
    }

    void decodeAndAppend(ByteBuffer buffer) {
        buffer.flip();
        responseBuilder.append(StandardCharsets.US_ASCII.decode(buffer));
    }
}
package.com.example;
导入java.nio.ByteBuffer;
导入java.nio.channels.AsynchronousSocketChannel;
导入java.nio.charset.StandardCharset;
导入java.util.concurrent.Callable;
导入java.util.concurrent.ExecutionException;
公共类LoopingReader实现了可调用{
最终异步socketchannel客户端;
最终串响应器;
最终StringBuilder responseBuilder;
循环读取器(
异步socketchannel客户端,
串响应器
) {
this.client=client;
this.responseTerminator=responseTerminator;
responseBuilder=新的StringBuilder();
}
公共字符串调用(){
布尔doLoop;
做{
int bytesRead=执行操作();//阻塞
布尔DIDRACHENDOFSTREAM=字节读取==-1;
布尔值didEncounterResponseTerminator=responseBuilder.indexOf(responseTerminator)!=1;
doLoop=!DidReachenOfStream&!didEncounterResponseTerminator;
}while(doLoop);
返回responseBuilder.toString();
package com.example;

import java.nio.ByteBuffer;
import java.nio.channels.AsynchronousSocketChannel;
import java.nio.charset.StandardCharsets;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;

public class LoopingReader implements Callable<String> {
    final AsynchronousSocketChannel client;
    final String responseTerminator;
    final StringBuilder responseBuilder;

    LoopingReader(
        AsynchronousSocketChannel client,
        String responseTerminator
    ) {
        this.client = client;
        this.responseTerminator = responseTerminator;

        responseBuilder = new StringBuilder();
    }

    public String call() {
        boolean doLoop;
        do {
            int bytesRead = executeIteration();//blocking
            boolean didReachEndOfStream = bytesRead == -1;
            boolean didEncounterResponseTerminator = responseBuilder.indexOf(responseTerminator) != -1;

            doLoop =  !didReachEndOfStream && !didEncounterResponseTerminator;
        } while(doLoop);
        return responseBuilder.toString();
    }

    int executeIteration() {
        final ByteBuffer buffer = ByteBuffer.allocate(256);//use pool here
        final int bytesRead;
        try {
            bytesRead = client.read(buffer).get();
        } catch (InterruptedException | ExecutionException e) {
            throw new IllegalStateException("Failed to read", e);
        }
        decodeAndAppend(buffer);
        return bytesRead;
    }

    void decodeAndAppend(ByteBuffer buffer) {
        buffer.flip();
        responseBuilder.append(StandardCharsets.US_ASCII.decode(buffer));
    }
}
class SomeUtilClass {
public interface Processor<T> {
    boolean process(Integer byteCount, ByteBuffer buffer);
    T result();
}
public static <T> ListenableFuture<T> read(
    final AsynchronousSocketChannel delegate,
    final Processor<T> processor,
    ByteBuffer buffer
) {
    final SettableFuture<T> resultFuture = SettableFuture.create();
    delegate.read(buffer, buffer, new Handler<T, Integer, ByteBuffer>(resultFuture) {
        public void completed(Integer bytesRead, ByteBuffer buffer) {
            buffer.flip();
            if(processor.process(bytesRead, buffer)) {
                buffer.clear();
                delegate.read(buffer, buffer, this);
            } else {
                resultFuture.set(processor.result());
            }
        }
    });
    return resultFuture;
}
}