有一个api我需要执行没有长度的八位字节流。它只是一个实时数据流。我遇到的问题是,当我提出请求时,它似乎试图在将信息读入输入流之前等待内容的结束,但是它没有看到内容的结束和超时与NoHttpResponse异常。以下是我的代码的简化版本:
private static HttpPost getPostRequest() {
// Build uri
URI uri = new URIBuilder()
.setScheme("https")
.setHost(entity.getStreamUrl())
.setPath("/")
.build();
// Create http http
HttpPost httpPost = new HttpPost(uri);
String nvpsStr = "";
Object myArray[] = nvps.toArray();
for(int i = 0; i < myArray.length; i ++) {
nvpsStr += myArray[i].toString();
if(i < myArray.length - 1) {
nvpsStr += "&";
}
}
// Build http payload
String request = nvpsStr + scv + streamRequest + "\n\n";
// Attach http data
httpPost.setEntity(new StringEntity(URLEncoder.encode(request,"UTF-8")));
return httpPost;
}
// Where client is simply
// private static final CloseableHttpClient client = HttpClients.createDefault();
private static runPostRequest (HttpPost request) {
CloseableHttpResponse response = client.execute(request);
try {
HttpEntity ent = response.getEntity();
InputStream is = ent.getContent();
DataInputStream dis = new DataInputStream(is);
// Only stream the first 200 bytes
for(int i = 0; i < 200; i++) {
System.out.println(( (char)dis.readByte()));
}
} finally {
response.close();
}
}
答案 0 :(得分:7)
编辑2
所以,如果你对线程/ runnables / Handler不熟悉并且不熟悉android AsyncTask,我会直接使用HttpUrlConnection(使用apacheHttpClient删除整个练习因为,基本上googl说HttpUrlConn将支持stream'd响应和它确实有效!)
对于转储标题等所有细节可能并不那么容易。但是,使用正常的流式响应对象,我认为它应该正常工作....请参阅编辑3以获取UrlConn代码示例
<强> EndEdit2 强>
不清楚使用什么'stream'协议(渐进式dwnld | http流)或者你如何实际管理客户端的流式响应。
建议从连接中转储标头,以确切了解客户端和服务器之间的协议?
假设您关闭了UI线程(在asyncTask中或在Handler的回调部分中),因此您可能需要重构一点。
假设使用apache client 4.3.5+
的http流如果响应的标题中没有长度,那么你在Http 1.1上做了一个'chunked'响应,你必须读取一个缓冲区直到你得到一个'last-chunk'或者决定关闭这个流或连接:
服务器刚刚开始发送(流),客户端应该根据生成实体内容的详细apache说明,通过使用缓冲区来处理它从http响应中获取的'input-stream'。
如果30秒的套接字超时会抢占活动流,我不记得了吗?请记住,在apache中,构建器中存在用于套接字超时和READ超时的单独设置。不希望socket关闭,并且不希望在服务器提供响应时等待可读流的可用字节超时。
无论如何,客户端处理程序只需要通过检查读入缓冲区的内容来了解流的结束方式......
如果适当的协议是“继续”&amp; “chunked”然后客户端上的响应处理程序应该在流处理程序循环中UNTIL它从http spec看到LAST-CHUNK。
response.getEntity().getContent()
应该为您提供处理响应流所需的引用,直到'last-chunk'...
我认为你应该read here关于如何使用一个缓冲的实体,其中需要多个读取才能在响应中的'last-chunk'结束。它是HttpUrlConn可能更容易的另一个原因......
执行一个处理缓冲读取的循环,直到由匹配'last-chunk'的字节表示的END。
然后根据有关消费实体和可重用连接的详细apache说明CLO流或CONN。
编辑 apache HttpClient中流式响应的代码
在'handler's callback或asyncTask
中 request.execute();
...
processStreamingEntity(response.getEntity());
response.close();
//implement your own wrapper as mentioned in apache docs
private void processStreamingEntity(HttpEntity entity) throws IOException {
InputStreamHttpEntityHC4 bufHttpEntity = new InputStreamHttpEntityHC4(entity);
while not bufHttpEntity.LAST_CHUNK {
handleResponse(bufHttpEntity.readLine())
}
编辑3
httpUrlConnection版本,如果你这样做。 (使用MessageHandler,但你可以使用这些字节,因为这是来自流式语音示例,文本中的单词将被发送回UI)
private void openHttpsConnection(String urlStr, Handler mhandler) throws IOException {
HttpsURLConnection httpConn = null;
String line = null;
try {
URL url = new URL(urlStr);
URLConnection urlConn = url.openConnection();
if (!(urlConn instanceof HttpsURLConnection)) {
throw new IOException ("URL is not an Https URL");
}
httpConn = (HttpsURLConnection)urlConn;
httpConn.setAllowUserInteraction(false);
httpConn.setInstanceFollowRedirects(true);
httpConn.setRequestMethod("GET");
httpConn.setReadTimeout(50 * 1000);
BufferedReader is =
new BufferedReader(new InputStreamReader(httpConn.getInputStream()));
while ((line = is.readLine( )) != null) {
Message msg = Message.obtain();
msg.what=1;
msg.obj=line;
mhandler.sendMessage(msg);
}
} catch (MalformedURLException e) {
e.printStackTrace();
} catch( SocketTimeoutException e){
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
Message msg = Message.obtain();
msg.what=2;
BufferedInputStream in = new BufferedInputStream(httpConn.getErrorStream());
line =new String(readStream(in));
msg.obj=line;
mhandler.sendMessage(msg);
}
finally {httpConn.disconnect();}
}
答案 1 :(得分:0)
尝试RxSON:https://github.com/rxson/rxson 它利用JsonPath和RxJava尽快从响应中读取JSON流数据块,并在响应完成之前将其解析为java对象。
示例:
String serviceURL = "https://think.cs.vt.edu/corgis/datasets/json/airlines/airlines.json";
HttpRequest req = HttpRequest.newBuilder(URI.create(serviceURL)).GET().build();
RxSON rxson = new RxSON.Builder().build();
String jsonPath = "$[*].Airport.Name";
Flowable<String> airportStream = rxson.create(String.class, req, jsonPath);
airportStream
.doOnNext(it -> System.out.println("Received new item: " + it))
//Just for test
.toList()
.blockingGet();