java nio SocketChannel.read不返回-1以指示流结束

java nio SocketChannel.read不返回-1以指示流结束,java,sockets,selector,nio,nio2,Java,Sockets,Selector,Nio,Nio2,我正在编写一个代码,它使用NIO/Selector进行web抓取。 它起作用了。我确实获得了OP_CONNECT,然后我发送了get请求,并返回了整个html页面。 但是,在那之后,我没有得到一个-1知道它完成了。我明白了,这意味着整个页面都已发送,但SocketChannel.read不会返回-1以指示流的结束。 非常感谢您的帮助 以下是完整的示例代码: import java.io.ByteArrayOutputStream; import java.io.IOException; impo

我正在编写一个代码,它使用NIO/Selector进行web抓取。 它起作用了。我确实获得了OP_CONNECT,然后我发送了get请求,并返回了整个html页面。 但是,在那之后,我没有得到一个-1知道它完成了。我明白了,这意味着整个页面都已发送,但SocketChannel.read不会返回-1以指示流的结束。 非常感谢您的帮助

以下是完整的示例代码:

import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.net.InetSocketAddress;
import java.net.MalformedURLException;
import java.net.StandardSocketOptions;
import java.net.URL;
import java.nio.ByteBuffer;
import java.nio.channels.SelectionKey;
import java.nio.channels.Selector;
import java.nio.channels.SocketChannel;
import java.util.Iterator;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class HttpClientTest {
    private static final Logger logger = LoggerFactory.getLogger(HttpClientTest.class);
    private static final String BASE_URL_STR = "https://www.youtube.com/channel";
    private static final String CHANNEL_ID = "UCDm6kPZFCoT7altG4WNGy-A";

    private final ByteArrayOutputStream baHtmlPage = new ByteArrayOutputStream();
    private final ByteBuffer buffer = ByteBuffer.allocate(128 * 1024);

    private String htmlPage = null;

    private void startHttpClient() throws InterruptedException {


        // open Selector and ServerSocketChannel by calling the open() method
        try (Selector selector = Selector.open();
                SocketChannel socketChannel = SocketChannel.open()) {

            // check that both of them were successfully opened
            if ((socketChannel.isOpen()) && (selector.isOpen())) {

                // configure non-blocking mode
                socketChannel.configureBlocking(false);
                socketChannel.setOption(StandardSocketOptions.SO_RCVBUF,
                        128 * 1024);
                socketChannel.setOption(StandardSocketOptions.SO_SNDBUF,
                        128 * 1024);
                socketChannel.setOption(StandardSocketOptions.SO_KEEPALIVE,
                        true);
                //socketChannel.setOption(StandardSocketOptions.TCP_NODELAY,
                //      true);

                //socketChannel.connect(new InetSocketAddress(IP, DEFAULT_PORT));
                socketChannel.connect(createSocketAddress(CHANNEL_ID));

                // register the current channel with the given selector
                socketChannel.register(selector, SelectionKey.OP_CONNECT);


                while (true) {
                    // wait for incomming events
                    int num = selector.selectNow();
                    if (num==0) {
                        //Thread.yield();
                        Thread.sleep(2000);
                        System.out.println("sleep: 2 sec");
                        continue;
                    }


                    // there is something to process on selected keys
                    Iterator<SelectionKey> keys = selector.selectedKeys().iterator();
                    while (keys.hasNext()) {
                        SelectionKey key = (SelectionKey) keys.next();

                        // prevent the same key from coming up again
                        keys.remove();

                        if (!key.isValid()) {
                            continue;
                        }

                        if (key.isConnectable() && socketChannel.finishConnect()) {
                            System.out.println("Key: OP_CONNECT");
                            // reset the byte-array
                            baHtmlPage.reset();

                            // Connected --> Send the HTTP request 
                            key.interestOps(SelectionKey.OP_WRITE);

                        } else if (key.isReadable()) {
                            System.out.println("Key: OP_READ");
                            if (readResponse(key)) {
                                logger.info("finished reading, htmlpage:{}", htmlPage);
                            } else {
                                key.interestOps(SelectionKey.OP_READ);
                            }

                            // Once read is done --> we are done
                            //key.interestOps(SelectionKey.OP_WRITE);

                        } else if (key.isWritable()) {
                            System.out.println("Key: OP_WRITE");
                            if (writeHttpRequest(key)) {                            
                                // HTTP request is sent --> Get the response
                                key.interestOps(SelectionKey.OP_READ);
                            }
                        }
                    }

                }
            } else { // if ((serverSocketChannel.isOpen()) && (selector.isOpen())) {
                System.out
                        .println("The server socket channel or selector cannot be opened!");
            }
        } catch (IOException ex) {
            System.err.println(ex);
        }
    }

    private static InetSocketAddress createSocketAddress(String channelID) throws MalformedURLException {
        //String urlStr = BASE_URL_STR + "/" + CHANNEL_ID;  
        String urlStr = "http://www.google.com";  

        URL url = new URL(urlStr);
        String host = url.getHost();  
        int port = url.getPort();  
        if (port == -1) 
            port = 80;

        return new InetSocketAddress(host, port);
    }

    private boolean readResponse(SelectionKey key) throws IOException {
        boolean done = false;
        SocketChannel socketChannel = (SocketChannel) key.channel();

        int numRead = -1;
        do {
            buffer.clear();
            numRead = socketChannel.read(buffer);

            baHtmlPage.write(buffer.array(), 0, numRead);
            System.out.println("Server sent:" + new String(buffer.array(), 0, numRead, "UTF-8") );
        } while(numRead>0);

        if (numRead == -1) {
            System.out.println("Connection closed by: " + socketChannel.getRemoteAddress());
            key.cancel();
            socketChannel.close();
            htmlPage = baHtmlPage.toString("UTF-8");
            done = true;
        }
        return done;
    }

    private boolean writeHttpRequest(SelectionKey key) throws IOException {
        boolean done = false;

        SocketChannel socketChannel = (SocketChannel) key.channel();
        String request = 
                "GET /channel/UCDm6kPZFCoT7altG4WNGy-A HTTP/1.1\r\n" + 
                "Host: www.youtube.com\r\n" +
                "Cache-Control: no-cache\r\n\r\n"; 

        // ISO-8859-1
        ByteBuffer randomBuffer = ByteBuffer.wrap(request.getBytes("UTF-8"));
        int rem = randomBuffer.remaining();
        int num = socketChannel.write(randomBuffer);

        if (rem==num) {
            done = true;
            System.out.printf("Request written:%s\n", request);
        }
        return done;
    }

//  private void doEchoJob(SelectionKey key, byte[] data) {
//
//      SocketChannel socketChannel = (SocketChannel) key.channel();
//      List<byte[]> channelData = keepDataTrack.get(socketChannel);
//      channelData.add(data);
//
//      key.interestOps(SelectionKey.OP_WRITE);
//  }

    public static void main(String[] args) throws InterruptedException {
        HttpClientTest client = new HttpClientTest();
        client.startHttpClient();
    }
}
import java.io.ByteArrayOutputStream;
导入java.io.IOException;
导入java.net.InetSocketAddress;
导入java.net.MalformedURLException;
导入java.net.StandardSocketOptions;
导入java.net.URL;
导入java.nio.ByteBuffer;
导入java.nio.channels.SelectionKey;
导入java.nio.channels.Selector;
导入java.nio.channels.SocketChannel;
导入java.util.Iterator;
导入org.slf4j.Logger;
导入org.slf4j.LoggerFactory;
公共类HttpClientTest{
私有静态最终记录器Logger=LoggerFactory.getLogger(HttpClientTest.class);
私有静态最终字符串BASE\u URL\u STR=”https://www.youtube.com/channel";
专用静态最终字符串通道_ID=“UCDm6kPZFCoT7altG4WNGy-A”;
私有最终ByteArrayOutputStream baHtmlPage=新ByteArrayOutputStream();
私有最终ByteBuffer缓冲区=ByteBuffer.allocate(128*1024);
私有字符串htmlPage=null;
私有void startHttpClient()引发InterruptedException{
//通过调用open()方法打开选择器和ServerSocketChannel
try(Selector=Selector.open();
SocketChannel SocketChannel=SocketChannel.open()){
//检查它们是否都已成功打开
if((socketChannel.isOpen())和&(selector.isOpen()){
//配置非阻塞模式
socketChannel.configureBlocking(假);
socketChannel.setOption(标准socketoptions.SO_RCVBUF,
128 * 1024);
socketChannel.setOption(标准socketoptions.SO_SNDBUF,
128 * 1024);
socketChannel.setOption(标准socketoptions.SO_KEEPALIVE,
正确的);
//socketChannel.setOption(标准socketoptions.TCP_节点延迟,
//正确的);
//connect(新的InetSocketAddress(IP,默认_端口));
连接(createSocketAddress(通道ID));
//使用给定选择器注册当前通道
socketChannel.寄存器(选择器、选择键、操作连接);
while(true){
//等待输入事件
int num=selector.selectNow();
如果(num==0){
//螺纹屈服强度();
《睡眠》(2000年);
System.out.println(“睡眠:2秒”);
继续;
}
//在选定的关键点上有一些要处理的内容
迭代器键=选择器。selectedKeys()。迭代器();
while(keys.hasNext()){
SelectionKey=(SelectionKey)keys.next();
//防止同一钥匙再次出现
键。移除();
如果(!key.isValid()){
继续;
}
if(key.isConnectable()&&socketChannel.finishConnect()){
System.out.println(“键:OP_CONNECT”);
//重置字节数组
baHtmlPage.reset();
//已连接-->发送HTTP请求
key.interesttops(选择key.OP_WRITE);
}else if(key.isReadable()){
System.out.println(“键:OP_READ”);
if(读取响应(键)){
info(“已完成读取,htmlpage:{}”,htmlpage);
}否则{
key.interesttops(选择key.OP_READ);
}
//一旦读取完成-->我们就完成了
//key.interesttops(选择key.OP_WRITE);
}else if(key.isWritable()){
System.out.println(“Key:OP_WRITE”);
if(writeHttpRequest(key)){
//HTTP请求已发送-->获取响应
key.interesttops(选择key.OP_READ);
}
}
}
}
}else{//if((serverSocketChannel.isOpen())和&(selector.isOpen()){
系统输出
.println(“无法打开服务器套接字通道或选择器!”);
}
}捕获(IOEX异常){
系统错误打印项次(ex);
}
}
私有静态InetSocketAddress createSocketAddress(字符串channelID)引发畸形异常{
//字符串urlStr=BASE\u URL\u STR+“/”+通道ID;
字符串urlStr=”http://www.google.com";  
URL=新URL(urlStr);
String host=url.getHost();
int-port=url.getPort();
如果(端口==-1)
端口=80;
返回新的InetSocketAddress(主机、端口);
}
私有布尔读取响应(SelectionKey)引发IOException{
布尔完成=假;
SocketChannel SocketChannel=(SocketChannel)key.channel();
int numRead=-1;
做{
buffer.clear();
numRead=socketChannel.read(缓冲区);
baHtmlPage.write(buffer.array(),0,numRead);
System.out.println(“服务器已发送:”+新字符串(buffer.arr