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

Android VPN应用使用SOCKS5代理时出现Connection reset by peer错误

Android VPN服务连接SOCKS5代理报错:Connection reset by peer

我正在开发一款Android应用,通过自定义VPN服务连接VPS上的SOCKS5代理,但遇到如下错误:

Connection failed: Connection reset by peer

我怀疑问题在于SOCKS5代理未在应用中被正确创建或初始化。


使用的代码

MyVpnService类

class MyVpnService : android.net.VpnService() {
    private val TAG = "VpnService"
    private val sshClient = SSHClient()

    private var vpnInterface: ParcelFileDescriptor? = null

    override fun onCreate() {
        super.onCreate()
        GlobalScope.launch {
            showNotification()
            startVPN()
        }
    }

    private fun startVPN() {
        // Step 1: Start the SOCKS proxy (SSH connection)
        sshClient.startSSH(
            host = "my.ssh.service",
            port = 22,
            username = "root",
            password = "password",
            socksPort = 1080
        )

        // Step 2: Configure and establish the VPN interface
        val builder = Builder()
        builder.setSession("MyVPN")
            .addAddress("10.0.0.2", 24) // VPN IP address
            .addDnsServer("8.8.8.8") // DNS server
            .addRoute("0.0.0.0", 0) // Route all traffic through VPN
            .setMtu(1500)

        vpnInterface = builder.establish()
        if (vpnInterface == null) {
            Log.e(TAG, "Failed to establish VPN interface")
            return
        }
        Log.d(TAG, "VPN interface established")

        // Step 3: Redirect traffic through the SOCKS proxy
        redirectTraffic(vpnInterface!!)
        Log.d(TAG, "Traffic redirection started")
    }

    override fun onDestroy() {
        super.onDestroy()
        stopVPN()
    }

    private fun stopVPN() {
        sshClient.stopSSH()
        vpnInterface?.close()
        vpnInterface = null
    }

    private fun showNotification() {
        val notification = NotificationCompat.Builder(this, "my_channel")
            .setSmallIcon(com.sy.onboarding_ui.R.drawable.ic_next)
            .setContentTitle("Run is active")
            .setContentText("Hello world")
            .build()
        startForeground(1, notification)
    }
}

流量转发函数

fun redirectTraffic(vpnInterface: ParcelFileDescriptor) {
    val buffer = ByteBuffer.allocate(32767)

    try {
        // Connect to the SOCKS5 proxy
        val tunnel = SocketChannel.open(InetSocketAddress("127.0.0.1", 1080))
        if (!tunnel.isConnected) {
            Log.e("redirectTraffic", "Failed to connect to SOCKS5 proxy")
            return
        }
        Log.d("redirectTraffic", "Connected to SOCKS5 proxy")

        val inputStream = ParcelFileDescriptor.AutoCloseInputStream(vpnInterface)
        val outputStream = ParcelFileDescriptor.AutoCloseOutputStream(vpnInterface)

        while (true) {
            // Read packets from the VPN interface
            val length = inputStream.read(buffer.array())
            if (length > 0) {
                buffer.limit(length)

                // Forward packets to the SOCKS5 proxy
                tunnel.write(buffer)
                Log.d("redirectTraffic", "Forwarded $length bytes to SOCKS5 proxy")

                // Clear the buffer for the next read
                buffer.clear()
            }

            // Read responses from the SOCKS5 proxy
            val bytesRead = tunnel.read(buffer)
            if (bytesRead > 0) {
                // Write responses back to the VPN interface
                outputStream.write(buffer.array(), 0, bytesRead)
                Log.d("redirectTraffic", "Received $bytesRead bytes from SOCKS5 proxy")

                // Clear the buffer for the next read
                buffer.clear()
            }
        }
    } catch (e: IOException) {
        Log.e("redirectTraffic", "Connection failed: ${e.message}")
        e.printStackTrace()
    }
}

日志信息

SSH connection established
SOCKS5 proxy started on port 1080
VPN interface established
Connected to SOCKS5 proxy
Forwarded 76 bytes to SOCKS5 proxy
Connection failed: Connection reset by peer

问题根源与解决办法

核心问题

你直接将VPN接口收到的原始IP数据包转发给了SOCKS5代理,但SOCKS5是应用层协议,它只处理TCP/UDP流,不识别IP数据包格式。SOCKS5代理收到不符合协议的IP包后,会直接断开连接,导致Connection reset by peer错误。

具体修复步骤

  1. 解析IP数据包

    • 从VPN接口读取的是完整的IP层数据,需要先解析出IP头,提取目标IP、端口,以及传输层(TCP/UDP)的负载数据。
    • 可以手动解析IPv4头(前20字节包含版本、协议类型、源/目标IP等信息),或者使用成熟的网络数据包解析库简化开发。
  2. 实现SOCKS5协议握手逻辑

    • 针对每个TCP连接:
      1. 与SOCKS5代理建立握手:发送版本标识0x05、支持的认证方法列表,等待代理响应。
      2. 发送CONNECT请求,指定目标IP和端口,等待代理返回连接成功的响应。
      3. 握手完成后,再双向转发TCP数据。
    • 针对UDP流量:
      1. 发送UDP ASSOCIATE请求给SOCKS5代理,获取代理分配的UDP端口。
      2. 转发UDP数据时,需要添加SOCKS5的UDP头部(包含目标IP和端口)。
  3. 重构流量转发逻辑

    • 放弃当前单线程同步读写的方式,改用多线程或NIO Selector处理每个独立的TCP/UDP连接,避免阻塞导致的异常。
    • 为每个新的目标连接创建单独的SOCKS5会话,不要共用一个SocketChannel。
  4. 额外检查项

    • 确认SSH客户端创建的SOCKS5代理同时支持TCP和UDP转发(部分SSH客户端默认仅启用TCP)。
    • 检查VPS防火墙规则,确保允许SOCKS5代理的流量进出。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.15 07:28:18