본문 바로가기

Linux

websocket 프로토콜 분석

웹소켓은 핸드세이크 과정이 있다

클라이언트의 요청

GET /ws HTTP/1.1
Host: 127.0.0.1
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Key: x3JJHMbDL1EzLkh9GBhXDw==
Sec-WebSocket-Version: 13

 

서버의 응답

HTTP/1.1     101    Switching Protocols
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Accept: m1gerlQGO93j1SfFew3lhvMXqro=

 

===============

핸드세이크 설명: 클라이언트가  http 연결을 하며 필수로 보내야 하는 4개의 헤더가 있다

1. Connection,

2. Connection헤더 값(Upgrade) 과 같은 헤더

3. 2번 항목의 값이: "websocket" 일것

4. Sec-WebSocket-Key

그러면 서버는 이 헤더 중 Sec-WebSocket-Key의 값과  웹소켓 RFC에서 지정한 매직키 ("258EAFA5-E914-47DA-95CA-C5AB0DC85B11") 를 스트링으로 연결한 다음 SHA-1 로직에 따라 해쉬값을 도출한 다음 이 값을 base64 로 encode 하여 응답전문의 Sec-WebSocket-Accept 헤더의 값으로 보내주어야 한다

리턴되는 http 상태코드는 반드시 "101" 이어야 한다

그러면 클라이언트는 서버가 웹소켓을 지원하며 웹소켓으로 연결을 했다고 알고 소켓을 끊지 않는다

 

이후에는 아래의 패킷 정보에 따라 서로 패킷을 주고 받는다

 

// 이하는 실제 웹소켓 통신시 암복호화 및 패킷 정보

====================================

// websocket 클라이언트가 서버로 데이타를 보낼 때는 무조건 암호화한다
// 요거 websocket 프로토콜을 알아야 코딩된다
// https://tools.ietf.org/id/draft-ietf-hybi-thewebsocketprotocol-09.html#rfc.section.1.3
// 위의 RFC 문서를 참조하여 작성했다.

   0                   1                   2                   3
   0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
  +-+-+-+-+-------+-+-------------+-------------------------------+
  |F|R|R|R| opcode|M| Payload len |    Extended payload length    |
  |I|S|S|S|  (4)  |A|     (7)     |             (16/63)           |
  |N|V|V|V|       |S|             |   (if payload len==126/127)   |
  | |1|2|3|       |K|             |                               |
  +-+-+-+-+-------+-+-------------+ - - - - - - - - - - - - - - - +
  |     Extended payload length continued, if payload len == 127  |
  + - - - - - - - - - - - - - - - +-------------------------------+
  |                               |Masking-key, if MASK set to 1  |
  +-------------------------------+-------------------------------+
  | Masking-key (continued)       |          Payload Data         |
  +-------------------------------- - - - - - - - - - - - - - - - +
  :                     Payload Data continued ...                :
  + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +
  |                     Payload Data continued ...                |
  +---------------------------------------------------------------+
          



1. 첫번째 바이트
1.1. opcode : x1, x2, x8, x9, xA ===> 4비트값 중 5가지만 사용
 {   이 비트는 오른쪽(여덟번째비트)이 설정되면 x1, 왼쪽(다섯째비트)이 설정되면 x8
      % x0 denotes a continuation frame
      % x1 denotes a text frame
      % x2 denotes a binary frame
      % x3 - 7 are reserved for further non - control frames
      % x8 denotes a connection close
      % x9 denotes a ping
      % xA denotes a pong
      % xB - F are reserved for further control frames
 }
2. 두번째 바이트
2.1 MASK : 설정되면 암호화 되어 있다 (암호화 키는 길이필드 다음 바이트부터 무조건 4바이트)
               클라이언트에서 서버로 송신되는 모든 데이타는 반드시 암호화 한다
2.2 Payload len : 이 값에 따라 길이를 나타내는 방법이 다르다
 {
 Payload len == 0 ~ 125 : 마스킹키 다음 바이트부터 실린 데이타의 실제 길이
 Payload len == 126 : 세번째 바이트와 네번째 바이트의 값이 unsigned short 형식으로 
                      마스킹키 다음 데이타구간에 실린 데이타의 실제 길이
 Payload len == 127 : 세번째 바이트 부터 열번째 바이트의 값이 unsigned 64int 형식으로 
                   마스킹키 다음 데이타구간에 실린 데이타의 실제 길이
                   (단 첫번째 비트는 반드시 0이어야 한다. 즉 63비트만 유효함)
                   이경우는 최대 길이를 제한할 필요가 있다. 아니면 이 값에 다라 시스템이 오동작한다(메모리 확보실패.등등...)
 }


3. 일단 길이값이 구해지면 해당 길이만큼의 데이타를 수신해야한다.
                    수신이 완료되면 데이타 복호화작업을 한다.


4. 데이타 암복호화: 마스킹 값을 가지고 비트마스킹(XOR)하여 복호화작업을 한다
            데이타 첫번째 바이트와 마스킹 첫번째 바이트,
             데이타 두번째 바이트와 마스킹 두번째 바이트,
             데이타 세번째 바이트와 마스킹 세번째 바이트,
             데이타 네번째 바이트와 마스킹 네번째 바이트,
             데이타 다섯번째 바이트와 마스킹 첫번째 바이트,
             데이타 여섯번째 바이트와 마스킹 두번째 바이트....
 for (i = 0; i < (size_t)data_len; i++) {
             data[i] ^= mask[i & 3];
 }


 5. 프로토콜에 따르면 길이가 0이 설정 되어도 최소한 2바이트는 무조건 보낸다
         데이타가 있으면 데이터길이에 따라 마스킹키 및 데이터 시작 위치가 변경된다.