본문 바로가기
카테고리 없음

WebSocket 실시간 양방향 통신 프로토콜 및 구현 (자바스크립트, php)

by happytimes 2024. 5. 22.

WebSocket은 실시간 양방향 통신을 제공하는 프로토콜로, 클라이언트와 서버 간의 데이터 전송을 더욱 효율적으로 만들어 줍니다. WebSocket의 작동 원리는 다음과 같습니다:

핸드셰이크(Handshake)

클라이언트와 서버 간의 WebSocket 연결은 HTTP 핸드셰이크로 시작됩니다. 클라이언트는 서버에 HTTP 요청을 보내며, 이 요청에는 업그레이드 헤더가 포함되어 있어 WebSocket 연결로 업그레이드를 요청합니다. 서버는 이 요청을 받아들일 경우 HTTP 101 Switching Protocols 응답을 보내어 WebSocket 연결을 수립합니다.

 

 

GET /chat HTTP/1.1
Host: example.com
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==
Sec-WebSocket-Version: 13

 

HTTP/1.1 101 Switching Protocols
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Accept: s3pPLMBiTxaQ9kYGzzhZRbK+xOo=

 

 

양방향 통신

핸드셰이크가 완료되면, 클라이언트와 서버는 WebSocket을 통해 양방향으로 데이터를 주고받을 수 있습니다. 이 연결은 지속적이며, HTTP의 요청-응답 방식과 달리 데이터를 실시간으로 주고받을 수 있습니다. 클라이언트와 서버는 메시지를 프레임 단위로 전송합니다. 이 프레임은 텍스트 또는 바이너리 데이터일 수 있습니다.

 

프레임 구조:WebSocket 프레임은 특정 형식을 따릅니다. 첫 번째 바이트는 메타데이터(종료 비트, 예약 비트, opcode 등)를 포함하고, 두 번째 바이트는 페이로드 길이를 나타냅니다. 이후 페이로드 데이터가 뒤따릅니다. 예: 텍스트 메시지를 전송할 때는 첫 번째 바이트의 opcode가 텍스트 프레임(1)으로 설정됩니다.

 

연결 유지 및 관리:WebSocket 연결은 클라이언트나 서버가 명시적으로 연결을 닫지 않는 한 계속 유지됩니다. 연결을 닫을 때는 클로즈 프레임을 보내어 종료를 알립니다. 클라이언트나 서버는 핑/퐁 프레임을 사용하여 연결이 여전히 유효한지 확인할 수 있습니다. 이는 연결 유지 관리(keep-alive)를 위한 기능입니다.

 

예시 코드:클라이언트에서 WebSocket 연결을 생성하고 사용하는 간단한 예제는 다음과 같습니다:

 

// 클라이언트에서 WebSocket 연결 생성
const socket = new WebSocket('ws://example.com/socket');

// 연결이 열렸을 때 실행되는 이벤트
socket.onopen = function(event) {
    console.log('WebSocket connection established');
    // 서버로 메시지 전송
    socket.send('Hello, Server!');
};

// 서버로부터 메시지를 수신할 때 실행되는 이벤트
socket.onmessage = function(event) {
    console.log('Message from server:', event.data);
};

// 연결이 닫혔을 때 실행되는 이벤트
socket.onclose = function(event) {
    console.log('WebSocket connection closed');
};

// 에러가 발생했을 때 실행되는 이벤트
socket.onerror = function(event) {
    console.error('WebSocket error:', event);
};

 

WebSocket을 사용하면 HTTP의 요청-응답 모델보다 더 적은 오버헤드로 실시간 데이터를 주고받을 수 있어, 실시간 채팅, 실시간 알림, 게임 등 다양한 애플리케이션에서 유용하게 사용됩니다.

 

PHP 소켓 서버 코드 (server.php)

 

 

<?php
// 설정
$host = '127.0.0.1';
$port = 8080;

