9.4 代理客户端
const server = new net.createServer()
server
.listen(socks5Port, '0.0.0.0')
.on('connection', (socket) => {
socket.setNoDelay()
udpack.openStream((err, stream) => {
if (err) {
logger.error(err)
socket.destroy()
} else {
var proxy = Proxy(socket, stream)
socket
.on('data', (buf) => {
try {
proxy.handle(buf)
} catch (err) {
logger.error('proxy.handle', err)
socket.destroy()
}
})
.on('close', hadError => {
proxy.dstTerminate()
})
}
})
})
UDPack Client 启动 TCP 监听端口,等待 Browser 的连接。有新的连接进来后,会通过之前创建的 UDPack 实例对象 udpack 打开 Stream。并创建 SOCKS 代理对象 Proxy 的实例 proxy,传入 socket 和 stream。
Browser 发送过来的数据通过 proxy.handle(buf)
调用,转交给 proxy 处理,而 proxy 内部会通过 stream 发送到 UDPack Server 端。同样的,UDPack Server 端也会通过相同的 stream 对象发送数据,UDPack Client 端接收到数据后,通过 socket 对象转发给 Browser。
dstConnect(req) {
let { domain, ip, port } = req
let authority = Authority.create({
domain,
ip,
port
})
let packet = Packet.create({
sid: this._session.id,
cmd: 'connect',
authority
})
let buffer = Packet.encode(packet).finish()
stream.write(buffer, (err) => {
if (err) {
logger.error(err.message, err.stack)
}
})
logger.debug('dstConnect', `sid [${this._session.id}]`, req)
}
上方是 UDPack Client 请求 UDPack Server 打开一个到 authority 的连接通道的代码。首先构造 Packet 对象,然后通过 protobuf 序列化为字节数组,然后通过 stream 发送出去。
stream.on('data', (buffer) => {
let packet = Packet.decode(buffer)
if (packet.cmd === 'connect') {
if (packet.connectRes.succeeded) {
socket._proxy.replyDstConnect(packet)
}
} else if (packet.cmd === 'data') {
socket._proxy.replyData(packet)
} else if (packet.cmd === 'terminate') {
socket._proxy.terminate(packet)
}
})
如上述代码所示,UDPack Server 发送过来的数据,经过 protobuf 解码得到 Packet 对象,然后根据 cmd 命令字执行不同的逻辑。如 packet.cmd === 'data'
时,会通过调用 socket._proxy.replyData(packet)
转发数据回 Browser。