当尝试使用 OkHttp(在 android 上)从 AVS 网络服务器接收 http 响应时,我得到一个 SocketTimeoutException
When trying to recieve an http response from the AVS web server using OkHttp (on android), I get a SocketTimeoutException
下面的代码有很多调试打印语句。我在谷歌搜索和浏览其他堆栈溢出帖子时发现,人们通常会在发出同步事件请求后得到一个 json 文件,但我在调用 Log.d("syncResponse_body", "Result: " + response.body().string());
时大约 30 秒内什么也得不到,然后是 SocketTimeoutException。这是我关注的亚马逊文档:
https://developer.amazon.com/en-US/docs/alexa/alexa-voice-service/manage-http2-connection.html
private Response makeSyncRequest(OkHttpClient downChannelClient, String accessToken) throws IOException {
final Request getRequest = new Request.Builder()
.url("https://alexa.na.gateway.devices.a2z.com/" + AVS_API_VERSION + "/directives")//endpoint url
.get()
.addHeader("authorization", "Bearer " + accessToken)
.build();
Log.d("Request_header", getRequest.toString());
Call currentCall = downChannelClient.newCall(getRequest);
Response syncResponse = currentCall.execute();
Log.d("Response_success", String.valueOf(syncResponse.isSuccessful()));
Log.d("Response_str", syncResponse.toString());
return syncResponse;
}
private Response syncronizeWithAVS(OkHttpClient downChannelClient, String accessToken) throws IOException {
Response syncResponse = makeSyncRequest(downChannelClient, accessToken);
String msgId = UUID.randomUUID().toString();
String speakToken = "";
long offsetMili = 20; // if lags put down to 10.
String playerActivity = "PLAYING";
// ------------------------------
final String JSON_SYNC = "{\"context\":[{\"header\":{\"namespace\":\"SpeechRecognizer\",\"name\":\"RecognizerState\"},\"payload\":{\"wakeword\":\"ALEXA\"}},{\"header\":{\"namespace\":\"SpeechSynthesizer\",\"name\":\"SpeechState\"},\"payload\":{\"token\":\"" + speakToken + "\",\"offsetInMilliseconds\":" + offsetMili + ",\"playerActivity\":\"" + playerActivity + "\"}}],\"event\":{\"header\":{\"namespace\":\"System\",\"name\":\"SynchronizeState\",\"messageId\":\"" + msgId + "\"},\"payload\":{}}}";
List<MultipartBody.Part> partList = new ArrayList<>();
MultipartBody.Part syncPart = MultipartBody.Part.create(Headers.of("Content-Disposition", "form-data; name=\"metadata\""), RequestBody.create(JSON_SYNC, JSON_TYPE));
partList.add(syncPart);
RequestBody body = new MultipartBody(ByteString.encodeUtf8(BOUNDARY_TERM), MultipartBody.FORM, partList);
Log.d("part", syncPart.headers().toString());
Log.d("body", body.contentType().toString());
final Request postRequest = new Request.Builder()
.url("https://alexa.na.gateway.devices.a2z.com/" + AVS_API_VERSION + "/events")//endpoint url
.post(body)
.addHeader("authorization", "Bearer " + accessToken)
.build();
Log.d("post_request", postRequest.toString());
Log.d("post_req_body", JSON_SYNC);
Response postResponse = null;
try {
postResponse = downChannelClient.newCall(postRequest).execute();
Log.d("Post_succes", "" + postResponse.isSuccessful());
Log.d("Post_succes", "" + postResponse.toString());
Log.d("Post_Response", "Result: " + postResponse.body().string());
} catch (IOException e) {
e.printStackTrace();
}
return syncResponse;
}
private void login(AuthorizeResult authorizeResult) {
...
Response response = syncronizeWithAVS(downChannelClient, accessToken);
Log.d("syncResponse_body", "Result: " + response.body().string());
...
}
登录亚马逊获取访问令牌后,这是我得到的输出:
D/accessToken: authorizeResult.toString(): com.amazon.identity.auth.device.api.authorization.AuthorizeResult@f028508b
accessToken: <censored accesstoken for this forum post>
D/Request_header: Request{method=GET, url=https://alexa.na.gateway.devices.a2z.com/v20160207/directives, headers=[authorization:Bearer <censored accesstoken for this forum post>]}
D/Response_success: true
D/Response_str: Response{protocol=h2, code=200, message=, url=https://alexa.na.gateway.devices.a2z.com/v20160207/directives}
D/part: Content-Disposition: form-data; name="metadata"
D/body: multipart/form-data; boundary=------------------------qM9tn4VZyj
D/post_request: Request{method=POST, url=https://alexa.na.gateway.devices.a2z.com/v20160207/events, headers=[authorization:Bearer <censored accesstoken for this forum post>]}
D/post_req_body: {"context":[{"header":{"namespace":"SpeechRecognizer","name":"RecognizerState"},"payload":{"wakeword":"ALEXA"}},{"header":{"namespace":"SpeechSynthesizer","name":"SpeechState"},"payload":{"token":"","offsetInMilliseconds":20,"playerActivity":"PLAYING"}}],"event":{"header":{"namespace":"System","name":"SynchronizeState","messageId":"c5488d48-c758-46a0-aeea-e305a452df90"},"payload":{}}}
D/Post_succes: true
Response{protocol=h2, code=204, message=, url=https://alexa.na.gateway.devices.a2z.com/v20160207/events}
D/Post_Response: Result:
然后,30 秒后,
W/System.err: java.net.SocketTimeoutException: timeout
at okhttp3.internal.http2.Http2Stream$StreamTimeout.newTimeoutException(Http2Stream.kt:677)
at okhttp3.internal.http2.Http2Stream$StreamTimeout.exitAndThrowIfTimedOut(Http2Stream.kt:686)
at okhttp3.internal.http2.Http2Stream$FramingSource.read(Http2Stream.kt:382)
W/System.err: at okhttp3.internal.connection.Exchange$ResponseBodySource.read(Exchange.kt:276)
at okio.Buffer.writeAll(Buffer.kt:1655)
at okio.RealBufferedSource.readString(RealBufferedSource.kt:95)
at okhttp3.ResponseBody.string(ResponseBody.kt:187)
at aut.rnd.alexa.ui.account.AccountFragment.login(AccountFragment.java:362)
at aut.rnd.alexa.ui.account.AccountFragment.access0(AccountFragment.java:65)
at aut.rnd.alexa.ui.account.AccountFragment$TokenListener.onSuccess(AccountFragment.java:381)
at aut.rnd.alexa.ui.account.AccountFragment$TokenListener.onSuccess(AccountFragment.java:373)
at com.amazon.identity.auth.device.api.authorization.AuthorizationManager.onSuccess(AuthorizationManager.java:276)
at com.amazon.identity.auth.device.thread.AuthzCallbackFuture.onSuccess(AuthzCallbackFuture.java:36)
at com.amazon.identity.auth.device.thread.MAPCallbackFuture.onSuccess(MAPCallbackFuture.java:88)
at com.amazon.identity.auth.device.authorization.InternalAuthManager.onSuccess(InternalAuthManager.java:119)
at com.amazon.identity.auth.device.authorization.TokenHelper.getToken(TokenHelper.java:65)
W/System.err: at com.amazon.identity.auth.device.authorization.InternalAuthManager.run(InternalAuthManager.java:115)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1167)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:641)
at java.lang.Thread.run(Thread.java:923)
guide you mention 有这个信息:
Read timeouts: Because AVS requires a downchannel stream to be open
between AVS and a client for the life of the connection, set any read
timeout for your client to at least 60 minutes.
所以你当然应该增加这个连接的读超时时间。为此,请使用此代码(在 this answer 中找到):
downChannelClient.setReadTimeout(60, TimeUnit.MINUTES);
下面的代码有很多调试打印语句。我在谷歌搜索和浏览其他堆栈溢出帖子时发现,人们通常会在发出同步事件请求后得到一个 json 文件,但我在调用 Log.d("syncResponse_body", "Result: " + response.body().string());
时大约 30 秒内什么也得不到,然后是 SocketTimeoutException。这是我关注的亚马逊文档:
https://developer.amazon.com/en-US/docs/alexa/alexa-voice-service/manage-http2-connection.html
private Response makeSyncRequest(OkHttpClient downChannelClient, String accessToken) throws IOException {
final Request getRequest = new Request.Builder()
.url("https://alexa.na.gateway.devices.a2z.com/" + AVS_API_VERSION + "/directives")//endpoint url
.get()
.addHeader("authorization", "Bearer " + accessToken)
.build();
Log.d("Request_header", getRequest.toString());
Call currentCall = downChannelClient.newCall(getRequest);
Response syncResponse = currentCall.execute();
Log.d("Response_success", String.valueOf(syncResponse.isSuccessful()));
Log.d("Response_str", syncResponse.toString());
return syncResponse;
}
private Response syncronizeWithAVS(OkHttpClient downChannelClient, String accessToken) throws IOException {
Response syncResponse = makeSyncRequest(downChannelClient, accessToken);
String msgId = UUID.randomUUID().toString();
String speakToken = "";
long offsetMili = 20; // if lags put down to 10.
String playerActivity = "PLAYING";
// ------------------------------
final String JSON_SYNC = "{\"context\":[{\"header\":{\"namespace\":\"SpeechRecognizer\",\"name\":\"RecognizerState\"},\"payload\":{\"wakeword\":\"ALEXA\"}},{\"header\":{\"namespace\":\"SpeechSynthesizer\",\"name\":\"SpeechState\"},\"payload\":{\"token\":\"" + speakToken + "\",\"offsetInMilliseconds\":" + offsetMili + ",\"playerActivity\":\"" + playerActivity + "\"}}],\"event\":{\"header\":{\"namespace\":\"System\",\"name\":\"SynchronizeState\",\"messageId\":\"" + msgId + "\"},\"payload\":{}}}";
List<MultipartBody.Part> partList = new ArrayList<>();
MultipartBody.Part syncPart = MultipartBody.Part.create(Headers.of("Content-Disposition", "form-data; name=\"metadata\""), RequestBody.create(JSON_SYNC, JSON_TYPE));
partList.add(syncPart);
RequestBody body = new MultipartBody(ByteString.encodeUtf8(BOUNDARY_TERM), MultipartBody.FORM, partList);
Log.d("part", syncPart.headers().toString());
Log.d("body", body.contentType().toString());
final Request postRequest = new Request.Builder()
.url("https://alexa.na.gateway.devices.a2z.com/" + AVS_API_VERSION + "/events")//endpoint url
.post(body)
.addHeader("authorization", "Bearer " + accessToken)
.build();
Log.d("post_request", postRequest.toString());
Log.d("post_req_body", JSON_SYNC);
Response postResponse = null;
try {
postResponse = downChannelClient.newCall(postRequest).execute();
Log.d("Post_succes", "" + postResponse.isSuccessful());
Log.d("Post_succes", "" + postResponse.toString());
Log.d("Post_Response", "Result: " + postResponse.body().string());
} catch (IOException e) {
e.printStackTrace();
}
return syncResponse;
}
private void login(AuthorizeResult authorizeResult) {
...
Response response = syncronizeWithAVS(downChannelClient, accessToken);
Log.d("syncResponse_body", "Result: " + response.body().string());
...
}
登录亚马逊获取访问令牌后,这是我得到的输出:
D/accessToken: authorizeResult.toString(): com.amazon.identity.auth.device.api.authorization.AuthorizeResult@f028508b
accessToken: <censored accesstoken for this forum post>
D/Request_header: Request{method=GET, url=https://alexa.na.gateway.devices.a2z.com/v20160207/directives, headers=[authorization:Bearer <censored accesstoken for this forum post>]}
D/Response_success: true
D/Response_str: Response{protocol=h2, code=200, message=, url=https://alexa.na.gateway.devices.a2z.com/v20160207/directives}
D/part: Content-Disposition: form-data; name="metadata"
D/body: multipart/form-data; boundary=------------------------qM9tn4VZyj
D/post_request: Request{method=POST, url=https://alexa.na.gateway.devices.a2z.com/v20160207/events, headers=[authorization:Bearer <censored accesstoken for this forum post>]}
D/post_req_body: {"context":[{"header":{"namespace":"SpeechRecognizer","name":"RecognizerState"},"payload":{"wakeword":"ALEXA"}},{"header":{"namespace":"SpeechSynthesizer","name":"SpeechState"},"payload":{"token":"","offsetInMilliseconds":20,"playerActivity":"PLAYING"}}],"event":{"header":{"namespace":"System","name":"SynchronizeState","messageId":"c5488d48-c758-46a0-aeea-e305a452df90"},"payload":{}}}
D/Post_succes: true
Response{protocol=h2, code=204, message=, url=https://alexa.na.gateway.devices.a2z.com/v20160207/events}
D/Post_Response: Result:
然后,30 秒后,
W/System.err: java.net.SocketTimeoutException: timeout
at okhttp3.internal.http2.Http2Stream$StreamTimeout.newTimeoutException(Http2Stream.kt:677)
at okhttp3.internal.http2.Http2Stream$StreamTimeout.exitAndThrowIfTimedOut(Http2Stream.kt:686)
at okhttp3.internal.http2.Http2Stream$FramingSource.read(Http2Stream.kt:382)
W/System.err: at okhttp3.internal.connection.Exchange$ResponseBodySource.read(Exchange.kt:276)
at okio.Buffer.writeAll(Buffer.kt:1655)
at okio.RealBufferedSource.readString(RealBufferedSource.kt:95)
at okhttp3.ResponseBody.string(ResponseBody.kt:187)
at aut.rnd.alexa.ui.account.AccountFragment.login(AccountFragment.java:362)
at aut.rnd.alexa.ui.account.AccountFragment.access0(AccountFragment.java:65)
at aut.rnd.alexa.ui.account.AccountFragment$TokenListener.onSuccess(AccountFragment.java:381)
at aut.rnd.alexa.ui.account.AccountFragment$TokenListener.onSuccess(AccountFragment.java:373)
at com.amazon.identity.auth.device.api.authorization.AuthorizationManager.onSuccess(AuthorizationManager.java:276)
at com.amazon.identity.auth.device.thread.AuthzCallbackFuture.onSuccess(AuthzCallbackFuture.java:36)
at com.amazon.identity.auth.device.thread.MAPCallbackFuture.onSuccess(MAPCallbackFuture.java:88)
at com.amazon.identity.auth.device.authorization.InternalAuthManager.onSuccess(InternalAuthManager.java:119)
at com.amazon.identity.auth.device.authorization.TokenHelper.getToken(TokenHelper.java:65)
W/System.err: at com.amazon.identity.auth.device.authorization.InternalAuthManager.run(InternalAuthManager.java:115)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1167)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:641)
at java.lang.Thread.run(Thread.java:923)
guide you mention 有这个信息:
Read timeouts: Because AVS requires a downchannel stream to be open between AVS and a client for the life of the connection, set any read timeout for your client to at least 60 minutes.
所以你当然应该增加这个连接的读超时时间。为此,请使用此代码(在 this answer 中找到):
downChannelClient.setReadTimeout(60, TimeUnit.MINUTES);