WebSockets 소개 - 웹에 소켓 사용하기

문제: 지연 시간이 짧은 클라이언트-서버 및 서버-클라이언트 연결

웹은 주로 소위 HTTP 요청/응답 패러다임을 중심으로 구축되었습니다. 클라이언트가 웹페이지를 로드한 후 사용자가 다음 페이지를 클릭할 때까지 아무 일도 일어나지 않습니다. 2005년경 AJAX는 웹을 더욱 동적으로 만들기 시작했습니다. 하지만 모든 HTTP 통신은 클라이언트가 주도했기 때문에 서버에서 새 데이터를 로드하려면 사용자 상호작용이나 주기적인 폴링이 필요했습니다.

서버가 새로운 데이터를 사용할 수 있다는 사실을 인지하는 순간 서버에 데이터를 전송할 수 있게 해 주는 기술입니다. 'Push'나 'Comet' 같은 이름으로 불립니다. 서버가 시작된 것처럼 착각을 불러일으키는 가장 일반적인 해킹 중 하나는 긴 폴링입니다. 긴 폴링을 사용하면 클라이언트가 서버에 대한 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'입니다. (전송 시 binaryType 매개변수를 정렬할 필요가 없습니다.)

// 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 연결을 WebSocket 연결로 '업그레이드'하는 데 일반적으로 HTTP/SSL에 사용되는 HTTP 업그레이드 시스템을 사용합니다. 일부 프록시 서버는 이를 좋아하지 않아 연결을 끊을 수 있습니다. 따라서 특정 클라이언트가 WebSocket 프로토콜을 사용하더라도 연결을 설정하지 못할 수 있습니다. 따라서 다음 섹션이 더 중요해집니다.

지금 WebSocket 사용

WebSocket은 아직 초기 단계이며 모든 브라우저에서 완전히 구현되지는 않습니다. 그러나 WebSocket을 사용할 수 없을 때는 언제나 위에서 언급한 대체 방식 중 하나를 사용하는 라이브러리와 함께 WebSocket을 사용할 수 있습니다. 이 도메인에서 매우 인기를 얻고 있는 라이브러리는 socket.io입니다. 이 라이브러리는 클라이언트 및 프로토콜의 서버 구현과 함께 제공되며 대체 기능을 포함합니다 (socket.io는 2012년 2월부터 바이너리 메시징을 지원하지 않습니다). PusherApp과 같은 상업용 솔루션도 있습니다. 이 솔루션은 클라이언트에게 WebSocket 메시지를 전송하는 HTTP API를 제공하여 모든 웹 환경에 쉽게 통합할 수 있습니다. 추가 HTTP 요청으로 인해 순수 WebSocket에 비해 항상 추가 오버헤드가 발생합니다.

서버 측

WebSocket을 사용하면 서버 측 애플리케이션을 위한 완전히 새로운 사용 패턴이 생성됩니다. LAMP와 같은 기존 서버 스택은 HTTP 요청/응답 주기를 중심으로 설계되었지만, 많은 수의 개방형 WebSocket 연결을 제대로 처리하지 못하는 경우가 많습니다. 많은 수의 연결을 동시에 열어 두려면 낮은 성능 비용으로 높은 동시 실행을 허용하는 아키텍처가 필요합니다. 이러한 아키텍처는 일반적으로 스레딩 또는 비블로킹 IO를 중심으로 설계됩니다.

서버 측 구현

프로토콜 버전

WebSocket의 유선 프로토콜 (핸드셰이크 및 클라이언트와 서버 간 데이터 전송)은 이제 RFC6455입니다. 최신 Chrome 및 Android용 Chrome은 바이너리 메시징을 비롯한 RFC6455와 완벽하게 호환됩니다. 또한 Firefox는 버전 11, Internet Explorer 버전 10에서는 호환됩니다. 이전 프로토콜 버전을 계속 사용할 수 있지만 취약한 것으로 알려져 있기 때문에 권장되지 않습니다. 이전 버전의 WebSocket 프로토콜에서 서버를 구현한 경우 최신 버전으로 업그레이드하는 것이 좋습니다.

사용 사례

클라이언트와 서버 간의 실시간에 가까운 매우 짧은 지연 시간이 필요할 때마다 WebSocket을 사용하세요. 이를 위해서는 이벤트 큐와 같은 기술에 새로운 중점을 두고 서버 측 애플리케이션을 빌드하는 방법을 재고해야 할 수도 있습니다. 사용 사례의 예는 다음과 같습니다.

  • 멀티플레이어 온라인 게임
  • 채팅 애플리케이션
  • 실시간 스포츠 티커
  • 소셜 스트림 실시간 업데이트

데모

참조