您必须将连接存储在某个地方,例如一个数组(如果您有两个以上)或一个以某种唯一标识符为键的对象,您可以使用该标识符轻松查找目标套接字。例如:
var sockets = [];
net.createServer(function(s) {
// Add the socket to our connected socket list
sockets.push(s);
// Automatically remove the socket when it disconnects
s.on('close', function() {
sockets.splice(sockets.indexOf(s), 1);
});
// How you determine the destination socket is up to you
var destSocket = ...;
// This pipes data between the current socket and the
// destination socket (both directions) but does not automatically
// close one socket when the other socket closes
s.pipe(destSocket, { end: false })
.pipe(s, { end: false });
}).listen(port);
如果您的应用需要更灵活,您可以让它比这更复杂。例如,您可以通过在连接时从源套接字读取一行(以\n 结尾)来找到目标套接字,其中包含目标套接字的 IP 地址。然后,您可以在每个套接字的 IP 地址上键入一个对象,而不是一个数组,这样您就可以轻松找到合适的目标套接字。
或者您可能让每个源套接字都发送“用户 ID”和请求的目标“用户 ID”。此时,您不妨将初始单行设为 JSON 字符串,后跟 \n,这样客户端就可以发送所需的任意数量的参数来实现桥接。
使用 JSON 标头的更复杂解决方案的示例:
var sockets = Object.create(null);
net.createServer(function(s) {
s.on('error', function(err) {
});
readJSON(s, function(err, result) {
var dest;
// Check if socket closed while reading our JSON header
if (!s.writable)
return;
// Immediately close the socket when either:
// * We receive a malformed JSON header
// * Or the source socket id is already used
// * Or the destination socket is not connected
// You could extend this check more if you wanted, for example
// to perform authentication.
if (err
|| typeof result !== 'object'
|| result === null
|| (typeof result.id !== 'string' && typeof result.id !== 'number')
|| (typeof result.destId !== 'string' && typeof result.destId !== 'number')
|| sockets[result.id]
|| !(dest = sockets[result.destId])
|| dest.busy)
return s.destroy();
var id = result.id;
sockets[id] = s;
// Set a flag on each socket to allow exclusive communication
// between connected sockets
s.busy = dest.busy = true;
// Hook the sockets up together, allowing the closing of either
// socket to close the other
s.pipe(dest).pipe(s);
s.on('close', function() {
delete sockets[id];
});
});
}).listen(port);
function readJSON(s, cb) {
var buf = '';
(function read() {
var chunk = s.read();
if (chunk === null)
return s.once('readable', read);
for (var i = 0; i < chunk.length; ++i) {
// 10 === LF (\n)
if (chunk[i] === 10) {
// Check if there is leftover data that is not
// a part of our JSON message that we need to
// put back onto the socket
if (i < (chunk.length - 1))
s.unshift(chunk.slice(i + 1));
buf += chunk.toString('utf8', 0, i);
var result = tryParseJSON(buf);
if (result instanceof Error)
cb(result);
else
cb(null, result);
return;
}
}
buf += chunk.toString('utf8');
// TODO: implement `buf.length` check to make sure we don't
// buffer indefinitely
})();
}
// This is merely an optimization since v8 currently does not
// optimize any functions containing a `try-catch` or `try-finally`.
// By isolating the `try-catch` needed for JSON parsing, we can limit
// the deoptimization.
function tryParseJSON(str) {
try {
return JSON.parse(str);
} catch (ex) {
return ex;
}
}
上述解决方案的示例客户端:
var s = net.connect(port, function() {
// Send our header
s.write(JSON.stringify({
id: '123',
destId: '456'
}) + '\n');
s.end('Hello Dave!');
}).on('error', function(err) {
// This should fire when the server rejects our request
// due to one of the request checks failing
console.log('Uh oh: ' + err);
});
一旦此客户端连接(假设已连接另一个标识为456 的套接字),目标套接字应该会看到数据Hello Dave!。之后,两个套接字都将关闭。