1. 程式人生 > >VScode原始碼分析:查詢服務可用的埠

VScode原始碼分析:查詢服務可用的埠

基本資訊:

分支: master;
commitID: 3c4e9323;
檔案路徑: src/vs/base/node/ports.ts

基本思路:

1、查詢方法返回一個Promise(結果為resolve)
2、與127.0.0.1逐個埠進行Socket連線:

  • connect:埠已經被佔用,繼續查詢;

  • error:error.code = ECONNREFUSED埠可用,否則埠被佔用且不能連線;

原始碼:

/**
 * Given a start point and a max number of retries, will find a port that
 * is openable. Will return 0 in case no free port can be found.
 */
export function findFreePort(startPort: number, giveUpAfter: number, timeout: number): Thenable<number> {
	let done = false;

	return new Promise(resolve => {
		const timeoutHandle = setTimeout(() => { //超時後退出並返回未找到
			if (!done) {
				done = true;
				return resolve(0);
			}
		}, timeout);

		doFindFreePort(startPort, giveUpAfter, (port) => { //逐個查詢埠
			if (!done) {
				done = true;
				clearTimeout(timeoutHandle);
				return resolve(port);
			}
		});
	});
}
function doFindFreePort(startPort: number, giveUpAfter: number, clb: (port: number) => void): void {
	if (giveUpAfter === 0) {
		return clb(0);
	}

	const client = new net.Socket();

	// If we can connect to the port it means the port is already taken so we continue searching
	client.once('connect', () => {
		dispose(client); //一旦連線或error都要關閉

		return doFindFreePort(startPort + 1, giveUpAfter - 1, clb);
	});

	client.once('data', () => {
		// this listener is required since node.js 8.x
	});

	client.once('error', (err: Error & { code?: string }) => {
		dispose(client);

		// If we receive any non ECONNREFUSED error, it means the port is used but we cannot connect
		if (err.code !== 'ECONNREFUSED') {
			return doFindFreePort(startPort + 1, giveUpAfter - 1, clb);
		}

		// Otherwise it means the port is free to use!
		return clb(startPort);
	});

	client.connect(startPort, '127.0.0.1');
}
function dispose(socket: net.Socket): void {
	try {
		socket.removeAllListeners('connect'); //移除connect監聽
		socket.removeAllListeners('error'); //移除error監聽
		socket.end(); //半關閉 socket,服務端仍可以傳送資料
		socket.destroy(); //確保在該 socket 上不再有 I/O 活動。僅在出現錯誤的時候才需要(如解析錯誤等)
		socket.unref(); //如果活躍的 socket 是事件系統中僅存的 socket,則呼叫 unref 將會允許程式退出
	} catch (error) {
		console.error(error); // otherwise this error would get lost in the callback chain
	}
}

後記:

dispose中socket關閉的步驟還不是很清楚,相關連線