问题描述
我需要一个 api 来执行没有长度的八位字节流.它只是一个实时数据流.我遇到的问题是,当我提出请求时,它似乎在将信息读入输入流之前尝试等待内容结束,但是它没有看到内容结束和 NoHttpResponse 异常超时.以下是我的代码的简化版本:
There's an api I need to perform octet-streaming from which does not have a length. It is just a stream of real time data. The issue that I'm having is that when I make my request, it seems to try to wait out for the end of the content before reading information into the inputstream, however it's not seeing the end of the content and timingout with NoHttpResponse exception. Below is a simplified version of my code:
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();
}
}
推荐答案
EDIT 2
因此,如果您对线程/可运行程序/处理程序不满意并且对 android AsyncTask 不满意,我会直接转到 HttpUrlConnection(使用 Apache HttpClient 放弃整个练习,因为基本上 Google 说 HttpUrlConnection 将支持流式响应它确实有效!)
So, if you're not comfortable with threads/runnables/Handlers and not comfortable with android AsyncTask, I would just go straight to HttpUrlConnection (drop the entire excercise with Apache HttpClient because, basically Google says that HttpUrlConnection will support streamed response and it does work!)
检测所有细节(如转储标头)可能并不容易.但是对于普通的流式响应对象,我认为它应该可以正常工作......请参阅 HttpsUrlConnection 代码示例的编辑 3
It may not be as easy instrumenting all the details like dumping headers. But with a normal streamed response Object, I think that it should just work.... see edit 3 for HttpsUrlConnection code sample
EndEdit2
不清楚正在使用什么流"协议(渐进式下载或 HTTP 流)或您实际上如何管理客户端上的流响应.
Not clear from the question what 'stream' protocol is being used (progressive download or HTTP streaming) OR how you are actually managing the streamed response on your client.
建议从连接中转储标头以准确查看客户端和服务器同意的内容??
Recommended to dump the headers from the connection to see exactly what the client and server are agreeing on??
我假设您已关闭 UI 线程(在 AsyncTask 中或在处理程序的回调部分中);如果这不准确,您可能需要稍微重构一下.
I'm assuming that you are OFF the UI thread (either in AsyncTask or in the callback portion of a Handler); if that's not accurate you may have to refactor a little bit.
假设 HTTP 流与 Apache HttpClient 4.3.5+ 一起使用
Assuming HTTP stream in use with Apache HttpClient 4.3.5+
如果响应的标头中没有长度,那么您正在 HTTP 1.1 上执行分块"响应,您必须读取缓冲区,直到获得最后一个块"或决定关闭流或连接:
If there is no length in the headers of the response, then you are doing a 'chunked' response on HTTP 1.1 where you have to read a buffer until you get a 'last-chunk' or decide to CLOSE either the stream or the Connection:
服务器刚刚开始发送(流式传输),客户端应根据关于生成实体内容的详细 Apache 说明使用缓冲区来处理它从 HTTP 响应中获取的输入流".
The server just starts sending (streaming) and the client should handle the 'input-stream' that it gets from the HTTP response by employing a buffer as per the detailed Apache notes on producing entity content.
我不记得 30 秒的套接字超时是否会抢占活动流?请记住,在 Apache 中,构建器中存在单独的套接字超时设置和读取超时设置.不希望套接字在您身上关闭,也不希望在服务器提供响应时超时等待可读流的可用字节.
I don't remember offhand if socket timeout of 30 seconds will pre-empt an active stream? Remember in Apache, separate settings exist in the builder for socket timeout, and read timeout. Don't want socket to close on you and don't want to timeout waiting on a readable stream's available bytes while server is providing the response.
无论如何,客户端处理程序只需要通过检查读入缓冲区的内容来了解流如何结束...
Anyway, the client-side handler just needs to be aware of how the stream ends by inspection of what's read into the buffer...
如果适当的协议是继续"&分块"然后客户端上的响应处理程序应该处于流处理程序循环中,直到它看到来自 http 规范.
If the protocol in place is "continue" & "chunked" then the response handler on the client should be in a stream handler loop until it sees the LAST-CHUNK from the http spec.
response.getEntity().getContent()
应该为您提供处理响应流所需的参考,直到last-chunk"...
should give you the reference you need to process the response's stream until 'last-chunk'...
我认为你应该在这里阅读 关于如何使用缓冲实体,其中需要不止一次读取才能在响应中的最后一个块"处结束.这是 HttpURLConnection 可能更容易的另一个原因...
I think u should read here on how to consume a buffered entity where more than a single read is going to be require to wind up at the 'last-chunk' in the response. It's another reason why HttpURLConnection may be easier...
执行一个循环来处理缓冲读取,直到匹配last-chunk"的字节发出 END 信号为止.
Do a loop that handles buffered reads until END signaled by the bytes matching 'last-chunk'.
然后根据有关使用实体和可重用连接的详细 Apache 说明关闭流或连接.
Then close either the stream or connection as per the detailed Apache notes on consuming entities and reusable Connections.
EDIT Apache HttpClient 中流式响应的代码
EDIT code for streamed response in apache HttpClient
在处理程序的回调"中或在 asyncTask 中
In a 'handler's callback or in 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 此处)
HttpURLConnection version if you go that way. ( uses a MessageHandler but you could consume the bytes in place as this is from a streaming speach example and the words from text are being sent back to UI here)
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();}
}
这篇关于如何使用 apache HttpClient 流式传输响应正文的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!