英文:
What could be the reason for a SocketTimeoutException on android but not on Postman?
问题
我有一个使用gin
和gorm
帮助创建的Golang
服务器和API。它之前一直工作正常,但最近出现了一些奇怪的问题。在我的安卓应用中使用Retrofit
调用API之前,我会在Postman
上进行测试。就在昨天,我尝试调用登录API以获取后续需要的JWT
令牌。在Postman
上,登录功能正常。但是昨天开始,由于出现了SocketTimeoutException
,登录在安卓应用中无法正常工作了。我不知道是什么原因导致的。在Postman
上显示调用只花了大约10秒钟,而且我已经使用以下代码将Retrofit
的超时时间增加到了180秒:
private val httpClient = OkHttpClient.Builder()
.connectTimeout(180, TimeUnit.SECONDS)
.writeTimeout(180, TimeUnit.SECONDS)
.readTimeout(180, TimeUnit.SECONDS)
但问题仍然存在。我不确定之前在服务器上添加的代码是否导致了这个问题,但现在它并没有被调用。这段代码用于启动一个套接字服务器,但由于某种原因(尽管它是来自googollee的官方示例代码),它似乎无法正常工作。以下是代码示例:
package helpers
import (
"fmt"
"net/http"
socketio "github.com/googollee/go-socket.io"
"github.com/googollee/go-socket.io/engineio"
"github.com/googollee/go-socket.io/engineio/transport"
"github.com/googollee/go-socket.io/engineio/transport/polling"
"github.com/googollee/go-socket.io/engineio/transport/websocket"
)
const (
SERVER_HOST = "localhost"
SERVER_PORT = "60051"
SERVER_TYPE = "tcp"
)
var allowOriginFunc = func(r *http.Request) bool {
return true
}
func StartSocket() {
server := socketio.NewServer(&engineio.Options{
Transports: []transport.Transport{
&polling.Transport{
CheckOrigin: allowOriginFunc,
},
&websocket.Transport{
CheckOrigin: allowOriginFunc,
},
},
})
server.OnConnect("/", func(s socketio.Conn) error {
s.SetContext("")
fmt.Println("connected:", s.ID())
return nil
})
server.OnEvent("/", "notice", func(s socketio.Conn, msg string) {
fmt.Println("notice:", msg)
s.Emit("reply", "have "+msg)
})
server.OnError("/", func(s socketio.Conn, e error) {
fmt.Println("meet error:", e)
})
server.OnDisconnect("/", func(s socketio.Conn, reason string) {
fmt.Println("closed", reason)
})
go server.Serve()
defer server.Close()
http.Handle("/socket.io/", server)
http.Handle("/", http.FileServer(http.Dir("./asset")))
fmt.Println("ScktSrv Serving at localhost:8000...")
fmt.Print(http.ListenAndServe(":8000", nil))
}
服务器和模拟器都在我的电脑上运行。
更新:
以下是安卓代码的示例,它有点复杂,但基本上只是一个Retrofit
调用:
val username = binding.txtUsername.text.toString()
val password = binding.txtPassword.text.toString()
val account = Account("", "", "", username, password, mutableListOf())
scope.launch {
val inAPIDataResp = retFetchHlpr.fetchAPIData{ atlAPI.loginAccount(account) }
//一些处理接收到的响应的代码
}
Retrofit端点设置如下:
@Headers("Content-Type: application/json")
@POST("/account/login")
suspend fun loginAccount(
@Body data: Account
) : Response<AccountLoginResponse>
这是我的辅助类中的代码:
suspend fun <V> fetchAPIData(apiGetterFunc: suspend () -> Response<V>): V? {
val actsResult: Response<V>?
if( utils.isInternetAvailable(ctx) ){
try{
actsResult = apiGetterFunc.invoke()
val respBody = retrofitErrorHandler(actsResult)
if (respBody != null){
return respBody
}
}catch (e: Exception){
Log.e(TAG, "Retrofit Error: ${e.localizedMessage}")
}
}
return null
}
private fun <T> retrofitErrorHandler(res: Response<T>): T {
if (res.isSuccessful) {
Log.d(TAG, "retrofitErrorHandler success: ${res.body()}")
return res.body()!!
} else {
Log.d(TAG, "retrofitErrorHandler errMsg: ${res.errorBody().toString()}")
throw Exception(res.errorBody().toString())
}
}
我得到的错误是:
Retrofit Error: failed to connect to /192.168.45.102 (port 5000) from
/10.0.2.16 (port 54552) after 180000ms: isConnected failed: ETIMEDOUT
(Connection timed out)
我尝试将我的电脑和运行该应用的平板连接到我的手机热点,但仍然出现这个错误。
英文:
I have a Golang
server with an API created with the help of gin
and gorm
. It was working fine until recently when it's having some weird behavior. Before calling the API on my android app using Retrofit
, I'd test it on Postman
. Just yesterday, I tried to call the login API to get the JWT
token that is needed for the other API calls I'd need later on. On Postman
, the login was working fine. But yesterday, the login doesn't work on the android app anymore because of the SocketTimeoutException
. And I don't know what's causing that. On Postman
, it said that it only took 10-ish seconds to finish the call, and I already increased the timeout of Retrofit
to 180s using these lines of code:
private val httpClient = OkHttpClient.Builder()
.connectTimeout(180, TimeUnit.SECONDS)
.writeTimeout(180, TimeUnit.SECONDS)
.readTimeout(180, TimeUnit.SECONDS)
and the issue still persist. I'm not sure if a code I added on the server before is causing this, but it isn't being called right now. It's some code for starting a socket server, which doesn't seem to really work right now for some reason(even though it's an official sample code from googollee). Here it is:
package helpers
import (
"fmt"
"net/http"
socketio "github.com/googollee/go-socket.io"
"github.com/googollee/go-socket.io/engineio"
"github.com/googollee/go-socket.io/engineio/transport"
"github.com/googollee/go-socket.io/engineio/transport/polling"
"github.com/googollee/go-socket.io/engineio/transport/websocket"
)
const (
SERVER_HOST = "localhost"
SERVER_PORT = "60051"
SERVER_TYPE = "tcp"
)
var allowOriginFunc = func(r *http.Request) bool {
return true
}
func StartSocket() {
server := socketio.NewServer(&engineio.Options{
Transports: []transport.Transport{
&polling.Transport{
CheckOrigin: allowOriginFunc,
},
&websocket.Transport{
CheckOrigin: allowOriginFunc,
},
},
})
server.OnConnect("/", func(s socketio.Conn) error {
s.SetContext("")
fmt.Println("connected:", s.ID())
return nil
})
server.OnEvent("/", "notice", func(s socketio.Conn, msg string) {
fmt.Println("notice:", msg)
s.Emit("reply", "have "+msg)
})
server.OnError("/", func(s socketio.Conn, e error) {
fmt.Println("meet error:", e)
})
server.OnDisconnect("/", func(s socketio.Conn, reason string) {
fmt.Println("closed", reason)
})
go server.Serve()
defer server.Close()
http.Handle("/socket.io/", server)
http.Handle("/", http.FileServer(http.Dir("./asset")))
fmt.Println("ScktSrv Serving at localhost:8000...")
fmt.Print(http.ListenAndServe(":8000", nil))
}
Both the server and the emulator are running on my PC
UPDATE:
Here's what the android code looks like, it's kinda complex but it's basically just a Retrofit
call:
val username = binding.txtUsername.text.toString()
val password = binding.txtPassword.text.toString()
val account = Account(" ", " ", " ", username, password, mutableListOf())
scope.launch {
val inAPIDataResp = retFetchHlpr.fetchAPIData{ atlAPI.loginAccount(account) }
//some code for processing the received response
}
//Retrofit endpoint setup
@Headers("Content-Type: application/json")
@POST("/account/login")
suspend fun loginAccount(
@Body data: Account
) : Response<AccountLoginResponse>
Here's the code from my helper class:
suspend fun <V> fetchAPIData(apiGetterFunc: suspend () -> Response<V>): V? {
val actsResult: Response<V>?
if( utils.isInternetAvailable(ctx) ){
try{
actsResult = apiGetterFunc.invoke()
val respBody = retrofitErrorHandler(actsResult)
if (respBody != null){
return respBody
}
}catch (e: Exception){
Log.e(TAG, "Retrofit Error: ${e.localizedMessage}")
}
}
return null
}
private fun <T> retrofitErrorHandler(res: Response<T>): T {
if (res.isSuccessful) {
Log.d(TAG, "retrofitErrorHandler success: ${res.body()}")
return res.body()!!
} else {
Log.d(TAG, "retrofitErrorHandler errMsg: ${res.errorBody().toString()}")
throw Exception(res.errorBody().toString())
}
}
The error I'm getting is:
> Retrofit Error: failed to connect to /192.168.45.102 (port 5000) from
> /10.0.2.16 (port 54552) after 180000ms: isConnected failed: ETIMEDOUT
> (Connection timed out)
I tried connecting my PC and a tablet running the app to my phone's hotspot and I still get this error.
答案1
得分: 1
我认为我已经找到了一个解决方案。为OkHttpClient
添加.protocols(listOf(Protocol.HTTP_1_1))
似乎可以使Retrofit
正常工作。在这里找到了答案。
英文:
I think I already found a solution. Adding .protocols(listOf(Protocol.HTTP_1_1))
for the OkHttpClient
seems to make Retrofit
work fine again. Found the answer here
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论