문제: 지연 시간이 짧은 클라이언트-서버 및 서버-클라이언트 연결
웹은 대체로 HTTP의 이른바 요청/응답 패러다임을 중심으로 구축되었습니다. 클라이언트가 웹페이지를 로드한 후 사용자가 다음 페이지를 클릭할 때까지 아무 일도 일어나지 않습니다. 2005년경 AJAX를 통해 웹이 더욱 역동적으로 느껴지기 시작했습니다. 하지만 모든 HTTP 통신은 클라이언트에서 제어하므로 서버에서 새 데이터를 로드하려면 사용자 상호작용이나 주기적 폴링이 필요했습니다.
서버가 새 데이터를 사용할 수 있음을 알리는 즉시 클라이언트로 데이터를 전송할 수 있는 기술은 오래 전부터 존재해 왔습니다. '푸시' 또는 '혜성'과 같은 이름으로 불립니다. 서버에서 시작한 연결의 착각을 유발하는 가장 일반적인 해킹 방법 중 하나는 장기 폴링입니다. 긴 폴링을 사용하면 클라이언트가 서버에 대한 HTTP 연결을 열고 응답을 보낼 때까지 열린 상태를 유지합니다. 서버에 실제로 새 데이터가 있을 때마다 응답을 전송합니다(다른 기법에는 Flash, XHR 멀티파트 요청, 소위 htmlfiles가 포함됨). 긴 폴링과 다른 기법도 매우 효과적입니다. Gmail 채팅과 같은 애플리케이션에서 매일 사용합니다.
하지만 이러한 모든 해결 방법에는 하나의 문제가 있습니다. HTTP 오버헤드를 수반하므로 지연 시간이 짧은 애플리케이션에 적합하지 않습니다. 브라우저의 멀티플레이어 1인칭 슈팅 게임이나 실시간 요소가 포함된 다른 온라인 게임을 생각해 보세요.
WebSocket 소개: 웹에 소켓 가져오기
WebSocket 사양은 웹브라우저와 서버 간에 '소켓' 연결을 설정하는 API를 정의합니다. 간단히 말해 클라이언트와 서버는 지속적으로 연결되어 있으므로 양측은 언제든지 데이터 전송을 시작할 수 있습니다.
시작하기
WebSocket 생성자를 호출하여 WebSocket 연결을 열면 됩니다.
var connection = new WebSocket('ws://html5rocks.websocket.org/echo', ['soap', 'xmpp']);
ws:
를 확인합니다. WebSocket 연결의 새 URL 스키마입니다. https:
가 보안 HTTP 연결에 사용되는 것과 동일한 방식으로 보안 WebSocket 연결을 위한 wss:
도 있습니다.
일부 이벤트 핸들러를 연결에 즉시 연결하면 연결이 열릴 때, 수신 메시지가 수신되거나 오류가 발생한 시점을 알 수 있습니다.
두 번째 인수는 선택적 하위 프로토콜을 허용합니다. 문자열 또는 문자열 배열일 수 있습니다. 각 문자열은 하위 프로토콜 이름을 나타내야 하며 서버는 배열에 전달된 하위 프로토콜 중 하나만 허용합니다. 허용된 하위 프로토콜은 WebSocket 객체의 protocol
속성에 액세스하여 확인할 수 있습니다.
하위 프로토콜 이름은 IANA 레지스트리에 등록된 하위 프로토콜 이름 중 하나여야 합니다. 현재 2012년 2월 기준으로 등록된 하위 프로토콜 이름은 SOAP 하나뿐입니다.
// When the connection is open, send some data to the server
connection.onopen = function () {
connection.send('Ping'); // Send the message 'Ping' to the server
};
// Log errors
connection.onerror = function (error) {
console.log('WebSocket Error ' + error);
};
// Log messages from the server
connection.onmessage = function (e) {
console.log('Server: ' + e.data);
};
서버와 통신
서버에 연결되는 즉시(open
이벤트가 실행될 때) 연결 객체의 send('your message')
메서드를 사용하여 서버에 데이터를 전송할 수 있습니다. 이전에는 문자열만 지원했지만 최신 사양에서는 이제 바이너리 메시지도 보낼 수 있습니다. 바이너리 데이터를 전송하려면 Blob
또는 ArrayBuffer
객체를 사용하면 됩니다.
// Sending String
connection.send('your message');
// Sending canvas ImageData as ArrayBuffer
var img = canvas_context.getImageData(0, 0, 400, 320);
var binary = new Uint8Array(img.data.length);
for (var i = 0; i < img.data.length; i++) {
binary[i] = img.data[i];
}
connection.send(binary.buffer);
// Sending file as Blob
var file = document.querySelector('input[type="file"]').files[0];
connection.send(file);
마찬가지로 서버에서도 언제든지 메시지를 보낼 수 있습니다. 이 경우 onmessage
콜백이 실행됩니다. 콜백은 이벤트 객체를 수신하며 data
속성을 통해 실제 메시지에 액세스할 수 있습니다.
WebSocket은 최신 사양의 바이너리 메시지도 수신할 수 있습니다. 바이너리 프레임은 Blob
또는 ArrayBuffer
형식으로 수신될 수 있습니다. 수신된 바이너리의 형식을 지정하려면 WebSocket 객체의 binaryType 속성을 'blob' 또는 'arraybuffer'로 설정합니다. 기본 형식은 'blob'입니다. (전송 시 바이너리유형 매개변수를 정렬할 필요는 없습니다.)
// Setting binaryType to accept received binary as either 'blob' or 'arraybuffer'
connection.binaryType = 'arraybuffer';
connection.onmessage = function(e) {
console.log(e.data.byteLength); // ArrayBuffer object if binary
};
WebSocket에 새로 추가된 또 다른 기능은 확장 프로그램입니다. 확장 프로그램을 사용하면 압축, 다중화된 프레임을 전송할 수 있습니다. 열기 이벤트 후 WebSocket 객체의 확장 프로그램 속성을 검사하여 서버에서 허용된 확장 프로그램을 찾을 수 있습니다. 2012년 2월 현재 아직 공식적으로 게시된 확장 프로그램 사양은 없습니다.
// Determining accepted extensions
console.log(connection.extensions);
교차 출처 통신
최신 프로토콜이므로 교차 출처 통신이 WebSocket에 바로 빌드됩니다. 여전히 신뢰할 수 있는 클라이언트 및 서버와만 통신해야 하지만 WebSocket을 사용하면 모든 도메인의 당사자 간에 통신할 수 있습니다. 서버는 서비스를 모든 클라이언트에 제공할지 아니면 잘 정의된 도메인에 있는 클라이언트만 제공할지 결정합니다.
프록시 서버
새로운 기술이 등장할 때마다 새로운 문제가 발생합니다. WebSocket의 경우 대부분의 회사 네트워크에서 HTTP 연결을 중재하는 프록시 서버와의 호환성입니다. WebSocket 프로토콜은 HTTP 업그레이드 시스템(일반적으로 HTTP/SSL에 사용됨)을 사용하여 HTTP 연결을 WebSocket 연결로 '업그레이드'합니다. 일부 프록시 서버는 이를 좋아하지 않으며 연결을 끊습니다. 따라서 특정 클라이언트가 WebSocket 프로토콜을 사용하더라도 연결을 설정하지 못할 수 있습니다. 따라서 다음 섹션이 더욱 중요합니다.
지금 바로 WebSocket 사용하기
WebSocket은 아직 초기 기술이며 일부 브라우저에서는 완전히 구현되지 않았습니다. 하지만 WebSocket을 사용할 수 없는 경우 언제든지 위에 언급된 대체 중 하나를 사용하는 라이브러리와 함께 WebSocket을 사용할 수 있습니다. 이 도메인에서 매우 인기 있는 라이브러리는 socket.io로, 프로토콜의 클라이언트 및 서버 구현과 함께 제공되며 대체 옵션이 포함되어 있습니다(2012년 2월 현재 socket.io는 아직 바이너리 메시지를 지원하지 않음). WebSocket 메시지를 클라이언트로 전송하는 HTTP API를 제공하여 모든 웹 환경에 쉽게 통합할 수 있는 PusherApp과 같은 상용 솔루션도 있습니다. 추가 HTTP 요청으로 인해 순수 WebSocket에 비해 항상 추가 오버헤드가 발생합니다.
서버 측
WebSocket을 사용하면 서버 측 애플리케이션을 위한 완전히 새로운 사용 패턴이 만들어집니다. LAMP와 같은 기존 서버 스택은 HTTP 요청/응답 주기를 중심으로 설계되었지만 많은 수의 열린 WebSocket 연결을 제대로 처리하지 못하는 경우가 많습니다. 동시에 많은 수의 연결을 열어 두려면 낮은 성능 비용으로 높은 동시 실행을 수용하는 아키텍처가 필요합니다. 이러한 아키텍처는 일반적으로 스레딩 또는 소위 비차단 IO를 중심으로 설계됩니다.
서버 측 구현
- Node.js
- 자바
- Ruby
- Python
- Erlang
- C++
- .NET
프로토콜 버전
WebSocket의 전송 프로토콜(클라이언트와 서버 간의 핸드셰이크 및 데이터 전송)은 이제 RFC6455입니다. 최신 Chrome 및 Android용 Chrome은 바이너리 메시지를 포함하여 RFC6455와 완벽하게 호환됩니다. 또한 Firefox는 버전 11에서, Internet Explorer는 버전 10에서 호환됩니다. 이전 프로토콜 버전을 계속 사용할 수는 있지만 취약한 것으로 알려져 있으므로 권장하지 않습니다. 이전 버전의 WebSocket 프로토콜에 대한 서버 구현이 있는 경우 최신 버전으로 업그레이드하는 것이 좋습니다.
사용 사례
클라이언트와 서버 간의 실시간 연결에 가까운 지연 시간을 필요로 할 때마다 WebSocket을 사용하세요. 이 과정에서 이벤트 큐와 같은 기술에 새로운 중점을 두고 서버 측 애플리케이션을 빌드하는 방법을 다시 생각해 볼 수 있습니다. 몇 가지 사용 사례는 다음과 같습니다.
- 멀티플레이어 온라인 게임
- 채팅 애플리케이션
- 스포츠 실시간 시세 표시기
- 실시간 소셜 스트림 업데이트