You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Android Kotlin应用与Python服务端Socket连接超时问题排查

Android Kotlin Socket客户端无法连接Python服务端(超时错误)

问题背景

搭建一套系统,让手机连接至带VPN的服务器,通过Socket实现数据传输、处理存储及回传。服务端用Python编写,客户端是Kotlin开发的Android应用。Python回声服务端已验证可正常运行,但Android客户端连接时出现SocketTimeoutException超时错误。

服务端Python代码

import socket

HOST = "10.8.0.1"
PORT = 65432

with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s:
    s.bind((HOST, PORT))
    s.listen()
    print("Listening...")
    conn, addr = s.accept()
    with conn:
        print(f"Connected by {addr}")
        while True:
            data = conn.recv(1024)
            print(f"Recived: {data.decode(\"utf-8\")}")
            if not data:
                break
            conn.sendall(data)

Android客户端Kotlin代码

package com.example.testsockets

import android.os.Bundle
import android.view.View
import android.widget.TextView
import androidx.appcompat.app.AppCompatActivity
import com.google.android.material.textfield.TextInputEditText
import java.net.InetSocketAddress
import java.net.Socket
import java.util.concurrent.Executors


class MainActivity : AppCompatActivity() {
    private val socket = Socket()
    private val executorService = Executors.newSingleThreadExecutor();
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
    }

    fun send(v: View?){
        executorService.execute {
            var entrada = findViewById<TextInputEditText>(R.id.entrada)
            var salida = findViewById<TextView>(R.id.salida)
            val host = "10.8.0.1"
            val port = 65432
            socket.connect(InetSocketAddress(host, port), 3000)
            // This will be used to send to the server
            val out = socket.getOutputStream()
            out.write(entrada.text.toString().toByteArray(charset("UTF-8")))
        }
    }
}

AndroidManifest配置

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools">
     <uses-permission android:name="android.permission.INTERNET"/>
     <application
     android:allowBackup="true"
     android:dataExtractionRules="@xml/data_extraction_rules"
     android:fullBackupContent="@xml/backup_rules"
     android:icon="@mipmap/ic_launcher"
     android:label="@string/app_name"
     android:roundIcon="@mipmap/ic_launcher_round"
     android:supportsRtl="true"
     android:theme="@style/Theme.TestSockets"
     tools:targetApi="31">
     <activity android:name=".MainActivity" android:exported="true">
          <intent-filter>
             <action android:name="android.intent.action.MAIN" />
             <category android:name="android.intent.category.LAUNCHER" />
          </intent-filter>
      </activity>
  </application>
</manifest>

错误日志

FATAL EXCEPTION: pool-1-thread-1
Process: com.example.testsockets, PID: 1897
java.lang.Error: java.net.SocketTimeoutException: failed to connect to /10.8.0.1 (port 65432) from /10.8.0.2 (port 45974) after 3000ms
    at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1173)
    at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:641)
    at java.lang.Thread.run(Thread.java:764)
Caused by: java.net.SocketTimeoutException: failed to connect to /10.8.0.1 (port 65432) from /10.8.0.2 (port 45974) after 3000ms
    at libcore.io.IoBridge.connectErrno(IoBridge.java:185)
    at libcore.io.IoBridge.connect(IoBridge.java:129)
    at java.net.PlainSocketImpl.socketConnect(PlainSocketImpl.java:137)
    at java.net.AbstractPlainSocketImpl.doConnect(AbstractPlainSocketImpl.java:390)
    at java.net.AbstractPlainSocketImpl.connectToAddress(AbstractPlainSocketImpl.java:230)
    at java.net.AbstractPlainSocketImpl.connect(AbstractPlainSocketImpl.java:212)
    at java.net.SocksSocketImpl.connect(SocksSocketImpl.java:436)
    at java.net.Socket.connect(Socket.java:621)
    at com.example.testsockets.MainActivity.send$lambda$0(MainActivity.kt:27)
    at com.example.testsockets.MainActivity.$r8$lambda$qyWxoPew67mcJ-GNbw1keB5dm7k(Unknown Source:0)
    at com.example.testsockets.MainActivity$$ExternalSyntheticLambda0.run(Unknown Source:2)
    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:764) 

排查与解决步骤

  • 验证网络连通性:在手机上执行ping 10.8.0.1测试网段连通性,确认手机已成功接入VPN且与服务器在同一网段;在服务器上用nc -zv 10.8.0.1 65432检查端口是否处于监听状态。
  • 调整服务端绑定IP:将服务端的HOST改为0.0.0.0,允许所有网络接口接入,避免因网卡绑定问题导致无法接收连接:
    HOST = "0.0.0.0"
    
  • 修复客户端Socket复用问题:全局Socket实例重复调用connect会引发异常,改为每次发送时创建新Socket,并添加异常处理与资源释放:
    fun send(v: View?){
        executorService.execute {
            val entrada = findViewById<TextInputEditText>(R.id.entrada)
            val salida = findViewById<TextView>(R.id.salida)
            val host = "10.8.0.1"
            val port = 65432
            val socket = Socket()
            try {
                socket.connect(InetSocketAddress(host, port), 3000)
                val out = socket.getOutputStream()
                out.write(entrada.text.toString().toByteArray(charset("UTF-8")))
                out.flush()
                out.close()
                socket.close()
                runOnUiThread {
                    salida.text = "发送成功"
                }
            } catch (e: Exception) {
                e.printStackTrace()
                runOnUiThread {
                    salida.text = "连接失败:${e.message}"
                }
            }
        }
    }
    
  • 放行服务器端口:检查服务器防火墙(如ufw)是否开放65432端口,执行sudo ufw allow 65432/tcp放行;若为云服务器,需在平台安全组添加对应端口的入站规则。
  • 确认VPN路由规则:检查Android设备的VPN设置,确保应用被允许通过VPN访问,且VPN已正确路由10.8.0.0/24网段流量。

内容的提问来源于stack exchange,提问作者Alejandro

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.22 07:48:12