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错误。
具体修复步骤
解析IP数据包
- 从VPN接口读取的是完整的IP层数据,需要先解析出IP头,提取目标IP、端口,以及传输层(TCP/UDP)的负载数据。
- 可以手动解析IPv4头(前20字节包含版本、协议类型、源/目标IP等信息),或者使用成熟的网络数据包解析库简化开发。
实现SOCKS5协议握手逻辑
- 针对每个TCP连接:
- 与SOCKS5代理建立握手:发送版本标识
0x05、支持的认证方法列表,等待代理响应。 - 发送
CONNECT请求,指定目标IP和端口,等待代理返回连接成功的响应。 - 握手完成后,再双向转发TCP数据。
- 与SOCKS5代理建立握手:发送版本标识
- 针对UDP流量:
- 发送
UDP ASSOCIATE请求给SOCKS5代理,获取代理分配的UDP端口。 - 转发UDP数据时,需要添加SOCKS5的UDP头部(包含目标IP和端口)。
- 发送
- 针对每个TCP连接:
重构流量转发逻辑
- 放弃当前单线程同步读写的方式,改用多线程或NIO Selector处理每个独立的TCP/UDP连接,避免阻塞导致的异常。
- 为每个新的目标连接创建单独的SOCKS5会话,不要共用一个SocketChannel。
额外检查项
- 确认SSH客户端创建的SOCKS5代理同时支持TCP和UDP转发(部分SSH客户端默认仅启用TCP)。
- 检查VPS防火墙规则,确保允许SOCKS5代理的流量进出。
内容的提问来源于stack exchange,提问作者saeid yousefi
相关产品推荐
相关产品推荐

