当尝试使用 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);