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