実際の開発では、接続問題の診断と解決が重要なスキルとなります。
class WebSocketDebugger {
static enableDebugLogging() {
const originalWebSocket = window.WebSocket;
window.WebSocket = class extends originalWebSocket {
constructor(url, protocols) {
console.log('🔌 WebSocket接続開始:', { url, protocols });
super(url, protocols);
this.addEventListener('open', (event) => {
console.log('✅ WebSocket接続成功:', {
url: this.url,
protocol: this.protocol,
extensions: this.extensions,
timestamp: new Date().toISOString()
});
});
this.addEventListener('error', (event) => {
console.error('❌ WebSocket接続エラー:', {
url: this.url,
readyState: this.readyState,
event: event,
timestamp: new Date().toISOString()
});
this.analyzeConnectionError();
});
this.addEventListener('close', (event) => {
console.log('🔌 WebSocket接続クローズ:', {
code: event.code,
reason: event.reason,
wasClean: event.wasClean,
timestamp: new Date().toISOString()
});
this.analyzeCloseReason(event.code);
});
}
analyzeConnectionError() {
this.checkNetworkConnectivity();
this.checkProxyConfiguration();
this.checkFirewallRestrictions();
}
analyzeCloseReason(code) {
const closeReasons = {
1000: '正常終了',
1001: 'エンドポイント離脱',
1002: 'プロトコルエラー',
1003: '不正なデータ受信',
1006: '異常終了(詳細不明)',
1007: 'データ形式エラー',
1008: 'ポリシー違反',
1009: 'メッセージサイズ過大',
1010: '拡張ネゴシエーション失敗',
1011: 'サーバー内部エラー'
};
console.log('クローズ理由:', closeReasons[code] || `不明(コード: ${code})`);
if ([1006, 1011, 1012, 1013, 1014, 1015].includes(code)) {
console.log('💡 推奨: 再接続を試行してください');
}
}
async checkNetworkConnectivity() {
try {
const response = await fetch('https://httpbin.org/get', {
method: 'GET',
mode: 'cors'
});
if (response.ok) {
console.log('✅ 基本的なHTTP接続は正常');
}
} catch (error) {
console.error('❌ 基本的なネットワーク接続に問題:', error);
}
}
checkProxyConfiguration() {
const navigator = window.navigator;
if (navigator.connection) {
console.log('ネットワーク情報:', {
effectiveType: navigator.connection.effectiveType,
downlink: navigator.connection.downlink,
rtt: navigator.connection.rtt
});
}
const userAgent = navigator.userAgent;
if (userAgent.includes('Proxy') || userAgent.includes('Gateway')) {
console.log('💡 プロキシまたはゲートウェイの存在が疑われます');
}
}
};
}
static async performConnectionDiagnostics(url) {
console.log('🔍 WebSocket接続診断を開始:', url);
const diagnostics = {
timestamp: new Date().toISOString(),
url: url,
results: {}
};
diagnostics.results.urlValidation = this.validateWebSocketURL(url);
diagnostics.results.dnsResolution = await this.testDNSResolution(url);
diagnostics.results.tcpConnectivity = await this.testTCPConnectivity(url);
diagnostics.results.httpConnectivity = await this.testHTTPConnectivity(url);
diagnostics.results.websocketHandshake = await this.testWebSocketHandshake(url);
console.log('🔍 診断結果:', diagnostics);
return diagnostics;
}
static validateWebSocketURL(url) {
try {
const wsUrl = new URL(url);
const validation = {
isValid: true,
protocol: wsUrl.protocol,
hostname: wsUrl.hostname,
port: wsUrl.port,
pathname: wsUrl.pathname,
issues: []
};
if (!['ws:', 'wss:'].includes(wsUrl.protocol)) {
validation.isValid = false;
validation.issues.push('無効なプロトコル: ' + wsUrl.protocol);
}
if (!wsUrl.hostname) {
validation.isValid = false;
validation.issues.push('ホスト名が指定されていません');
}
return validation;
} catch (error) {
return {
isValid: false,
error: error.message,
issues: ['URL形式が無効です']
};
}
}
}
WebSocketDebugger.enableDebugLogging();