// 소켓 생성
$serverSocket = socket_create(AF_INET, SOCK_STREAM, SOL_TCP);
socket_set_option($serverSocket, SOL_SOCKET, SO_REUSEADDR, 1);
socket_bind($serverSocket, $host, $port);
socket_listen($serverSocket);

$clients = [];

echo "WebSocket server started on ws://$host:$port\n";

while (true) {
    $readSockets = array_merge([$serverSocket], $clients);
    $writeSockets = null;
    $exceptSockets = null;

    // 소켓 변경 대기
    socket_select($readSockets, $writeSockets, $exceptSockets, null);

    if (in_array($serverSocket, $readSockets)) {
        $newClient = socket_accept($serverSocket);
        $clients[] = $newClient;
        $index = array_search($serverSocket, $readSockets);
        unset($readSockets[$index]);
    }

    foreach ($readSockets as $socket) {
        $data = @socket_recv($socket, $buffer, 2048, 0);
        if ($data === false) {
            $index = array_search($socket, $clients);
            unset($clients[$index]);
            socket_close($socket);
            continue;
        }

        if ($data > 0) {
            if (strpos($buffer, 'GET') !== false) {
                performHandshake($socket, $buffer);
            } else {
                $message = decode($buffer);
                $response = encode($message);
                foreach ($clients as $client) {
                    @socket_write($client, $response, strlen($response));
                }
            }
        }
    }
}

socket_close($serverSocket);

function performHandshake($client, $headers)
{
    if (preg_match("/Sec-WebSocket-Key: (.*)\r\n/", $headers, $matches)) {
        $key = $matches[1];
        $acceptKey = base64_encode(pack(
            'H*',
            sha1($key . '258EAFA5-E914-47DA-95CA-C5AB0DC85B11')
        ));
        $upgrade = "HTTP/1.1 101 Switching Protocols\r\n" .
                   "Upgrade: websocket\r\n" .
                   "Connection: Upgrade\r\n" .
                   "Sec-WebSocket-Accept: $acceptKey\r\n\r\n";
        socket_write($client, $upgrade, strlen($upgrade));
    }
}

function encode($message)
{
    $frame = [];
    $frame[0] = 129;
    $len = strlen($message);

    if ($len <= 125) {
        $frame[1] = $len;
    } else {
        $frame[1] = 126;
        $frame[2] = ($len >> 8) & 255;
        $frame[3] = $len & 255;
    }

    for ($i = 0; $i < $len; $i++) {
        $frame[] = ord($message[$i]);
    }

    $response = '';
    foreach ($frame as $b) {
        $response .= chr($b);
    }

    return $response;
}

function decode($buffer)
{
    $length = ord($buffer[1]) & 127;
    if ($length == 126) {
        $masks = substr($buffer, 4, 4);
        $data = substr($buffer, 8);
    } elseif ($length == 127) {
        $masks = substr($buffer, 10, 4);
        $data = substr($buffer, 14);
    } else {
        $masks = substr($buffer, 2, 4);
        $data = substr($buffer, 6);
    }

    $text = '';
    for ($i = 0; $i < strlen($data); ++$i) {
        $text .= $data[$i] ^ $masks[$i % 4];
    }

    return $text;
}

 

 

 

지금까지 WebSocket 실시간 양방향 통신 프로토콜 및 구현 (자바스크립트, php)에 대해서 알아보았습니다

많은 도움 되셨으면 합니다.

 

 

HTTP 1.0과 HTTP 2.0의 차이점, HTTP 1.0 구현 방법

 

HTTP 1.0과 HTTP 2.0의 차이점, HTTP 1.0 구현 방법

HTTP 1.0과 HTTP 2.0은 웹 통신의 기본 규약으로, 서로 다른 버전에서 여러 개선 사항과 변화를 제공합니다. 각 버전의 주요 차이점을 아래와 같이 정리할 수 있습니다:  HTTP 1.0비연결성 (Non-Persistent

issuetale.kr